Modules

정민교·2023년 9월 5일
0

NestJS

목록 보기
4/4

📒Modules

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

각 애플리케이션은 적어도 하나의 root module을 가지고 있습니다.

루트 모듈은 Nest가 애플리케이션 그래프를 구축하는데 사용하는 시작점입니다.

애플리케이션 그래프는 Nest가 모듈과 프로바이더 간의 관계와 의존성을 처리하는 데 사용하는 내주 데이터 구조입니다.

매우 작은 애플리케이션의 경우에는 이론적으로 루트 모듈만 가질 수도 있지만 이는 일반적인 경우는 아닙니다.

모듈은 컴포넌트를 조직화하는 효과적인 방법이고, 대부분의 애플리케이션에서는 결과적으로 여러 개의 모듈을 사용하며 각 모듈들은 밀접하게 관련된 기능들을 캡슐화하여 작성하게 됩니다.

@Module 데코레이터는 모듈을 설명하는 프로퍼티들을 가지는 단일 객체를 받습니다.

  • providers:
    Nest injector에 의해 인스턴스화 되고 공유되는 프로바이더들입니다.
  • controllers:
    이 모듈에서 정의된 컨트롤러의 집합으로, 컨트롤러들은 인스턴스화 되어야합니다.
  • imports:
    이 모듈에서 필요한 프로바이더들을 export한 모듈들의 목록입니다.
  • exports:
    이 모듈에서 제공되고 이 모듈을 import 하는 다른 모듈에서 사용 가능해야 하는 프로바이더의 부분 집합입니다. 프로바이더 자체 또는 그 토큰(제공 값)만을 사용할 수 있습니다.

모듈은 기본적으로 프로바이더를 캡슐화합니다.

현재 모듈의 포함되어 있지 않거나, 가져온 모듈에서 export 되어있지 않은 모듈은 주입할 수 없습니다.

따라서 모듈에서 export한 프로바이더는 모듈의 고개 인터페이스 혹은 API로 간주할 수 있습니다.

✔️Feature modules

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

CatsControllerCatsService는 같은 애플리케이션 도메인에 속합니다.

이들은 매우 밀접하게 관련되어 있기 때문에 feature module에 두는 것이 합리적입니다.

feature module은 특정 기능과 관련된 코드를 조직화하여 코드를 정리하고 명확한 경계를 설정합니다.

이는 복잡성을 관리하고 애플리케이션의 크기가 커짐에 따라 SOLID 원칙에 따라 개발하는 데 도움이 됩니다.

nest g module cats

nest clit를 통해 간단하게 위 명령어를 실행하여 module을 생성할 수 있습니다.

cats.module.ts 파일에 CatsModule을 정의했고 관련된 모든 것들을 CatsModule 안에 옮겼습니다.

마지막으로 이 모듈을 root module(app.module.ts에 정의된 AppModule)에서 import 하는 일을 해야합니다.

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

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

이제 디렉토리 구조는 다음과 같습니다

✔️Shared modules

Nest에서 module은 기본적으로 싱글톤입니다. 어떤 프로바이더의 인스턴스든 하나의 인스턴스를 여러 module간에 쉽게 공유할 수 있습니다.

모든 module은 자동으로 shared module입니다. 일단 만들어졌다면 어떤 module에서든 재사용이 가능합니다.

CatsService의 인스턴스를 여러 모듈에서 사용하려고 한다고 생각해봅시다.

이렇게 하기 위해서는 먼저 CatsService 프로바이더를 module의 exports 배열에 추가하여 CatsService 프로바이더를 export 해야합니다.

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을 import하는 모든 모듈은 CatsService에 접근할 수 있고, 같은 CatsService 인스턴스를 공유합니다.

✔️Module re-exporting

Module들은 내부의 프로바이더를 export 할 수 있습니다. import한 모듈을 다시 export하는 것도 가능합니다.

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

위 예시처럼 CoreModuleCommonModule을 import하고 다시 export합니다. 그리고 이 모듈을 import하는 다른 모듈에서 CommonModule을 사용할 수 있게 됩니다.

✔️Dependency injection

module 클래스는 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) {}
}

하지만 circular dependency 때문에 module 클래스 자체는 프로바이더로 주입될 수 없습니다.

✔️Global modules

만약 곳곳에서 같은 모듈들의 세트를 import 해야하는 경우도 있습니다.

Angular의 프로바이더들은 전역 범위에 등록되어 한 번 정의되면 어디에서나 사용할 수 있지만, Nest는 프로바이더를 모듈 범위 내에 캡슐화합니다.

따라서 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 {}

@Globa() 데코레이터는 모듈을 전역 범위로 만듭니다. 전역 모듈은 일반적으로 루트 또는 코어 모듈에 의해 한 번만 등록되어야 합니다.

위 예시에서 CatsService 프로바이더는 어디에서나 사용할 수 있고, 이 서비스를 주입하려는 모듈들은 자산딜의 import 배열에 CatsModule을 가져올 필요가 없습니다.

모든 것들을 전역으로 만드는 것은 좋은 설계가 아닙니다. 전역 모듈은 필요한 보일러플레이트의 양을 줄이기 위해 사용되는 것이며,

일반적으로는 imports 배열을 통해 사용자에게 모듈의 api를 제공하는 것이 더 선호되는 방법입니다.

✔️Dynamic modules

Nest의 모듈 시스템은 dynamic modules라고 불리는 강력한 기능이 포함되어 있습니다.

이 기능을 사용하면 동적으로 프로바이더를 등록하고 구성할 수 이는 커스터마이즈 가능한 모듈을 쉽게 생성할 수 있습니다.

동적 모듈에 대한 자세한 설명은 이 문서를 참조하세요 Dynamic modules

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,
    };
  }
}

위 모듈은 기본적으로 @Module() 데코레이터의 메타데이터에서 Connection 프로바이더를 정의합니다.

하지만 추가로 forRoot() 메서드에 전달되는 entitiesoptions 객체에 의해 프로바이더들의 콜렉션을 노출합니다.

동적 모듈에서 반환된 속성들은 @Module() 데코레이터에서 정의된 기본 모듈 메타데이터를 확장하는 것이지 대체하는 것이 아니라는 점을 유의해야 합니다.

위와 같은 방법으로 정적으로 선언된 Connection 프로바이더와 동적으로 생성된 프로바이더가 어떻게 모듈에서 내보내지는지를 알 수 있습니다.

만약 동적 모듈을 전역 스코프에 등록하고 싶다면 global 프로퍼티를 true로 설정하면 됩니다.

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

DatabaseModule은 아래와 같은 방법으로 import 되고 구성할 수 있습니다.

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

동적 모듈을 re-export 하려면 forRoot() 메서드 실행을 생략하고 exports 배열에 추가하면 됩니다.

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 {}
profile
백엔드 개발자

0개의 댓글