모듈은 @Module 데코레이터로 정의된 클래스이며, 이 모듈들의 조합을 통해 NestJS의 구조가 결정된다.
애플리케이션은 반드시 하나의 Root Module이 있어야 작동하며 그 외의 기능들을 구현하는 모듈들을 Root Module로 연결하여 사용한다. @Module 데코레이터는 아래의 인자들을 받아 모듈을 구성할 수 있다.
providers
: Injectable된 클래스들을 인스턴스화 하고 인스턴스는 모듈 안에서 최소한으로 공유된다.controllers
: 해당 모듈에서 사용되는 컨트롤러의 모음을 인스턴스화 해준다.imports
: 다른 모듈들을 임포트하여 연결해준다. export된 provider가 있다면 사용할 수 있다.exprots
: 임포트하는 모듈에서 사용할 provider를 지정할 수 있다.앞서 만든 CatsController와 CatsService는 같은 영역에서 서로 연결할 수 있기 때문에 하나의 Feature Module로 묶을 수 있다. 하나의 module
내의 provider
와 controller
는 모듈 내에서만 사용하는 것이 원칙이다. 이렇게 해야 코드가 조직적으로 운영되어 SOILD원칙을 준수할 수 있다.
아래 예제를 통해 cats.module.ts 모듈을 정의해 보자.
// cats/cats.module.ts
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 {}
각 controller
와 provider
를 @Module
데코레이터를 통해 CatsModule
클래스를 정의하고 export
해준다. CatsModule
은 이제 다른 모듈에 import
시킬 수 있다. 만약 import
될 모듈에서도 CatsService
provider를 사용하고 싶다면 CatsService
provider를 export
해줄 수 있다.
이제 root module인 AppModule
에서 import
해보자.
// app.module.ts
import {Module} from "@nestjs/common";
import {CatsModule} from "./cats/cats.module";
@Module({
imports: [CatsModule]
})
export class AppModule{}
앞서 말했듯이 모든 모듈은 싱글톤이기 때문에 다른 모든 모듈에서도 export하여 사용할 수 있다.
모든 모듈은 의존성을 주입해 둘 수 있다. 단 모듈 클래스 자체를 provider로 주입하는 건 불가능하다.
라고 공식 문서에 나와있지만, 어떤 경우에 Module에 provider를 injection하는 건지는 모르겠다..
// cats.module.ts
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) {}
//constructor를 통해 CatsModule에 의존성을 주입한다.
}
모듈에 @Global 데코레이터를 달면 모든 모듈에서 사용 가능한 글로벌모듈이 된다. 글로벌 모듈은 단 하나만 가능하며 글로벌모듈을 별도로 import없이 모든 모듈에서 provider를 사용할 수 있다. (물론 export는 해줘야 한다.)
NestJS의 모듈을 100% 활용하기 위한 강력한 기능이다. 이 방법을 통해 모듈을 import할 때 커스텀을 할 수 있는데 이를 통해 provider를 동적으로 설정하고 등록할 수 있게 해준다. 동적 모듈은 너무 방대한 내용이기 때문에 여기를 통해 자세히 알아보기로 하자.
아래는 동적 모듈을 만드는 예제이다.
import {Module, DynamicModule} from "@nestjs/common";
import {createDatabaseProviders} from "./database.providers";
import {Connection} from "./connection.provider";
@Module({
providers: [Connection]
//기본적으로 Connection provider를 포함하고 있는 모듈이다.
})
export class DatabaseModule {
static forRoot(entities = [], options?): DynamicModule {
//forRoot static 메소드를 통해 인수를 받아 동적으로 모듈을 정의한다.
const providers = createDatabaseProviders(options, entities);
return {
module: DatabaseModule,
//동적으로 재구성할 모듈을 지정한다.
providers,
exports: providers
}
//동적으로 모듈을 재구성한다.
}
}
이렇게 정의된 동적 모듈을 아래와 같이 import하게 되면 인자에 맞춰 재구성된다
@Module({
imports: [DatabaseModule.forRoot([User])]
})
이렇게 만들어진 조립하듯이 맞춰서 서버를 동작시키는 것이 NestJS의 기본적인 컨셉이다. 간단한 서버를 돌릴 때는 성능이나 설계에서 큰 장점이 없지만, 서버의 규모가 커지고 협업을 하게 될수록 NestJS의 장점이 커진다고 한다.
이 외에도 공식문서에는 Middlewere나 Guards등의 기능을 설명하고 있지만 일단 당장은 GraphQL을 동작하는 게 먼저이니 필요할 때 찾아보는 걸로 하고 NestJS에 대한 공부는 여기까지만 하기로 했다.
Nest 배우시는 분이 있다니 반갑네요 ㅎㅎㅎ