[공식문서] Modules

Haeseo Lee·2023년 4월 7일
0

NestJS 공식문서

목록 보기
3/4
post-thumbnail

공식문서 공부하기
https://docs.nestjs.com/modules

필요한 부분만 발췌해서 정리

Module 정의

Module은 그냥 @Module() 데코레이터가 붙은 클래스이다. @Module() 데코레이터는 Nest가 application 구조를 만드는 데 사용할 metadata를 제공해준다.

모든 Nest application은 한 개의 root module(AppModule)을 갖는다. root module은 Nest가 application graph(module, provider 간의 관계와 의존성을 엮어주는 데이터 구조)를 그리는 시작점이 된다.
대부분의 application은 한 개가 아닌 여러 module이 있고, 공통 기능으로 컴포넌트를 특정 모듈 내에 분류해놓는다.

@Module() 데코레이터가 가지는 property는 아래와 같다.

providers이 module에서 정의되고 인스턴스화될 provider들
controllers이 module에서 정의되고 인스턴스화될 controller들
imports이 module에서 사용할 provider를 export하는 module들
exports이 module에서 제공되고, 다른 module에서 이 module을 import함으로써 사용될 수 있게끔 해야할 provider들

현재 module의 직접 포함되지 않았거나 import한 다른 module로부터 export되지 않은 provider를 사용하는 것은 불가능하다. 따라서, 그런 경우엔 어떤 module로부터 export된 provider를 사용하거나 API를 사용해야 한다.

Feature Modules

기능적으로 연관된 controller나 provider들은 한 module로 묶어서 사용하는 것이 유지보수에도 좋고 SOLID 원칙에 입각해서 개발할 수 있다.

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

이렇게 따로 module을 만들었다면, 그 module을 root module(AppModule)에서 import 해줘야 한다.

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

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

Shared Modules

기본적으로 Nest에서 module은 singleton이다. 따라서 어떤 provider든지 여러 module 간에 같은 instance로써 공유될 수 있다. 모든 module은 자동적으로 shared module로 취급된다. 한 번 만들어지면 어떤 module에서든지 재사용될 수 있다.

어떤 provider를 다른 module과도 공유하고 싶다면, 우선 그 provider를 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 {}

그리고 나서 그 provider를 export하고 있는 module을 다른 module에서 import해주면 된다.

import { Module } from '@nestjs/common';
import { AnimalController } from './animal.controller';
import { AnimalService } from './animal.service';
import { CatsModule } from './cats.module';

@Module({
  imports: [CatsModule],
  controllers: [AnimalController],
  providers: [AnimalService],
})
export class CatsModule {}

Module re-exporting

module은 내부의 provider뿐만 아니라 import했던 module도 다시 export할 수 있다. 아래 예시에선 CoreModule이 CommonModule을 import하고 또 export하고 있다. 이렇게 하면 다른 module에서 CoreModule을 import하여 CommonModule까지 같이 이용할 수 있다.

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

Dependency injection

controller에서 service 의존성을 주입했던 것처럼, module 역시도 내부에서 provider의 의존성을 주입할 수 있다.

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {
  constructor(private catsService: CatsService) {}
}

다만 module 자체는 의존성으로써 주입될 순 없다. (순환 참조 때문)

Global modules

어떤 module을 모든 곳에서 사용해야 할 경우, 일일히 import해주는 건 귀찮을 수 있다.
모든 provider들이 global scope인 Angluar와 달리, Nest는 module scope이고, module을 import하지 않는 이상 다른 모듈에서 그 module의 provider를 사용할 순 없다.

만약 모든 곳에서 사용할 수 있는 provider들(e.g. helper, database connection, etc, ..)을 묶어서 제공하고 싶다면 @Global() 데코레이터를 module에 사용해주면 된다.

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() module은 반드시 한 번만 등록되어야 하고, 보통 root module이나 핵심 module에서 이를 수행한다. 위의 코드에선 CatsService가 CatsModule을 import할 필요 없이 어디서든지 사용 가능한 상태가 된다.

그러나 global module을 쓰는 건 별로 권장되는 방식은 아니다. provider를 module scope로 관리하는 것이 설계상 더 이롭다.

Dynamic modules

Dynamic module은 provider들을 동적으로(dynamically) 등록하고 구성할 수 있는 이점이 있다.
(자세한 건 여기)

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

위의 예시에서, DatabaseModule은 기본적으로 'Connection' provider를 제공하지만, forRoot()로 들어오는 entities나 options에 따라 추가적인 provider를 제공할 수 있게 된다.

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

+) dynamic module의 대표적인 활용예시는 typeorm 이다.

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: process.env.host,
      port: +process.env.port,
      username: process.env.username,
      password: process.env.password,
      database: process.env.database,
      entities: [User, History],
      synchronize: false, 
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
profile
잡생각 많은 인간

0개의 댓글