[NestJS] Modules 개념 및 실습
저번 시간에는 [NestJS] Providers 개념 및 실습 을 알아보았습니다. 이번 시간에는 NestJS의 Modules 개념을 살펴보고 실습을 진행하겠습니다.
모든 모듈은 각각 선언되어 최종적으로 하나로 취합된다. 앞서 만들었던 app.modules.ts 와 user.modules.ts 는 하나로 취합되어 최종적으로는 아래와 같은 형태로 병합되어야 한다.
그 병합된 module 의 경우 아래와 같이 main.ts 에서 NestFactory 를 통해 Create 되고 app 이 실행된다.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
**const app = await NestFactory.create(AppModule, {});**
await app.listen(3000);
}
bootstrap();
AppModule의 경우 기존에 만들었던 UserModule 을 import 하고 있는데 아래와 같다.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
@Module({
**imports: [UserModule],**
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
결국 각각의 기능을 담당하는 Controller와 Providers 는 기능 모듈 별로 Module Decorator 에 등록되고 이 모듈들은 App Module 에 import 하여 최종적으로 NestFactory로 create 되는 플로우를 가지고 있다고 생각하면 될 것이다. 만약 AppModule에서 UserModule 을 import 하지 않는다면 아래와 같이 Nest Core 가 인식하지 못할 것이다.
정상적으로 포함 했을 때 Context Reload 시
정상적으로 포함되지 않았을 때 Context Reload 시
위와 같이 차이가 존재한다는 것을 인식할 수 있다.
작은 규모의 어플리케이션이 아니라면 분명 기능 단위로 @Module 을 만들고 그 하위에 Providers 에 등록되는 Dependency와 Controller 들이 존재할텐데 이 단위의 모듈은 최종적으로 Root 의 App 모듈에 포함되게 될 것이고 아래와 같이 각각 공유가 될 것이다.
모듈로 부터 의존성이 기본적으로 Singleton 으로 유지되고 관리되는데 공유가 된다고 했다. 만약 이러한 케이스라면 어떻게 될까?
기존에 만들었던 UserController 에 userService 의존성이 아닌 testService 의 의존성을 주입하려고 한다고 치자. 해당 의존성은 UserModule 이 아닌 다른 TestModule 에서 참조한다고 했을 때 이론에 의하면 모듈의 의존성들이 서로 공유가 되기 때문에 별도의 설정 없이 사용할 수 있을 것 같다.
추가한 TestService와 TestModule 의 코드는 아래와 같다.
src/test/test.module.ts
import { Module } from '@nestjs/common';
import { TestService } from './test.service';@Module({
providers: [TestService]
})
export class TestModule {}
src/test/test.service.ts
import { Injectable } from '@nestjs/common';@Injectable()
export class TestService {
getInfo(): string {
return `This is TestService instance.`;
}
}
위의 두 가지를 추가하고 App Module 에 최종적으로 TestModule을 추가하였다.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { TestModule } from './test/test.module';@Module({
imports: [UserModule, TestModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
자 그렇다면 UserController에서 UserService가 아닌 TestModule 에 Provider로 등록된 TestService 도 쓸 수 있을 것 같다. 하지만, 아래와 같은 에러가 Context Reload 시 발생한다.
이유는 UserController에서 주입한 TestService 의 의존성을 찾을 수 없다는 것이고 의존성을 사용하기 위해서는 UserModule 에서 TestService를 포함하고 있는 모듈을 Import 해야 한다는 것이다.
위의 에러를 참고하여 코드를 UserModule에 추가 해보도록 하겠다.
src/user/user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { TestModule } from '../test/test.module';@Module({
imports: [TestModule],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
UserModule 에서 TestModule을 import 만 한다고 바로 쓰진 못하고 TestModule 에서 다른 모듈에서 사용하고자 할 의존성을 export 해줘야 한다.
src/test/test.module.ts
import { Module } from '@nestjs/common';
import { TestService } from './test.service';@Module({
providers: [TestService],
exports: [TestService]
})
export class TestModule {}
이렇게 다른 모듈에서 사용하고자 하는 의존성을 명시적으로 export 해줘야 아래와 같이 정상적으로 Nest Core가 작동하는 것을 볼 수 있다.
UserController 에서 TestService 의 의존성에서 추가한 부분은 아래와 같다.
Route로는 /user/test 라는 api 를 추가하였고 해당 기능은 testService 의존성에서 호출 되고 있다.
API 도 정상적으로 호출 되는 것을 확인할 수 있다.
만약 이러한 과정이 번거롭고 해당 모듈을 Global 모드로 설정하여 모든 모듈에서 사용할 수 없을까 라는 생각도 할 수 있을 것이다. 그럴때는 @Global() 이라는 Decorator 를 모듈에서 선언 해주면 된다. 아래와 같이 Decorator 로 Global 이라고 설정하면 이 모듈에서 export 하고자 하는 모든 의존성은 다른 모듈에서 import를 할 필요 없이 사용이 가능하다.
import { Global, Module } from '@nestjs/common';
import { TestService } from './test.service';@Global()
@Module({
providers: [TestService],
exports: [TestService]
})
export class TestModule {}
글로벌로 선언을 남발하는 것은 아키텍처를 구성하는 상황에서 좋은 방향은 아니라고 NestJS 프로젝트를 주관한 재단에서 이야기 하고 있으니 잘 적절히 사용하도록 하자.
모듈은 Dynamic 모듈과 Config 모듈 등 다양한 전략들이 존재하는데 다음번에 설명하도록 하겠다.
다음 시간에는 [NestJS] Middleware 개념 및 실습 을 살펴보도록 하겠다.