@Module()
데코레이터로 주석이 달린 클래스이다.
해당 데코레이터는 Nest가 어플리케이션의 구조를 구성하는데 사용하는 메타데이터를 제공한다.
각 어플리케이션은 루트 모듈이라는 하나 이상의 모듈이 있다.
루트 모듈은 Nest가 어플리케이션 그래프(Nest가 module과 provider의 관계와 의존성을 해결하기 위해 사용하는 내부 데이터 구조)를 빌드하는데 사용하는 시작점이다.
대부분의 어플리케이션에서 결과 아키텍처는 각각 밀접하게 관련된 기능 집합을 캡슐화하는 여러 모듈을 사용한다.
providers | Nest 인젝터에 의해 인스턴스화 되고 적어도 이 모듈에서 공유될 수 있는 프로바이더 |
---|---|
controllers | 인스턴스화 되어야 하는 이 모듈에 정의된 컨트롤러 세트 |
imports | 이 모듈에 필요한 프로바이더를 내보내는 가져온 모듈 목록 |
exports | 이 모듈에서 제공하고 이 모듈을 임포트하는 다른 모듈에서 사용할 수 있어야 하는 프로바이더의 하위집합 |
모듈은 기본적으로 프로바이더를 캡슐화한다.
CatsController
와 CatsService
는 동일한 어플리케이션 도메인에 속하고, 밀접하게 관련되어 있으므로 별도의 기능 모듈(Feature Module) 만든다.(CatsModule
)
Feature Module은 특정 기능과 관련된 코드를 구성해 코드를 체계적으로 유지하고 명확한 경계를 설정한다.
이는 어플리케이션 및 팀의 규모가 커짐에 따라 복잡성을 관리하고 SOLID 원칙에 따라 개발하는데 도움이 된다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
nest -g module cats
을 이용해 모듈을 생성하면 된다.
위 과정을 통해 CatsModule
을 정의하고 해당 모듈과 관련된 모든 것들을 cats
디렉토리에 옮겼다.
이후에는 이 모듈을 루트 모듈에 포함시켜줘야 한다.
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {}
여기까지 했을때, 디렉토리의 구조는 다음과 같다.
Nest에서 모듈은 기본적으로 싱글톤이므로, 여러 모듈 간 쉽게 Provider의 동일 인스턴스를 공유할 수 있다.
모든 모듈은 자동으로 Shared Module이다. 일단 생성되면, 모든 모듈에서 재사용할 수 있다.
여러 모듈 간 CatsService
의 인스턴스를 공유한다고 해보자.
이렇게 하려면 먼저 아래와 같이 모듈의 exports
배열에 CatsService
Provider를 추가하여 export해야 한다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
export class CatsModule {}
이제 CatsModule
을 import하는 모든 모듈은 CatsService
에 접근할 수 있으며, 동일한 인스턴스를 공유한다.
위처럼 모듈은 내부 Provider를 내보낼 수 있다. 또한 가져온 모듈을 다시 내보낼 수 있다.
@Module({
imports: [CommonModule],
exports: [CommonModule],
})
export class CoreModule {}
위 모듈을 import하는 모듈은 자동으로 두 개의 모듈을 import한 셈인 것이다.
모듈 클래스에 Provider도 주입할 수 있다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {
constructor(private catsService: CatsService) {}
}
하지만, Module 클래스는 순환 의존성에 의해 Provider에 주입될 수 없다.
위같은 경우 CatsModule
은 CatsService
를 의존성 주입할 수는 있지만, CatService
가 CatsModule
을 의존성 주입할 수는 없다.
Nest는 모듈 범위 내에서 Provider를 캡슐화 한다. 그렇기에 사용하려는 모듈이 export되지 않았거나, 해당 모듈을 import하지 않고서는 사용(의존성 주입)할 수 없다
하지만, 즉시 사용될 수 있어야 하는 Provider 같은 경우(ex: Helper, Database Connection etc...) @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],
})
export class CatsModule {}
해당 데코레이터는 모듈을 전역 범위로 만든다.
전역 모듈은 일반적으로 Root 또는 Core 모듈에서 한번만 등록해야 한다.
위 예에서 CatsService
Provider는 어디에서는 사용할 수 있을 것이며, 해당 서비스를 의존성 주입하기 위해 import를 할 필요가 없다.
반드시 전역으로 등록해야하는 모듈이 아닌 경우에는 웬만하면, 전역 범위로 모듈을 설정하는 것을 권장하지 않는다.(최대한 export, import로)