NestJS 스터디
본 블로그에서는 NestJS의 모듈 캡슐화, Config Module 설정, 그리고 Web Socket 활용법에 대해 자세히 알아봅니다.
Module Encapsulation
NestJS 의 모듈은 다음과 같은 구조로 이루어져 있다고 공식 문서에서 설명한다.
| Keyword | Description | | --- | --- | | Providers | the providers that will be instantiated by the Nest injector and that may be shared at least across this module | | Controllers | the set of controllers defined in this module which have to be instantiated | | Imports | the list of imported modules that export the providers which are required in this module | | Exports | the subset of providers that are provided by this module and should be available in other modules which import this module. You can use either the provider itself or just its token (provide value) |
모듈은 기본적으로 프로바이더를 캡슐화한다. 즉, 현재 모듈에 직접 포함되거나 가져온(import) 모듈에서 내보내지(export) 않은 프로바이더를 삽입할 수 없다. 따라서 모듈에서 내보낸 프로바이더를 모듈의 공용 인터페이스 또는 API로 간주할 수 있다.
AppModule 에서 CatsModule 의 CatsService 를 DI 하는 방법은 다음의 두 가지 방법 중 하나로 할 수 있다.
@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}
@Module({
  imports: [CatsModule],
  providers: [CatsService]
})
export class AppModule {}
첫 번째 방법은, AppModule 에서 CatsModule 를 import 한 후, AppModule 에서 CatsService 를 Provider 로 등록한 후에 사용하는 것이다. 이렇게 세팅해도 동작하긴 하지만, CatsService 를 CatsModule 에 제대로 캡슐화했다고 볼 수는 없다. 만약 앱이 커짐에 따라 모듈들이 많아진다면, providers 항목에 매우 많은 클래스들이 등록되어야 할 것이다.
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
export class CatsModule {}
@Module({
  imports: [CatsModule],
})
export class AppModule {}
위의 방법은 공식 문서에 소개된 방법이다. CatsService 는 CatsModule 안에서 exports 클래스로 등록되었다. 이러한 기능을 통해, AppModule 에서는 별도로 CatsService 를 provider 로 등록하지 않아도 사용 가능하다.
NestJS Config Module
NestJS 에서 어플리케이션 설정은 ConfigModule, ConfigService 를 통해 관리할 수 있다. Express 의 경우, .env 나 AWS 의 Secret Manager 등에서 민감한 정보들 (ex. DB Password) 등을 불러온다. ConfigModule 은 이러한 설정들을 NestJS Container 안에서 간편하게 설정할 수 있게 도와준다.
NestJS 에서는 .env 에 대한 지원을 기본적으로 지원한다. App Module 에서 다음과 같이 간단하게 설정할 수 있다.
@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath:
        process.env.NODE_ENV === 'prod'
          ? '.production.env'
          : '.development.env',
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
위의 예는 개발서버, 실서버에서 사용할 환경변수를 분리하는 환경을 설정한 것이다.
Config Module 은 AppModule 에서 주로 사용되지만, 다른 모듈들에서도 사용되는 경우가 많다. 이러한 경우에, 다른 Component 들과 같이 IoC 컨테이너에 등록해서 사용할 수 있다. 다음의 Mongoose Module 의 예를 보자.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigService, ConfigModule } from '@nestjs/config';
@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => {
        return {
          uri: `mongodb://localhost`,
          dbName: configService.get('MONGODB_DBNAME'),
          user: configService.get('MONGODB_USER'),
          pass: configService.get('MONGODB_PASSWORD'),
          useNewUrlParser: true,
          useUnifiedTopology: true,
        };
      },
      inject: [ConfigService],
    }),
  ],
})
export class MongoDbModule {}
위의 예는 MongooseModule 에서 ConfigService의 MONGODBDBNAME, MONGODBUSER, MONGODB_PASSWORD 를 불러오는 기능을 보여준다. 다른 컨테이너처럼, ConfigModule 를 import 한 후, ConfigModule 내의 ConfigService 를 useFactory 함수에 DI 해서 사용할 수 있다.
Web Socket
Document
Websocket 에서는 Gateway 가 중점적으로 Client 와 Server 를 이어주는 역할을 한다.
Contents
Module Encapsulation Config Module Web Socket
이것도 읽어보세요