(NestJs) NestJs - Module

최건·2025년 5월 7일

참고 문서

Module이란?

  • @Module() 데코레이터로 정의된 클래스이며, NestJS에서 어플리케이션을 구조화하고 관리하는 기본 단위이다.
  • 모든 NestJS 앱은 최소한 하나의 루트 모듈(AppModule) 을 가진다.
  • 모듈은 providers, controllers, imports, exports로 구성되어 내부 구성요소를 캡슐화하며 재사용 가능하게 만든다.

주요 속성

  • providers: 서비스, 유틸 등 의존성 주입 대상
  • controllers: 요청/응답 처리용 컨트롤러
  • imports: 다른 모듈을 현재 모듈에서 사용
  • exports: 외부 모듈에서 사용할 수 있도록 내보냄
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  imports:[AnyModule],
  controllers: [CatsController],
  providers: [CatsService],  // CatsService 제공
  exports: [CatsService]  // 외부 모듈에서도 사용 가능하도록 export
})
export class CatsModule {}
// app.module.ts (또는 다른 모듈)
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],  // CatsModule을 불러옴
})
export class AppModule {}

exports를 해야하는 이유

  • exports를 사용하지 않으면 해당 모듈의 Provider를 다른 모듈에서 사용할 수 없다.
  • 즉, provider는 모듈 내부에서 기본적으로 캡슐화되어 있어서 exports 하지 않으면 외부에 공개되지 않는다.(private 설정이 Default라고 생각하는 게 편할 것 같다.)
  • 위 코드를 예시로 들면, CatsService를 담고 있는 Module에서 exports: [CatsService]를 설정해주지 않는다면, 다른 모듈에서 CatsService에 접근할 수 없다.
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  imports:[AnyModule],
  controllers: [CatsController],
  providers: [CatsService],  // CatsService 제공
  exports: []  // CatsService 외부로 내보내지 않음 (private)
})
export class CatsModule {}
// anyModule.module.ts
@Module({
  imports: [CatsModule],
  providers: [AnyService],
})
export class AnyModule {}
// any.service.ts
@Injectable()
export class AnyService {
  constructor(private catsService: CatsService) {} // 이 시점에서 에러 발생
}
  • AnyModule에서는 CatsModule에 있는 CatsService에 접근할 수 없다.
  • 위와 같은 경우 CatsService를 주입하려 하면 NestJS는 Cannot resolve dependency 에러를 던지게 된다.

Global 모듈 만들기

  • 다른 모듈에 접근할 때 해당 모듈에서 imports:[Module...]로 불러와야한다.
  • 하지만 @Global() 데코레이터를 사용하면 다른 모듈에서 해당 모듈에 접근할 때 imports를 하지 않아도 바로 사용이 가능하다.
  • 단, exports로 사용할 Provider를 설정해야 한다!!
  • 보통 공통 서비스(Helper, Logger, DB, Auth) 등은 Global로 설정한다.
import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],  // 반드시 exports도 함께 해야 외부 사용 가능
})
export class CatsModule {}
// 다른 모듈에서 CatsModule을 import하지 않아도 CatsService 사용 가능
@Injectable()
export class OtherService {
  constructor(private catsService: CatsService) {}
}

Dynamic Module이란?

  • 반적으로 모듈은 정적으로 정의되며, @Module() 데코레이터 내부에서 providers, controllers, imports, exports 등을 미리 지정해야 한다.
  • 하지만 경우에 따라 앱이 실행될 때 동적으로 모듈을 설정해야 하는 상황이 발생할 수 있다.
  • 예를 들어 데이터베이스 연결 → 환경 변수에 따라 다른 DB에 연결해야 할 때, API 인증 모듈 → 특정 설정 값에 따라 다른 인증 전략을 사용할 때, 다국어 지원 (i18n) → 설정에 따라 지원하는 언어를 다르게 구성할 때
  • 이러한 경우 동적 모듈(Dynamic Module) 을 사용하면 모듈을 유연하게 생성 및 구성할 수 있다.
import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';

@Module({
  providers: [Connection], // 기본 Provider
  exports: [Connection],
})
export class DatabaseModule {
  static forRoot(entities = [], options?): DynamicModule {
    const providers = createDatabaseProviders(options, entities); // 동적 Provider 생성

    return {
      module: DatabaseModule,
      providers: providers, // 동적으로 설정된 providers 추가
      exports: providers,
    };
  }
}
  • forRoot() 메서드를 통해 모듈을 생성할 때 옵션을 받을 수 있음.
  • 이 옵션을 기반으로 createDatabaseProviders() 함수를 호출하여 동적으로 Provider를 생성.
  • providersexports를 설정하여 해당 설정을 다른 모듈에서도 사용 가능하게 함.
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';

@Module({
  imports: [DatabaseModule.forRoot(['User', 'Product'], { sync: true })],
})
export class AppModule {}
  • forRoot() 메서드에서 ['User', 'Product'] 엔티티와 sync: true 옵션을 받아 런타임에서 동적으로 provider를 설정한다.

글로벌 동적 모듈

  • 만약 전역(Global)으로 사용할 동적 모듈을 만들고 싶다면 { global: true } 옵션을 추가할 수 있다.
static forRoot(entities = [], options?): DynamicModule {
  const providers = createDatabaseProviders(options, entities);

  return {
    global: true,  // 글로벌 모듈 설정
    module: DatabaseModule,
    providers: providers,
    exports: providers,
  };
}
profile
개발이 즐거운 백엔드 개발자

0개의 댓글