Nest.js 따라하기 (4) - Modules

Seok·2021년 5월 10일
1

Nest.js 따라하기

목록 보기
3/3
post-thumbnail

이 시리즈는 Nest.js 공식 Document를 바탕으로 따라해보며 내용들을 요약 정리한 게시글입니다.


0.Modules

모듈은 @Module()데코레이터로 주석이 달린 클래스이다. @Module()데코레이터는 Nest가 어플리케이션 구조를 구성하는데 사용하는 메타데이터를 제공한다.

각 어플리케이션에는 루트모듈이라는 하나 이상의 모듈이 있다. 루트 모듈은 Nest가 어플리케이션 그래프를 빌드하는데 사용하는 시작점이다. Nest가 모듈과 Provider관계 및 종속성을 해결하는데 사용하는 내부 데이터 구조이다. 매우 작은 어플리케이션에는 이론적으로 루트 모듈만 있을 수 있지만 일반적인 경우는 아니다. 모듈은 구성요소를 구성하는 효과적인 방법으로 적극 권장된다는 점을 강조하고 싶다. 그러므로 대부분의 어플리케이션에서 결과 아키텍처는 각각 밀접하게 관련된 기능 집합을 캡슐화하는 여러 모듈을 사용한다.

@Module()데코레이터는 속성이 모듈을 설명하는 단일 객체를 사용한다.

Providers : Nest인젝터에 의해 인스턴스화 되고 적어도 이 모듈에서 공유될 수 있는 provider
controllers : 인스턴화 되어야 하는 이 모듈에 정의된 컨트롤러 세트
imports : 이 모듈에 필요한 프로바이더를 내보내는 가져온 모듈 목록
exports : 이 모듈에서 제공하고 이 모듈을 임포트하는 다른 모듈에서 사용할 수 있어야하는 Provider의 하위 집합

모듈은 기본적으로 캡슐화한다. 현재 모듈에 직접 포함되거나 import 된 모듈에서 export되지 않는 provider를 삽입할 수 없다. 따라서 모듈에서 내보낸 프로바이더를 모듈의 공용 인터페이스 또는 API로 간주할 수 있다.


1.Feature modules

CatsControllerCatsService는 동일한 어플리케이션 도메인에 속한다. 밀접하게 관련되어 있으므로 기능 모듈로 이동하는것이 좋다. 기능 모듈은 단순히 특정 기능과 관련된 코드를 구성하여 코드를 체계적으로 유지하고 명확한 경계를 설정한다. 이는 특히 어플리케이션 및 팀의 규모가 커짐에 따라 복잡성을 관리하고 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디렉토리로 옮겼다. 마지막으로 해야할 일은 이 모듈을 루트 모듈(app.module.ts파일에 정의된 AppModule)로 가져오는 것 이다.

app.module.ts

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

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

아래는 현재의 디렉토리 구조 이다.


2.Shared modules

Nest에서 모듈은 기본적으로 싱글톤이므로 여러 모듈간에 쉽게 프로바이더의 동일한 인스턴스를 공유할 수 있다.

모든 모듈은 자동으로 공유 모듈이다. 일단 생성되면 모든 모듈에서 재사용할 수 있다. 여러 다른 모듈간에 CatsService의 인스턴스를 공유하고 싶다고 가정하자. 이렇게 하려면 먼저 아래와 같이 모듈의 exports배열에 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을 가져오는 모든 모듈은 CatsService에 억세스할 수 있으며 이를 가져오는 다른 모든 모듈과 동일한 이스턴스를 공유한다.


3.Module re-exporting

위에서 볼 수 있듯이 모듈은 내부 프로바이더를 내보낼 수 있다. 또한 가져온 모듈을 다시 내보낼 수 있다. 아래 예제에서 CommonModuleCoreModuleimport되고 export되므로 이 모듈을 가져오는 다른 모듈에서 사용할 수 있다.

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

4. Dependency injection

모듈 클래스는 Provider 주입도 가능하다.

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

그러나 모듈 클래스 자체는 순환 종속성으로 인해 프로바이더로 삽입될 수 없다.


5.Global modules

모든 곳에서 동일한 모듈 집합을 가져와야 한다면 지루할 수 있따. Nest는 모듈 범위내에서 Provider를 캡슐화 한다. 캡슐화 모듈을 먼저 가져오지 않으면 모듈의 Provider를 다른 곳에서 사용할 수 없다.

즉시 사용할 수 있어야하는 프로바이더 집합을 제공하려면 @Globl()데코레이터를 사용하여 전역모듈을 만든다.

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 provider는 어디에나 있을 것이며, 서비스를 주입하려는 모듈은 import 배열에서 CastsModule을 가져올 필요가 없다.

모든 것을 글로벌하게 만드는 것은 좋은 디자인이 아니다. 필요한 보일러플레이트의 양을 줄이기 위해 전역 모듈을 사용할 수 있다. imports배열은 일반적으로 소비자가 모듈의 API를 사요할 수 있도록 하는 방법이다.


6.Dynamic modules

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

다음은 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를 정의하지만 추가로 forRoot()메서드에 전달된 entitiesoptions객체에 따라 저장소와 같은 프로바이더 컬렉션을 추가로 노출한다. 동적모듈이 반환하는 속성은 @Module데코레이터에 정의된 기본 모듈 메타데이터를 재 정의하는 대신 확장한다. 이것이 정적으로 선언된 Connection Provider와 동적으로 생성된 저장소 Provider를 모듈에서 export하는 방법이다.

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

다시 동적 모듈을 내보내려면 exports 배열에서 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 {}

출처

Nest.js 공식 Document

profile
🦉🦉🦉🦉🦉

0개의 댓글