개요

NestJS에서 @Module은 기능 단위 의존성을 묶는 단위이자 DI 경계를 정의하는 컨테이너 개념임 핵심은 imports로 외부 의존성 수입, providers로 내부 의존성 등록, exports로 외부 공개 대상을 결정하는 흐름 이해가 전부임

Module의 imports, providers, exports

@Module 선언의 기본 형태

@Module({ imports: [...], providers: [...], exports: [...] })
class SomeModule {}
  • providers
    • 이 모듈에서 새로 생성해 DI 컨테이너에 등록할 대상 목록
    • @Injectable 클래스들 중심 Service, Repository, Adapter 등
    • 예시
providers: [BatchService, PortFetcher, StoreService, MergeService]
  • imports
    • 다른 모듈이 exports로 공개한 provider를 이 모듈에서 사용하겠다는 선언
    • 예시
imports: [RepositoryModule, HttpModule, LoggerModule]
  • exports
    • 이 모듈 바깥에서도 사용 가능하도록 내보낼 provider 목록
    • 예시
exports: [JSON_REPOSITORY, LogRepository]

정리하면 providers는 내부 생성, imports는 외부 수입, exports는 외부 공개 대상 지정임

ScheduleModule.forRoot와 forRoot 패턴

ScheduleModule은 크론과 인터벌 같은 스케줄링 기능을 제공하는 외부 모듈임

  • forRoot는 전역 설정을 포함한 DynamicModule을 반환하는 정적 메서드 패턴
  • 앱 부트스트랩 시 한 번 호출해 필요한 레지스트리와 provider를 구성하는 용도
  • 유사 패턴 예시 TypeOrmModule.forRoot, ConfigModule.forRoot 등

의미상 forRoot는 앱 레벨에서 한 번 설정하고 재사용하는 초기화 진입점 역할임

Symbol 토큰으로 DI 안정화

Nest DI는 토큰 기반 주입 동작 기본은 클래스가 토큰이지만 클래스가 아닌 값 또는 여러 구현을 구분해야 할 때 커스텀 토큰 필요

export const JSON_REPOSITORY = Symbol('JSON_REPOSITORY')
  • Symbol은 전역에서 유일한 값이라 이름 충돌 회피에 유리
  • provider 등록 시 커스텀 토큰과 매핑
{ provide: JSON_REPOSITORY, useFactory: () => /* 인스턴스 반환 */ }
  • 주입 시 @Inject(JSON_REPOSITORY)로 명시적 의존성 선택 가능
constructor(@Inject(JSON_REPOSITORY) private readonly repo: JsonRepository<any>) {}

클래스명으로는 구분이 어려운 경우 또는 인터페이스에 해당하는 구현을 주입해야 할 때 유효한 패턴임

useFactory를 쓰는 이유

기본 등록은 useClass 동작과 동일해 new SomeService(…)로 생성됨 생성 과정이 비동기이거나 외부 설정 주입, 사전 로딩이 필요한 경우 useFactory가 적합

  • 요구 사항 예시
    • 생성 자체가 async로 초기 데이터 로딩 필요
    • 외부 라이브러리 설정값 주입 필요
    • 완성된 인스턴스를 싱글톤으로 공유 필요

간단 예시

{
  provide: JSON_REPOSITORY,
  useFactory: async () => {
    const db = new JsonDB(new Config('path', true))
    await db.load()
    return JsonRepository.create(db)
  },
}

포인트

  • useFactory 결과값이 토큰에 매핑되어 DI 대상이 됨
  • 의존성이 있다면 inject 옵션으로 다른 provider를 안전하게 받아서 사용 권장 new로 직접 만드는 대신 주입 사용 권장

요약

  • imports 외부 모듈이 export한 provider 사용 선언
  • providers 이 모듈에서 생성해 등록하는 의존성 목록
  • exports 외부에서도 사용 가능하도록 공개할 provider 지정
  • forRoot 전역 설정을 수반하는 DynamicModule 초기화 패턴, 앱 부트스트랩에서 한 번 호출하는 용도
  • Symbol 토큰 클래스 이외의 토큰이 필요할 때 충돌 없는 식별자 제공, 다중 구현 구분에 유리
  • useFactory 비동기 초기화나 복잡한 생성 로직을 함수로 캡슐화해 주입, 설정 주입과 싱글톤 공유 시 적합

참고자료