Dynamic module은 import될 때 구성될 수 있는 module입니다 — 정적 @Module({...}) 대신, 전달하는 인수로 구성된 module을 반환하는 정적 메서드(관례적으로 **forRoot**와 forFeature)를 노출합니다. 이것이 구성 가능하고 재사용 가능한 module(config, database, JWT)이 옵션을 받아들이는 방법입니다.
정적 module은 고정되어 있지만 — 재사용 가능한 module(DatabaseModule, ConfigModule,
JwtModule)은 앱별로 구성되어야 합니다(연결 URL, secret, 옵션).
Dynamic module은 module이 import 시점에 구성을 받아들이게 합니다.
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }), // ← 옵션과 함께 forRoot
TypeOrmModule.forRoot({ type: "postgres", url: ... }), // ← import 시 구성
JwtModule.register({ secret: process.env.SECRET }), // ← register 변형
TypeOrmModule.forFeature([User, Post]), // ← 특정 항목을 위한 forFeature
],
})
export class AppModule {}
module을 import할 때 보이는 .forRoot()/.register()/.forFeature() 호출은 구성을 받아들이는 dynamic module 메서드입니다.
forRoot() → module을 한 번, 전역으로 구성 (연결, secret, 핵심 설정)
루트/app module에서 한 번 호출
forFeature() → 그것이 필요한 각 기능 module에서 기능별 조각을 등록
(예: TypeOrmModule.forFeature([User])가 module별로 entity 등록)
관례: 전역/일회성 구성에는 forRoot; 여러 module에서 호출하는 기능별 등록에는 forFeature.
@Module({})
export class StorageModule {
static forRoot(options: StorageOptions): DynamicModule {
return { // 구성된 module 반환
module: StorageModule,
providers: [
{ provide: "STORAGE_OPTIONS", useValue: options }, // 전달된 옵션 주입
StorageService,
],
exports: [StorageService],
global: true, // 선택적으로 전역으로 만들기
};
}
}
// 사용: StorageModule.forRoot({ bucket: "my-bucket", region: "us-east-1" })
정적 메서드는 DynamicModule 객체(module + provider + export)를 반환하여, 전달된 옵션을 (종종 injection token을 통해) module의 provider에 연결합니다.
// 다른 주입된 의존성(예: ConfigService)을 사용하여 구성 — 비동기/factory
StorageModule.forRootAsync({
useFactory: (config: ConfigService) => ({ bucket: config.get("BUCKET") }),
inject: [ConfigService],
});
forRootAsync 변형은 구성이 다른 provider(ConfigService 같은)에 의존하게 합니다 — 옵션이 검증된 구성에서 올 때 필수적입니다.
Dynamic module과 forRoot/forFeature 패턴은 구성 가능하고 재사용 가능한 module을 구축하는 중요한 NestJS 개념입니다 — 그리고 이를 이해하는 것은 생태계를 사용하는 데도, 공유 module을 구축하는 데도 가치가 있습니다.
거의 모든 주요 NestJS module(ConfigModule, TypeOrmModule, JwtModule, BullModule, CacheModule)이 .forRoot()/.register()/.forFeature()를 통해 구성되는 dynamic module이므로, 이 호출이 무엇인지 인식하는 것(정적 import가 아니라 import 시점에 module에 구성을 전달하는 것)이 전체 생태계가 어떻게 연결되는지 명확하게 합니다.
forRoot 대 forFeature 관례(데이터베이스 연결이나 secret 같은 일회성 전역 설정에는 forRoot; 각 module에 entity를 등록하는 것 같은 기능별 등록에는 forFeature)는 끊임없이 마주치는 패턴입니다.
시니어 개발자에게, 자신만의 dynamic module을 구축하는 방법(전달된 옵션에서 구성된 provider를 가진 DynamicModule을 반환, 종종 injection token을 통해, config 의존 설정을 위한 비동기 변형과 함께)을 아는 것이 재사용 가능하고 구성 가능한 module — 앱의 다른 부분이나 다른 프로젝트가 자체 설정으로 import하는 공유 인프라(storage, 외부 API 클라이언트, 커스텀 config) — 를 만들게 합니다.
이는 더 고급 주제이지만, NestJS의 모듈식 아키텍처를 깊이 이해하고 전문적이고 재사용 가능한 module 라이브러리를 구축하는 데 기본적이며, 대규모의 잘 구조화된 NestJS 시스템을 설계하는 중요한 능력입니다.