lottie
Seungjun's blog
blog
nestjs docs 5편

Modules

    모듈은 @Module() 데코레이터가 달린 클래스입니다. @Module() 데코레이터는 메타데이터를 제공하여 Nest가 애플리케이션 구조를 구성하는 데 사용합니다.

image

  각 애플리케이션은 적어도 하나의 모듈, 루트 모듈을 가지고 있습니다. 루트 모듈은 Nest가 모듈 및 provider 관계 및 종속성을 해결하는 데 사용하는 내부 데이터 구조인 애플리케이션 그래프를 빌드하는 데 사용되는 시작점입니다. 매우 작은 애플리케이션은 이론적으로 루트 모듈만 가질 수 있지만, 이는 일반적인 경우가 아닙니다. 따라서 대부분의 애플리케이션에서는 각각 관련성이 높은 기능집합을 캡슐화하는 여러 모듈을 사용하여 결과적인 아키텍처가 형성됩니다.


@Module() 데코레이터는 모듈을 설명하는 속성을 포함하는 객체 하나를 취합니다.

providers

Nest 인젝터에 의해 인스턴스화되고 적어도 이 모듈에서 공유될 수 있는 제공자

controllers

인스턴스화되어야 하는 이 모듈에 정의된 컨트롤러 세트

imports

이 모듈에 필요한 provider를 내보내는 가져온 모듈 목록

exports

이 모듈에서 제공하는 providers의 하위 집합은 이 모듈을 가져오는 다른 모듈에서 사용할 수 있어야 합니다. provider 자체 또는 해당 provider값 만 사용할 수 있습니다.

모듈은 기본적으로 provider를 캡슐화합니다. 이는 현재 모듈의 일부가 아니거나 가져온 모듈에서 내보내지지 않은 provider를 주입하는 것이 불가능하다는 것을 의미합니다. 따라서 모듈에서 내보낸 프로바이더를 모듈의 공개 인터페이스 또는 API로 간주할 수 있습니다.

Feature modules

CatsControllerCatsService는 같은 애플리케이션 도메인에 속합니다. 서로 밀접하게 관련되어 있으므로, 이들을 기능 모듈(feature module)로 이동하는 것이 합리적입니다. 기능 모듈은 특정 기능과 관련된 코드를 구성하여 코드를 정리하고 명확한 경계를 설정합니다. 이는 복잡성을 관리하고 SOLID 원칙에 따라 개발하는 것을 돕습니다. 특히, 애플리케이션의 규모와/또는 팀 규모가 커질 경우에 유용합니다. 

 이를 보여주기 위해, CatsModule을 생성합니다.

// cats/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 {}

위에서 우리는 cats.module.ts 파일에서 CatsModule을 정의했고, 이 모듈과 관련된 모든 것을 cats 디렉토리로 옮겼습니다. 마지막으로 해야 할 일은 이 모듈을 루트 모듈(AppModule, app.module.ts 파일에서 정의됨)로 가져오는 것입니다.

// app.module.ts

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}


Shared modules

   Nest에서 모듈들은 기본적으로 싱글톤 패턴입니다. 따라서 여러 모듈 간에 어떤 provider라도 같은 인스턴스를 쉽게 공유할 수 있습니다.


image


  모든 모듈은 자동으로 공유 모듈이 됩니다. 생성되면 어떤 모듈에서든 재사용할 수 있습니다.

  예를 들어, 여러 다른 모듈 간에 CatsService 인스턴스를 공유하려고 한다고 상상해 봅시다. 이를 위해 먼저 CatsService provider를 모듈의 exports 배열에 추가하여 내보내야 합니다. 이는 아래와 같이 나타낼 수 있습니다:

// 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 {}

  이제 CatsModule을 가져오는 모든 모듈은 CatsService에 접근할 수 있으며, 가져오는 다른 모든 모듈과 동일한 인스턴스를 공유합니다.


Module re-exporting

  위에서 본 것처럼, 모듈은 내부 provider를 export 할 수 있습니다. 또한, 모듈은 가져온 모듈을 다시 내보낼 수 있습니다. 아래 예에서 CommonModuleCoreModule에 가져와지면서 동시에 내보내져 다른 모듈에서 이를 사용할 수 있게 됩니다.

@Module({
  imports: [CommonModule],
  exports: [CommonModule],
})
export class CoreModule {}


Dependency injection

  모듈 클래스도 provider를 주입할 수 있습니다 (예: configuration 목적으로).

// 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) {}
}

 그러나 모듈 클래스 자체는 순환 의존성 때문에 공급자로 주입될 수 없습니다


Global modules

  Nest는 provider를 전역 범위가 아닌 모듈 범위 내에 캡슐화합니다. 캡슐화된 모듈을 먼저 가져오지 않으면 모듈의 provider를 다른 곳에서 사용할 수 없습니다. 

  전역적으로 사용할 수 있는 provider 세트를 제공하려면 (예: 도우미, 데이터베이스 연결 등), @Global() 데코레이터를 사용하여 모듈을 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 {}

  @Global() 데코레이터는 모듈의 범위가 전역 범위가 되게 합니다. 전역 모듈은 일반적으로 루트 또는 코어 모듈에 의해 한 번만 등록되어야 합니다. 위 예시에서, CatsService 공급자는 어디에서나 사용할 수 있으며, 서비스를 주입하려는 모듈은 CatsModule을 가져올 필요가 없습니다.


Dynamic modules

   Nest 모듈 시스템에는 동적 모듈이라는 강력한 기능이 포함되어 있습니다. 이 기능을 사용하면 동적으로 provider를 등록하고 구성할 수 있는 커스터마이즈 가능한 모듈을 쉽게 만들 수 있습니다.

 다음은 DatabaseModule에 대한 동적 모듈 정의 예시입니다:

import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';

@Module({
  providers: [Connection],
})
export class DatabaseModule {
  static forRoot(entities = [], options?): DynamicModule {
    const providers = createDatabaseProviders(options, entities);
    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }
}

   이 모듈은 기본적으로 Connection provider를 정의합니다(@Module() 데코레이터 메타데이터에 포함). 그 외에도 forRoot() 메서드에 전달되는 entitiesoptions 객체에 따라 여러 provider의 컬렉션(예: 리포지토리)을 노출합니다. 동적 모듈의 반환 속성은 @Module() 데코레이터에 정의된 기본 모듈 메타데이터를 확장합니다(덮어쓰기가 아님). 그래서 정적으로 선언된 Connection 공급자 그리고 동적으로 생성된 리포지토리 공급자가 모듈에서 내보내집니다. 

 동적 모듈을 전역 범위에 등록하려면 global 속성을 true로 설정하세요.

{
  global: true,
  module: DatabaseModule,
  providers: providers,
  exports: providers,
}

 DatabaseModule은 다음과 같은 방식으로 가져오고 구성할 수 있습니다:

import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [DatabaseModule.forRoot([User])],
})
export class AppModule {}

 동적 모듈을 다시 export하려면, export 배열에서 forRoot() 메서드 호출을 생략할 수 있습니다.

import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [DatabaseModule.forRoot([User])],
  exports: [DatabaseModule],
})
export class AppModule {}