| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| Invoke | /app:di |
| Aliases | /di:create, /wiring:setup |
| Tools | Read, Edit, Write, Glob, Grep |
| Model | sonnet |
| Skills | bloc |
/app:di โ ๋ถํ ์ฐ๊ฒฐ ๋ด๋น#
ํ๋ง๋๋ก#
์ฑ์ ์ด๋ฃจ๋ ์ฌ๋ฌ ๋ถํ(ํ๋ฉดยท๊ธฐ๋ฅยท๋ฐ์ดํฐ)์ ์๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ฐ๊ฒฐํด ์ฃผ๋ "์ ๊ธฐ ๋ฐฐ์ " ๋ด๋น์ ๋๋ค. ๋ ๊ณ ๋ธ๋ก์ ๋ฐ๋ก๋ฐ๋ก ๋ง๋ค์ด ๋๋ฉด ์์ฉ์๋ฏ, ์ด ๋ช ๋ น์ด ๋ธ๋ก๋ค์ ์ ์์น์ ๋ผ์ ๋ง์ถฐ ์ฑ์ด ์ค์ ๋ก ๋์ํ๊ฒ ํด์ค๋๋ค.
๋๊ฐยท์ธ์ ์ฐ๋์#
- ์ ๊ธฐ๋ฅ์ ๋ง๋ ๋ค, ๊ทธ ๊ธฐ๋ฅ์ด ์ฑ ์ ์ฒด์ ์ ๋๋ก ์ด์ด์ง๋๋ก "์ฐ๊ฒฐ ์์ "์ด ํ์ํ ๊ฐ๋ฐ์
/app:di๋ช ๋ น์ ์ง์ ์คํํ ๋- ๊ธฐ๋ฅ ์ ์ฒด ์๋ ์์ฑ(
/feature:create)์ด ์งํ๋๋ ๋์ค, ๋ถํ์ ํฉ์น๋ ๋จ๊ณ์์ ์๋์ผ๋ก ํธ์ถ๋ ๋
๋ฌด์์ ํด์ฃผ๋์#
์ฐ๊ฒฐ ๋ฐฉ์์ ์๋ ๋ฐฉ์(getIt/injectable)์์ ๊น๋ํ ์ ๋ฐฉ์์ผ๋ก ํต์ผํด ์ค๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋:
-
์ฑ ์ ์ฒด์์ ๊ณต์ฉ์ผ๋ก ์ฐ๋ ๊ธฐ๋ฅ(์: ๋ก๊ทธ์ธ
AuthBloc, ์๋ฆผNotificationBloc)์ ์ฑ ์์ ์ง์ (bootstrap.dart)์์ ๋ง๋ค์ด ์ฐ๊ฒฐ - ๊ฐ๋ณ ํ๋ฉด์ด ์๊ธฐ ๊ธฐ๋ฅ์ ์ง์ ๋ง๋ค์ด ์ฐ๋๋ก ๋ฐฐ์
- ์๋ก ์์กดํ๋ ๋ถํ ๊ฐ์ "๊ผฌ์(์ํ ์์กด)" ๋ฌธ์ ๋ฅผ ํ์ด์ค
- ์ด ๋ชจ๋ ๊ท์น์ ์ง์ผฐ๋์ง ์ ๊ฒํ ์ฒดํฌ๋ฆฌ์คํธ ์ ๊ณต
์ด๋ป๊ฒ ์ฐ๋์#
# ๊ธฐ๋ณธ ์คํ
/app:di
# ๊ฐ์ ๊ธฐ๋ฅ์ ๋ถ๋ฅด๋ ๋ค๋ฅธ ์ด๋ฆ(๋ณ์นญ)๋ ์ฌ์ฉ ๊ฐ๋ฅ
/di:create
/wiring:setup
์ ๋ฌํ ์ ์๋ ๊ฐ:
feature_name(ํ์) โ ์ฐ๊ฒฐํ ๊ธฐ๋ฅ ๋ชจ๋ ์ด๋ฆ (snake_case ํ์)-
module_type(์ ํ) โapp/console/common์ค ์ ํ (๊ธฐ๋ณธ๊ฐapp) is_global(์ ํ) โ ์ฑ ์ ์ฒด ๊ณต์ฉ ๊ธฐ๋ฅ์ธ์ง ์ฌ๋ถ (๊ธฐ๋ณธ๊ฐfalse)
์์์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์#
-
๊ธ์ง๋ ์๋ ๋ฐฉ์ ์ ๊ฑฐ โ
getIt,@injectable๊ฐ์ ๋ ์ด์ ์ฐ์ง ์๋ ์ฐ๊ฒฐ ์ฝ๋๋ฅผ ์ฐ์ง ์๋๋ก ๋ง์ต๋๋ค. - ํ๋ฉด๋ณ ๊ธฐ๋ฅ ์ฐ๊ฒฐ โ ๊ฐ ํ๋ฉด์ด ํ์ํ ๊ธฐ๋ฅ์ ์ง์ ์์ฑํด ๋ผ์ ๋ฃ์ต๋๋ค.
- ๊ณต์ฉ ๊ธฐ๋ฅ ์ฐ๊ฒฐ โ ์ฑ ์์ ์ง์ ์์ ๋ก๊ทธ์ธยท์๋ฆผ ๊ฐ์ ๊ณต์ฉ ๋ถํ์ ๋ง๋ค๊ณ ์ฑ ์ ์ฒด์ ๊ณต์ ํฉ๋๋ค.
- ๊ผฌ์ ํ๊ธฐ โ ์๋ก ๋ง๋ฌผ๋ฆฐ ๋ถํ(์๋ฆผ โ ๋ก๊ทธ์ธ)์ ์ํ ์์กด์ ๋ถ๋ฆฌํด ์์ ํ๊ฒ ์ด๊ธฐํํฉ๋๋ค.
- ์ ๊ฒ โ ๋ชจ๋ ๊ท์น(๊ธ์ง ํจํด ๋ฏธ์ฌ์ฉ, ์ฌ๋ฐ๋ฅธ ์์ฑ ๋ฐฉ์ ๋ฑ)์ ์ฒดํฌ๋ฆฌ์คํธ๋ก ํ์ธํฉ๋๋ค.
โ๏ธ ์์ธ ์ต์ ยท์คํ ๋ช ์ธ (๊ฐ๋ฐ์ / AI ์์ด์ ํธ์ฉ)
Role#
Sets up Pure DI-based dependency wiring.
- Global BLoC creation (bootstrap.dart)
- BlocProvider.value Pattern (App)
- Page BLoC direct creation (BlocProvider)
- UseCase Optional Constructor Injection
- Router initialization (initializeRouter)
Activation Conditions#
/app:diActivated when command is invoked/feature:createorchestration integration phase
Parameters#
| Parameter | Required | Description |
|---|---|---|
feature_name | โ | Feature module name (snake_case) |
module_type | โ | app, console, common (default: app) |
is_global | โ | Global BLoC Whether (default: false) |
Removed Patterns (Prohibited)#
// โ All removed
getIt<T>()
getIt.registerLazySingleton<T>(...)
getIt.registerFactory<T>(...)
@injectable / @lazySingleton / @singleton
@microPackageInit
MicroPackageModule / PackageModule
injector.dart / injector.module.dart
di/ ํด๋
ExternalModule(...)
export 'src/di/injector.dart'
Core Patterns#
1. Page BLoC (Most common)#
class {Feature}Page extends StatelessWidget {
const {Feature}Page({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => {Feature}Bloc()
..add(const {Feature}Event.loadRequested()),
child: const {Feature}View(),
);
}
}
2. Global BLoC (Created in bootstrap.dart)#
Authoritative DI paths (verify against the kobic monorepo):
ServiceLocator(get_it ๋์ฒด) โpackage/core/lib/src/app/service_locator.dart- App-level wiring /
ServiceLocator.instance.registerSingleton(...)โapp/kobic/lib/bootstrap.dartandapp/kobic_console/lib/bootstrap.dart- โ No
package/core/lib/src/di/folder โ.../di/app_service_locator.dartand.../di/console_service_locator.dartdo not exist.
// app/kobic/lib/bootstrap.dart (console: app/kobic_console/lib/bootstrap.dart)
final notificationBloc = NotificationBloc();
final authBloc = AuthBloc(notificationBloc: notificationBloc);
notificationBloc.initAuthListener(authBloc);
runApp(App(blocs: GlobalBlocProviders(
authBloc: authBloc,
notificationBloc: notificationBloc,
)));
3. App (BlocProvider.value)#
class App extends StatelessWidget {
const App({required this.blocs, super.key});
final GlobalBlocProviders blocs;
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthBloc>.value(value: blocs.authBloc),
BlocProvider<NotificationBloc>.value(value: blocs.notificationBloc),
],
child: const MaterialApp(...),
);
}
}
4. BLoC Generation (Optional Constructor Injection)#
class {Feature}Bloc extends Bloc<{Feature}Event, {Feature}State> {
{Feature}Bloc({
Get{Entity}UseCase? get{Entity}UseCase,
AuthBloc? authBloc, // cross-BLoC: nullable
}) : _get{Entity}UseCase = get{Entity}UseCase ?? const Get{Entity}UseCase(),
_authBloc = authBloc,
super(const {Feature}State());
final Get{Entity}UseCase _get{Entity}UseCase;
final AuthBloc? _authBloc;
}
5. UseCase (Direct creation)#
class Get{Entity}UseCase {
const Get{Entity}UseCase([I{Feature}Repository? repo])
: _repo = repo ?? const {Feature}Repository();
final I{Feature}Repository _repo;
Future<Either<Failure, {Entity}>> call(Get{Entity}Params params) {
return _repo.get{Entity}(params);
}
}
6. Accessing BLoC from Widget#
// โ
Use context.read
context.read<AuthBloc>().add(const AuthEvent.signOut());
// โ getIt usage prohibited
7. Router Initialization#
AppRouter.initializeRouter(authBloc);
Feature Module Barrel File#
// lib/{feature_name}.dart
library;
export 'src/data/data.dart';
export 'src/domain/domain.dart';
export 'src/presentation/presentation.dart';
export 'src/route/route.dart';
// No di/ export!
Circular Dependency Resolution#
// NotificationBloc <-> AuthBloc circular dependency resolution
final notificationBloc = NotificationBloc();
final authBloc = AuthBloc(notificationBloc: notificationBloc);
notificationBloc.initAuthListener(authBloc); // Separated initialization
Test Patterns#
class MockGetDataUseCase extends Mock implements GetDataUseCase {}
blocTest<{Feature}Bloc, {Feature}State>(
'emits [loading, loaded] when load succeeds',
setUp: () {
when(() => mockGetData(any()))
.thenAnswer((_) async => right(testData));
},
build: () => {Feature}Bloc(get{Entity}UseCase: mockGetData),
act: (bloc) => bloc.add(const {Feature}Event.loadRequested()),
expect: () => [
const {Feature}State.loading(),
{Feature}State.loaded(data: testData),
],
);
Checklist#
- Do not create di/ folder
- Do not use getIt, @injectable
- UseCase:
const UseCase([IRepo? repo]) : _repo = repo ?? ConcreteRepo() - BLoC: Optional Constructor Injection
- Page:
BlocProvider(create: (_) => Bloc()) - Global BLoC: Create in bootstrap.dart
- App:
BlocProvider.valuePattern - Widget:
context.read<Bloc>()Usage - Router:
AppRouter.initializeRouter(authBloc) - Test: Constructor Direct injection