Nest.js Module

모듈은 @Module() 데코레이터로 주석이 달린 클래스이다. @Module() 데코레이터는 Nest.js가 애플리케이션 구조를 구성하는 데 사용하는 메타 데이터를 제공한다. 여기서 데코레이터는 @Moudle() 속성이 모듈을 설명하는 단일 개체를 사용한다.

App Module 안에 BoardModule과 Auth Module이 있으면 각 모듈 안에 Controller, Entity, Service 등이 있다. (이렇게 모듈별로 나눠서 MSA를 구축하는건가???)  -> 맞는 것 같다. 효과적으로 모듈을 구성하는 것이 중요해 보인다. 대부분의 애플리케이션에서 결과 아키텍처는 밀접하게 관련된 기능 세트를 각각 캡슐화하는 여러 모듈을 사용한다고 한다.

 

각 응용 프로그램은 하나 이상의 모듈(루트 모듈)이 있다. 루트 모듈은 Nest가 사용하는 시작점이다. 

모듈의 모습

 

  • providers
    • nest 인젝터에 의해 인스턴스화되고 적어도 해당 모듈 전체에서 공유될 수 있는 공급자
  • controllers
    • 인스턴스화되어야 하는 이 모듈에 정의된 컨트롤러 세트
  • imports
    • 해당 모듈에 필요한 공급자를 내보내는 가져온 모듈 목록
  • exports
    • 하위 집합은 providers 모듈에서 제공되며, 모듈을 가져오는 다른 모듈에서 사용할 수 있어야 한다. 공급자 자체 또는 해당 토큰만 사용할 수 있다.

모듈은 기본적으로 공급자를 캡슐화한다. 즉, 현재 모듈에 직접 포함되지 않거나 가져온 모듈에서 내보낸 공급자를 삽입할 수 없다. 따라서 모듈에서 내보낸 공급자를 모듈의 공용 인터페이스 또는 API로 간주할 수 있다.

 

Feature Module

CatsController, CatsService는 동일한 애플리케이션 도메인에 속한다. 서로 밀접하게 관련되어 있으므로 기능 모듈로 이동하는 것이 좋다. 기능 모듈은 단순히 특정 기능과 관련된 코드를 구성하여 코드를 체계적으로 유지하고 명확한 경계를 설정한다. 이는 특히 애플리케이션의 규모가 커짐에 따라 복잡성을 관리하고 SOLID 원칙으로 개발하는데 도움이 된다.

Cat/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 {}
!힌트
CLI를 사용하여 모듈을 생성하려면 $ nest g moudle cats 명령을 실행하기만 하면 된다.

위 에서는 CatsMoudle 파일에 cats.module.ts를 정의하고 이 모듈과 관련된 모든 것을 cats 디렉터리로 옮겼다. 마지막으로 해야 할 일은 이 모듈을 루트 모듈로 가져오는 것이다. 

app.module.ts

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

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

현재 디렉터리 구조는 아래와 같다.

 

공유 모듈

nest에서 모듈은 기본적으로 싱글톤이므로 여러 모듈 간에 모든 공급자의 동일한 인스턴스를 쉽게 공유할 수 있다. 모든 모듈은 자동으로 공유 모듈이 되기 때문에 일단 생성되면 모든 모듈에서 재사용할 수 있다. 여러 다른 모듈 간에 CatsService 인스턴스를 공유해야된다고 가정해본다면, 아래와 같이 CatsService 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를 가져오는 모든 모듈은 Cats Service에 액세스할 수 있으며 이를 가져오는 다른 모든 모듈과 동일한 인스턴스를 공유한다. 

 

Module re-exporting

위에서 볼 수 있듯 모듈은 내부 공급자를 내보낼 수 있다. 추가적으로, 가져온 모듈을 다시 내보낼 수도 있다. 아래 예에서는 CommonMoudle을 coremodule로 가져오거나 내보내므로 이 항목을 가져오는 다른 모듈에서 사용할 수 있다.

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

 

 

Dependency Injection

모듈 클래스는 공급자도 주입할 수 있다.

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

 

그러나 모듈 클래스 자체는 순환 종속성으로 인해 공급자로 주입될 수 없다. 

 

 

전역 모듈

동일한 모듈 세트를 모든 곳에서 가져와야 한다면 불편할 수 있다. Nest와 달리 Angular는 providers 전역 범위에 등록된다. 일단 정의되면 어디에서나 사용할 수 있다. 그러나 Nest는 모듈 범위 내에 공급자를 캡슐화한다. 캡슐화 모듈을 먼저 가져오지 않으면 다른 곳에서 모듈 공급자를 사용할 수 없다.

 

즉시 사용 가능한 모든 곳에서 사용할 수 있는 공급자 세트를 제공하려는 경우 데코레이터를 사용하여 모듈을 전역으로 @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()은 모듈을 전역 범위로 만든다. 전역 모듈은 일반적으로 루트 또는 코어 모듈에 의해 한 번만 등록되어야 한다.

 

 

동적 모듈

Nest 모듈 시스템은 동적 모듈이라는 강력한 기능이 포함되어 있다. 이 기능을 사용하면 공급자를 동적으로 등록하고 구성할 수 있는 사용자 정의 가능한 모듈을 쉽게 만들 수 있다. 동적 모듈은 링크에서 광범위하게 다룬다. 

 

아래는 DatabaseModule에 대한 동적 모듈 정의의 예이다.

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

@Module({
  providers: [Connection],
  exports: [Connection],
})
export class DatabaseModule {
  static forRoot(entities = [], options) {
    const providers = createDatabaseProviders(options, entities);
    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }
}
!힌트
forRoot() 메서드는 동기적, 비동기적으로 동적 모듈을 반환할 수 있다.

이 모듈은 기본적으로 Connection 공급자를 정의하지만 추가적으로 forRoot() 메서드에 전달된 entities 및 개체에 따라 저장소와 같은 공급자 컬렉션을 노출한다. 동적 모듈에서 반환된 속성은 데코레이터에 정의된 기본 모듈 메타데이터를 확장한다. 이것이 정적으로 선언된 공급자와 동적으로 생성된 저장소 공급자가 모두 모듈에서 내보내지는 방식이다.

 

전역 범위에 동적 모듈을 등록하려면 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 {}

동적 모듈을 다시 내보내려면 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 {}
!힌트
링크에서는 configurableModuleBuilder를 사용하여 고도로 사용자 정의 가능한 동적 모듈을 구축하는 방법을 알 수 있다.

 

 

Reference

'Javascript' 카테고리의 다른 글

Nest.js를 이용한 게시판 API 개발  (0) 2024.07.21
Nest.js Pipes  (0) 2024.06.17
ES6  (0) 2024.06.05
기본 Javascript 문법  (0) 2024.06.04
왜 Javascript & Node.js를 사용할까?  (0) 2024.05.31

+ Recent posts