Dynamic modules

Dynamic modules

λͺ¨λ“ˆ μ±•ν„°λŠ” Nest λͺ¨λ“ˆμ˜ κΈ°λ³Έ 사항을 닀루고 동적 λͺ¨λ“ˆμ— λŒ€ν•œ κ°„λž΅ν•œ μ†Œκ°œλ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€. 이 μž₯은 λ‹€μ΄λ‚˜λ―Ή λͺ¨λ“ˆμ˜ 주제λ₯Ό ν™•μž₯ν•©λ‹ˆλ‹€. μ™„λ£Œλ˜λ©΄ μžμ‹ μ΄ 무엇인지, μ–Έμ œ μ–΄λ–»κ²Œ μ‚¬μš©ν•˜λŠ”μ§€ 잘 νŒŒμ•…ν•΄μ•Όν•©λ‹ˆλ‹€.

Introduction

μ„€λͺ…μ„œμ˜ κ°œμš” μ„Ήμ…˜μ—μžˆλŠ” λŒ€λΆ€λΆ„μ˜ μ‘μš© ν”„λ‘œκ·Έλž¨ μ½”λ“œ μ˜ˆμ œλŠ” 일반 λ˜λŠ” static λͺ¨λ“ˆμ„ μ‚¬μš©ν•©λ‹ˆλ‹€. λͺ¨λ“ˆμ€ ν”„λ‘œ 바이더 및 μ»¨νŠΈλ‘€λŸ¬μ™€ 같이 전체 μ‘μš© ν”„λ‘œκ·Έλž¨μ˜ λͺ¨λ“ˆ 식 λΆ€λΆ„μœΌλ‘œ κ΅¬μ„±λ˜λŠ” ꡬ성 μš”μ†Œ 그룹을 μ •μ˜ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ ꡬ성 μš”μ†Œμ— λŒ€ν•œ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ λ˜λŠ” λ²”μœ„λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λͺ¨λ“ˆμ— μ •μ˜ 된 κ³΅κΈ‰μžλŠ” 내보낼 ν•„μš”μ—†μ΄ λͺ¨λ“ˆμ˜ λ‹€λ₯Έ κ΅¬μ„±μ›μ—κ²Œ ν‘œμ‹œλ©λ‹ˆλ‹€. κ³΅κΈ‰μžκ°€ λͺ¨λ“ˆ μ™ΈλΆ€μ—μ„œ λ³Ό 수 μžˆμ–΄μ•Όν•˜λŠ” 경우, λ¨Όμ € 호슀트 λͺ¨λ“ˆμ—μ„œ _exported_λ₯Ό μ‚¬μš©ν•œ λ‹€μŒ μ†ŒλΉ„ λͺ¨λ“ˆλ‘œ _imported_ν•©λ‹ˆλ‹€.

μ΅μˆ™ν•œ 예λ₯Ό μ‚΄νŽ΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

λ¨Όμ €UsersServiceλ₯Ό μ œκ³΅ν•˜κ³  λ‚΄λ³΄λ‚΄λŠ” UsersModule을 μ •μ˜ν•©λ‹ˆλ‹€. UsersModule은 UsersService의 host λͺ¨λ“ˆμž…λ‹ˆλ‹€.

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';

@Module({
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

λ‹€μŒμœΌλ‘œ UserModule을 κ°€μ Έ μ™€μ„œ UserModule의 λ‚΄ 보낸 κ³΅κΈ‰μžλ₯Ό AuthModuleλ‚΄μ—μ„œ μ‚¬μš©ν•  수 μžˆλ„λ‘ν•˜λŠ” AuthModule을 μ •μ˜ν•©λ‹ˆλ‹€.

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [UsersModule],
  providers: [AuthService],
  exports: [AuthService],
})
export class AuthModule {}

μ΄λŸ¬ν•œ ꡬ문을 톡해 예λ₯Ό λ“€μ–΄ AuthModuleμ—μ„œ ν˜ΈμŠ€νŒ…λ˜λŠ” AuthService에 UserServiceλ₯Ό μ‚½μž… ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';

@Injectable()
export class AuthService {
  constructor(private readonly usersService: UsersService) {}
  /*
    Implementation that makes use of this.usersService
  */
}

이것을 정적(static) λͺ¨λ“ˆ λ°”μΈλ”©μ΄λΌκ³ ν•©λ‹ˆλ‹€. Nestκ°€ λͺ¨λ“ˆμ„ μ„œλ‘œ μ—°κ²°ν•˜λŠ” 데 ν•„μš”ν•œ λͺ¨λ“  μ •λ³΄λŠ” 이미 호슀트 및 μ†ŒλΉ„ λͺ¨λ“ˆμ—μ„œ μ„ μ–Έλ˜μ—ˆμŠ΅λ‹ˆλ‹€. 이 κ³Όμ •μ—μ„œ μΌμ–΄λ‚˜λŠ” 일을 ν’€μ–΄λ³΄κ² μŠ΅λ‹ˆλ‹€. NestλŠ”AuthModule λ‚΄μ—μ„œUsersServiceλ₯Ό λ‹€μŒκ³Ό 같이 μ‚¬μš© κ°€λŠ₯ν•˜κ²Œν•©λ‹ˆλ‹€.

  1. UsersModuleμžμ²΄κ°€ μ†ŒλΉ„ν•˜λŠ” λ‹€λ₯Έ λͺ¨λ“ˆμ„ μ „ 이적으둜 κ°€μ Έμ˜€κ³  쒅속성을 μ „ 이적으둜 ν•΄κ²°ν•˜λŠ” 것을 ν¬ν•¨ν•˜μ—¬ UsersModuleμΈμŠ€ν„΄μŠ€ν™” (Custom provider μ°Έμ‘°)

  2. AuthModule을 μΈμŠ€ν„΄μŠ€ν™”ν•˜κ³  UsersModule의 λ‚΄ 보낸 κ³΅κΈ‰μžλ₯Ό AuthModule의 ꡬ성 μš”μ†Œμ—μ„œ μ‚¬μš©ν•  수 μžˆλ„λ‘ν•©λ‹ˆλ‹€ ( AuthModuleμ—μ„œ μ„ μ–Έ 된 κ²ƒμ²˜λŸΌ).

  3. AuthService에 UsersService의 μΈμŠ€ν„΄μŠ€λ₯Ό μ£Όμž…ν•©λ‹ˆλ‹€.

Dynamic module use case

정적 λͺ¨λ“ˆ 바인딩을 μ‚¬μš©ν•˜λ©΄ μ†ŒλΉ„ λͺ¨λ“ˆμ΄ 호슀트 λͺ¨λ“ˆμ˜ κ³΅κΈ‰μžκ°€ κ΅¬μ„±λ˜λŠ” 방식에 영ν–₯을 쀄 μˆ˜μžˆλŠ” κΈ°νšŒκ°€ μ—†μŠ΅λ‹ˆλ‹€. 이것이 μ™œ μ€‘μš”ν•œκ°€? λ‹€λ₯Έ μ‚¬μš© μ‚¬λ‘€μ—μ„œ λ‹€λ₯΄κ²Œ μž‘λ™ν•΄μ•Όν•˜λŠ” λ²”μš© λͺ¨λ“ˆμ΄μžˆλŠ” 경우λ₯Ό κ³ λ €ν•˜μ‹­μ‹œμ˜€. μ΄λŠ” λ§Žμ€ μ‹œμŠ€ν…œμ—μ„œ "ν”ŒλŸ¬κ·ΈμΈ"κ°œλ…κ³Ό μœ μ‚¬ν•˜λ©° 일반 κΈ°λŠ₯은 μ†ŒλΉ„μžκ°€ μ‚¬μš©ν•˜κΈ° 전에 일뢀 ꡬ성이 ν•„μš”ν•©λ‹ˆλ‹€.

Nest의 쒋은 μ˜ˆλŠ” ꡬ성 λͺ¨λ“ˆμž…λ‹ˆλ‹€. λ§Žμ€ μ‘μš© ν”„λ‘œκ·Έλž¨μ€ ꡬ성 λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜μ—¬ ꡬ성 μ„ΈλΆ€ 정보λ₯Ό μ™ΈλΆ€ν™”ν•˜λŠ” 것이 μœ μš©ν•˜λ‹€λŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 개발자λ₯Όμœ„ν•œ 개발 λ°μ΄ν„°λ² μ΄μŠ€, μ€€λΉ„ / ν…ŒμŠ€νŠΈ ν™˜κ²½μ„μœ„ν•œ μ€€λΉ„ λ°μ΄ν„°λ² μ΄μŠ€ λ“± λ‹€μ–‘ν•œ 배포 ν™˜κ²½μ—μ„œ μ‘μš© ν”„λ‘œκ·Έλž¨ 섀정을 μ‰½κ²Œ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ꡬ성 맀개 λ³€μˆ˜ 관리λ₯Ό ꡬ성 λͺ¨λ“ˆ, μ‘μš© ν”„λ‘œκ·Έλž¨ μ†ŒμŠ€ μ½”λ“œμ— μœ„μž„ν•¨μœΌλ‘œμ¨ ꡬ성 맀개 λ³€μˆ˜μ™€ λ…λ¦½μ μœΌλ‘œ μœ μ§€λ©λ‹ˆλ‹€.

λ¬Έμ œλŠ” ꡬ성 λͺ¨λ“ˆ μžμ²΄κ°€ 일반 ( "ν”ŒλŸ¬κ·ΈμΈ"κ³Ό μœ μ‚¬ν•˜κΈ° λ•Œλ¬Έμ—) μ†ŒλΉ„ λͺ¨λ“ˆμ— 따라 μ‚¬μš©μž μ •μ˜ν•΄μ•Όν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œ dynamic modules이 μ‹œμž‘λ©λ‹ˆλ‹€. 동적 λͺ¨λ“ˆ κΈ°λŠ₯을 μ‚¬μš©ν•˜μ—¬ μ†ŒλΉ„ λͺ¨λ“ˆμ΄ APIλ₯Ό μ‚¬μš©ν•˜μ—¬ ꡬ성 λͺ¨λ“ˆμ„ κ°€μ Έμ˜¬ λ•Œ μ‚¬μš©μž μ •μ˜ν•˜λŠ” 방법을 μ œμ–΄ ν•  수 μžˆλ„λ‘ ꡬ성 λͺ¨λ“ˆμ„ λ™μ μœΌλ‘œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μ‹œ 말해, 동적 λͺ¨λ“ˆμ€ ν•œ λͺ¨λ“ˆμ„ λ‹€λ₯Έ λͺ¨λ“ˆλ‘œ κ°€μ Έμ˜€κ³ , μ§€κΈˆκΉŒμ§€ λ³Έ 정적 바인딩을 μ‚¬μš©ν•˜λŠ” 것과 λ°˜λŒ€λ‘œ λͺ¨λ“ˆμ„ κ°€μ Έμ˜¬ λ•Œ ν•΄λ‹Ή λͺ¨λ“ˆμ˜ 속성과 λ™μž‘μ„ μ‚¬μš©μž μ •μ˜ν•˜κΈ°μœ„ν•œ APIλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

Config module example

이 μ„Ήμ…˜μ—μ„œλŠ” configuration chapter의 예제 μ½”λ“œ κΈ°λ³Έ 버전을 μ‚¬μš©ν•©λ‹ˆλ‹€. 이 μž₯의 λμ—μ„œ μ™„μ„± 된 버전은 예제둜 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

우리의 μš”κ΅¬ 사항은 ConfigModule이 options 객체λ₯Ό λ°›μ•„ λ“€μ—¬ μ»€μŠ€ν„°λ§ˆμ΄μ¦ˆ ν•˜λ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. μ§€μ›ν•˜λ €λŠ” κΈ°λŠ₯은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. κΈ°λ³Έ μƒ˜ν”Œμ€ .env 파일의 μœ„μΉ˜λ₯Ό ν”„λ‘œμ νŠΈ 루트 폴더에 ν•˜λ“œ μ½”λ”©ν•©λ‹ˆλ‹€. μ„ νƒν•œ ν΄λ”μ—μ„œ .env νŒŒμΌμ„ 관리 ν•  수 μžˆλ„λ‘ ꡬ성 κ°€λŠ₯ν•˜κ²Œ λ§Œλ“€κ³  μ‹Άλ‹€κ³  κ°€μ • ν•΄ λ΄…μ‹œλ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μ–‘ν•œ .env νŒŒμΌμ„ configλΌλŠ” ν”„λ‘œμ νŠΈ 루트 μ•„λž˜μ˜ 폴더 (예: src의 ν˜•μ œ 폴더)에 μ €μž₯ν•œλ‹€κ³  κ°€μ • ν•΄λ³΄μ‹­μ‹œμ˜€. λ‹€λ₯Έ ν”„λ‘œμ νŠΈμ—μ„œ ConfigModule을 μ‚¬μš©ν•  λ•Œ λ‹€λ₯Έ 폴더λ₯Ό 선택할 수 μžˆμŠ΅λ‹ˆλ‹€.

동적 λͺ¨λ“ˆμ€ κ°€μ Έ μ˜€λŠ” λͺ¨λ“ˆμ— 맀개 λ³€μˆ˜λ₯Ό μ „λ‹¬ν•˜μ—¬ ν•΄λ‹Ή λ™μž‘μ„ λ³€κ²½ν•  μˆ˜μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. 이것이 μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ λ΄…μ‹œλ‹€. μ†ŒλΉ„ λͺ¨λ“ˆμ˜ κ΄€μ μ—μ„œ 이것이 μ–΄λ–»κ²Œ 보일지에 λŒ€ν•œ μ΅œμ’… λͺ©ν‘œμ—μ„œ μ‹œμž‘ν•˜μ—¬ 거꾸둜 μž‘μ—…ν•˜λ©΄ λ„μ›€μ΄λ©λ‹ˆλ‹€. λ¨Όμ €, ConfigModule을 κ°€μ Έ μ˜€λŠ” _statically_ κ°€μ Έ 였기 (즉, κ°€μ Έμ˜¨ λͺ¨λ“ˆμ˜ λ™μž‘μ— 영ν–₯을 쀄 μˆ˜μžˆλŠ” μ ‘κ·Ό 방법)λ₯Ό λΉ λ₯΄κ²Œ κ²€ν† ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. @Module()λ°μ½”λ ˆμ΄ν„°μ˜ imports λ°°μ—΄μ—μ£Όμ˜λ₯Ό κΈ°μšΈμ΄μ‹­μ‹œμ˜€ :

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';

@Module({
  imports: [ConfigModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

ꡬ성 객체λ₯Ό μ „λ‹¬ν•˜λŠ” _dynamic module_ importκ°€ μ–΄λ–»κ²Œ 보일지 생각해 λ΄…μ‹œλ‹€. 이 두 예제의 imports λ°°μ—΄μ˜ 차이점을 λΉ„κ΅ν•˜μ‹­μ‹œμ˜€.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';

@Module({
  imports: [ConfigModule.register({ folder: './config' })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

μœ„μ˜ 동적 μ˜ˆμ—μ„œ 무슨 일이 μΌμ–΄λ‚˜λŠ”μ§€ λ΄…μ‹œλ‹€. μ›€μ§μ΄λŠ” 뢀뢄은 λ¬΄μ—‡μž…λ‹ˆκΉŒ?

  1. ConfigModule은 일반 ν΄λž˜μŠ€μ΄λ―€λ‘œ register()λΌλŠ” 정적 λ©”μ†Œλ“œκ°€ μžˆμ–΄μ•Ό 함을 μœ μΆ”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬λŠ” 클래슀의 μΈμŠ€ν„΄μŠ€κ°€ μ•„λ‹ˆλΌ ConfigModule ν΄λž˜μŠ€μ—μ„œ ν˜ΈμΆœν•˜κΈ° λ•Œλ¬Έμ— μ •μ μž„μ„ μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. μ°Έκ³ : κ³§ λ§Œλ“€ 이 λ©”μ†Œλ“œλŠ” μž„μ˜μ˜ 이름을 κ°€μ§ˆ 수 μžˆμ§€λ§Œ, κ΄€λ‘€ 적으둜 forRoot()λ˜λŠ” register()라고 λΆ€λ¦…λ‹ˆλ‹€.

  2. register() λ©”μ†Œλ“œλŠ” μš°λ¦¬μ— μ˜ν•΄ μ •μ˜λ˜λ―€λ‘œ μ›ν•˜λŠ” μž…λ ₯ 인수λ₯Ό λ°›μ•„ 듀일 수 μžˆμŠ΅λ‹ˆλ‹€. 이 경우 μ μ ˆν•œ 속성을 κ°€μ§„ κ°„λ‹¨ν•œ options 객체λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. 이것이 일반적인 κ²½μš°μž…λ‹ˆλ‹€.

  3. register() λ©”μ†Œλ“œλŠ” 리턴 값이 μ΅μˆ™ν•œ importsλ¦¬μŠ€νŠΈμ— λ‚˜νƒ€λ‚˜κΈ° λ•Œλ¬Έμ— moduleκ³Ό 같은 것을 λ°˜ν™˜ν•΄μ•Όν•œλ‹€κ³  μœ μΆ” ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ—λŠ” λͺ¨λ“ˆλ¦¬μŠ€νŠΈκ°€ ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

사싀, register() λ©”μ†Œλ“œκ°€ 리턴 ν•  것은 DynamicModuleμž…λ‹ˆλ‹€. 동적 λͺ¨λ“ˆμ€ 정적 λͺ¨λ“ˆκ³Ό λ™μΌν•œ μ •ν™•ν•œ 속성과 moduleμ΄λΌλŠ” μΆ”κ°€ 속성을 κ°€μ§„ λŸ°νƒ€μž„μ— μƒμ„±λœ λͺ¨λ“ˆμ— μ§€λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ°μ½”λ ˆμ΄ν„°μ— μ „λ‹¬λœ λͺ¨λ“ˆ μ˜΅μ…˜μ—μ£Όμ˜λ₯Ό κΈ°μšΈμ΄λ©΄μ„œ μƒ˜ν”Œ 정적 λͺ¨λ“ˆ 선언을 λΉ λ₯΄κ²Œ κ²€ν† ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

@Module({
  imports: [DogsService],
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})

λ‹€μ΄λ‚˜λ―Ή λͺ¨λ“ˆμ€ μ •ν™•νžˆ 같은 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ°€μ§„ 객체와 moduleμ΄λΌλŠ” μΆ”κ°€ 속성을 λ°˜ν™˜ν•΄μ•Όν•©λ‹ˆλ‹€. module 속성은 λͺ¨λ“ˆ μ΄λ¦„μœΌλ‘œ μ‚¬μš©λ˜λ©° μ•„λž˜ μ˜ˆμ œμ™€ 같이 λͺ¨λ“ˆμ˜ 클래슀 이름과 λ™μΌν•΄μ•Όν•©λ‹ˆλ‹€.

info 힌트 동적 λͺ¨λ“ˆμ˜ 경우 λͺ¨λ“ˆ μ˜΅μ…˜ 객체의 λͺ¨λ“  속성은 선택 μ‚¬ν•­μž…λ‹ˆλ‹€ exceptmodule.

정적 register()λ©”μ†Œλ“œλŠ” μ–΄λ–»μŠ΅λ‹ˆκΉŒ? 이제 κ·Έ 역할은 DynamicModule μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ°€μ§„ 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” κ²ƒμž„μ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬κ°€ 그것을 호좜 ν•  λ•Œ, μš°λ¦¬λŠ” 정적 ν΄λž˜μŠ€μ—μ„œ λͺ¨λ“ˆ 클래슀 이름을 λ‚˜μ—΄ν•¨μœΌλ‘œμ¨ κ·Έλ ‡κ²Œν•˜λŠ” 것과 μœ μ‚¬ν•œ λ°©μ‹μœΌλ‘œ λͺ¨λ“ˆμ„ imports λͺ©λ‘μ— 효과적으둜 μ œκ³΅ν•©λ‹ˆλ‹€. λ‹€μ‹œ 말해, 동적 λͺ¨λ“ˆ APIλŠ” λ‹¨μˆœνžˆ λͺ¨λ“ˆμ„ λ°˜ν™˜ν•˜μ§€λ§Œ @Modules λ°μ½”λ ˆμ΄ν„°μ˜ 속성을 μˆ˜μ •ν•˜λŠ” λŒ€μ‹  ν”„λ‘œκ·Έλž˜λ° λ°©μ‹μœΌλ‘œ μ§€μ •ν•©λ‹ˆλ‹€.

사진을 μ™„μ„±ν•˜λŠ” 데 λ„μ›€μ΄λ˜λŠ” λͺ‡ κ°€μ§€ μ„ΈλΆ€ 정보가 μ—¬μ „νžˆ μžˆμŠ΅λ‹ˆλ‹€.

  1. 이제 @Module()의 λ°μ½”λ ˆμ΄ν„°μ˜ imports 속성은 λͺ¨λ“ˆ 클래슀 이름 (예: imports: [UsersModule])뿐만 μ•„λ‹ˆλΌ 동적 ν•¨μˆ˜ returning ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€ λͺ¨λ“ˆ (예: imports: [ConfigModule.register(...)]).

  2. 동적 λͺ¨λ“ˆ μžμ²΄λŠ” λ‹€λ₯Έ λͺ¨λ“ˆμ„ κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€. 이 μ˜ˆμ œμ—μ„œλŠ” κ·Έλ ‡κ²Œν•˜μ§€ μ•Šμ§€λ§Œ 동적 λͺ¨λ“ˆμ΄ λ‹€λ₯Έ λͺ¨λ“ˆμ˜ κ³΅κΈ‰μžμ— μ˜μ‘΄ν•˜λŠ” 경우 선택적 imports속성을 μ‚¬μš©ν•˜μ—¬ κ°€μ Έμ˜΅λ‹ˆλ‹€. λ‹€μ‹œ, 이것은 @Module()λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜μ—¬ 정적 λͺ¨λ“ˆμ— λŒ€ν•œ 메타 데이터λ₯Ό μ„ μ–Έν•˜λŠ” 방식과 μ •ν™•νžˆ μœ μ‚¬ν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ 이해λ₯Ό λ°”νƒ•μœΌλ‘œ 이제 동적 ConfigModuleμ„ μ–Έμ˜ λͺ¨μŠ΅μ„ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 그것에 균열을 보자.

import { DynamicModule, Module } from '@nestjs/common';
import { ConfigService } from './config.service';

@Module({})
export class ConfigModule {
  static register(): DynamicModule {
    return {
      module: ConfigModule,
      providers: [ConfigService],
      exports: [ConfigService],
    };
  }
}

이제 쑰각듀이 μ–΄λ–»κ²Œ λ¬Άμ—¬ μžˆλŠ”μ§€ λΆ„λͺ…ν•΄μ•Όν•©λ‹ˆλ‹€. ConfigModule.register(...)λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ§€κΈˆκΉŒμ§€ @Module() λ°μ½”λ ˆμ΄ν„°λ₯Ό 톡해 메타 λ°μ΄ν„°λ‘œ 제곡 ν•œ 것과 본질적으둜 λ™μΌν•œ 속성을 κ°€μ§„ DynamicModule 객체λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

info 힌트 @nestjs/commonμ—μ„œ DynamicModule을 κ°€μ Έμ˜΅λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ 동적 λͺ¨λ“ˆμ€ κ·Έλ‹€μ§€ ν₯λ―Έλ‘­μ§€ μ•Šμ§€λ§Œ, μš°λ¦¬κ°€ μ›ν•˜λŠ”λŒ€λ‘œ κ΅¬μ„±ν•˜λŠ” κΈ°λŠ₯을 λ„μž…ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. λ‹€μŒμ— κ·Έ λ‚΄μš©μ„ λ‹€λ£¨κ² μŠ΅λ‹ˆλ‹€.

Module configuration

ConfigModule의 λ™μž‘μ„ μ»€μŠ€ν„°λ§ˆμ΄μ¦ˆν•˜κΈ°μœ„ν•œ ν™•μ‹€ν•œ 해결책은 μœ„μ—μ„œ μƒκ°ν•œ κ²ƒμ²˜λŸΌ 정적 register()λ©”μ†Œλ“œμ—μ„œ options 객체λ₯Ό μ „λ‹¬ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. μ†ŒλΉ„ λͺ¨λ“ˆμ˜ imports 속성을 λ‹€μ‹œ ν•œλ²ˆ μ‚΄νŽ΄ 보자.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';

@Module({
  imports: [ConfigModule.register({ folder: './config' })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

그것은 'μ˜΅μ…˜'객체λ₯Ό 동적 λͺ¨λ“ˆμ— μ „λ‹¬ν•˜λŠ” 것을 잘 μ²˜λ¦¬ν•©λ‹ˆλ‹€. 그러면ConfigModuleμ—μ„œoptions 객체λ₯Ό μ–΄λ–»κ²Œ μ‚¬μš©ν•©λ‹ˆκΉŒ? μž μ‹œ 생각해 λ΄…μ‹œλ‹€. μš°λ¦¬λŠ”ConfigModule이 기본적으둜 λ‹€λ₯Έ μ œκ³΅μžκ°€ μ‚¬μš©ν•˜κΈ° μœ„ν•΄ 주사 κ°€λŠ₯ν•œ μ„œλΉ„μŠ€ 인ConfigServiceλ₯Ό μ œκ³΅ν•˜κ³  λ‚΄λ³΄λ‚΄λŠ” ν˜ΈμŠ€νŠΈλΌλŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ λ™μž‘μ„ μ‚¬μš©μž μ •μ˜ν•˜κΈ° μœ„ν•΄options 객체λ₯Ό μ½μ–΄μ•Όν•˜λŠ” 것은 'ConfigService'μž…λ‹ˆλ‹€. register ()λ©”μ†Œλ“œμ—μ„œConfigService둜options 'λ₯Ό μ–»λŠ” 방법을 μ•Œκ³  μžˆλ‹€κ³  κ°€μ •ν•˜μž. 이 가정을 톡해 μ„œλΉ„μŠ€μ˜ 일뢀λ₯Ό λ³€κ²½ν•˜μ—¬options` 객체의 속성에 따라 λ™μž‘μ„ μ‚¬μš©μž μ§€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ( μ°Έκ³  : 당뢄간은 μ‹€μ œλ‘œ μ „λ‹¬ν•˜λŠ” 방법을 κ²°μ • ν–ˆμœΌλ―€λ‘œ 'μ˜΅μ…˜'을 ν•˜λ“œ μ½”λ”© λ§Œν•˜λ©΄λ©λ‹ˆλ‹€. μž μ‹œ 후에 μˆ˜μ •ν•˜κ² μŠ΅λ‹ˆλ‹€.)

import { Injectable } from '@nestjs/common';
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import { EnvConfig } from './interfaces';

@Injectable()
export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor() {
    const options = { folder: './config' };

    const filePath = `${process.env.NODE_ENV || 'development'}.env`;
    const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
    this.envConfig = dotenv.parse(fs.readFileSync(envFile));
  }

  get(key: string): string {
    return this.envConfig[key];
  }
}

이제 ConfigServiceλŠ” optionsμ—μ„œ μ§€μ •ν•œ ν΄λ”μ—μ„œ .env νŒŒμΌμ„ μ°ΎλŠ” 방법을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€.

우리의 λ‚˜λ¨Έμ§€ μž‘μ—…μ€ register()λ‹¨κ³„μ˜ options 객체λ₯Ό ConfigService에 μ‚½μž…ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. λ¬Όλ‘  _dependency injection_을 μ‚¬μš©ν•˜μ—¬ μˆ˜ν–‰ν•©λ‹ˆλ‹€. 이것이 핡심 μ‚¬ν•­μ΄λ―€λ‘œ μ΄ν•΄ν•΄μ•Όν•©λ‹ˆλ‹€. 우리의 ConfigModule은 ConfigServiceλ₯Ό μ œκ³΅ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. ConfigServiceλŠ” λŸ°νƒ€μž„μ—λ§Œ μ œκ³΅λ˜λŠ” options 객체에 μ˜μ‘΄ν•©λ‹ˆλ‹€. λ”°λΌμ„œ λŸ°νƒ€μž„μ— λ¨Όμ € options 객체λ₯Ό Nest IoC μ»¨ν…Œμ΄λ„ˆμ— 바인딩 ν•œ λ‹€μŒ Nestκ°€ ConfigService에 μ‚½μž…ν•΄μ•Όν•©λ‹ˆλ‹€. 맞좀 제곡자 μž₯μ—μ„œ μ œκ³΅μžλŠ” μ„œλΉ„μŠ€λΏλ§Œ μ•„λ‹ˆλΌ λͺ¨λ“  κ°€μΉ˜λ₯Ό 포함 ν•  수 μžˆλ‹€λŠ” 점을 κΈ°μ–΅ν•˜μ‹­μ‹œμ˜€. μ˜μ‘΄μ„± μ£Όμž…μ„ μ‚¬μš©ν•˜μ—¬ κ°„λ‹¨ν•œ options 객체λ₯Ό μ²˜λ¦¬ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

μ˜΅μ…˜ 객체λ₯Ό IoC μ»¨ν…Œμ΄λ„ˆμ— λ°”μΈλ”©ν•˜λŠ” 방법을 λ¨Όμ € μ‚΄νŽ΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. 정적 register()λ©”μ†Œλ“œμ—μ„œ 이λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. μš°λ¦¬λŠ” λ™μ μœΌλ‘œ λͺ¨λ“ˆμ„ κ΅¬μ„±ν•˜κ³  있으며 λͺ¨λ“ˆμ˜ 속성 쀑 ν•˜λ‚˜λŠ” κ³΅κΈ‰μž λͺ©λ‘μž…λ‹ˆλ‹€. λ”°λΌμ„œ μš°λ¦¬κ°€ν•΄μ•Ό ν•  일은 μ˜΅μ…˜ 객체λ₯Ό κ³΅κΈ‰μžλ‘œ μ •μ˜ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 이λ₯Ό 톡해 ConfigService에 μ£Όμž… ν•  수있게 되며 λ‹€μŒ λ‹¨κ³„μ—μ„œ ν™œμš©ν•  κ²ƒμž…λ‹ˆλ‹€. μ•„λž˜ μ½”λ“œμ—μ„œproviders λ°°μ—΄μ—μ£Όμ˜ν•˜μ‹­μ‹œμ˜€ :

import { DynamicModule, Module } from '@nestjs/common';

import { ConfigService } from './config.service';

@Module({})
export class ConfigModule {
  static register(options): DynamicModule {
    return {
      module: ConfigModule,
      providers: [
        {
          provide: 'CONFIG_OPTIONS',
          useValue: options,
        },
        ConfigService,
      ],
      exports: [ConfigService],
    };
  }
}

이제 CONFIG_OPTIONS제곡자λ₯Ό ConfigService에 μ‚½μž…ν•˜μ—¬ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ™„λ£Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λΉ„ 클래슀 토큰을 μ‚¬μš©ν•˜μ—¬ κ³΅κΈ‰μžλ₯Ό μ •μ˜ ν•  λ•ŒλŠ” 여기에 μ„€λͺ…λœλŒ€λ‘œ @Inject() λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•΄μ•Όν•©λ‹ˆλ‹€.

import { Injectable, Inject } from '@nestjs/common';

import * as dotenv from 'dotenv';
import * as fs from 'fs';

import { EnvConfig } from './interfaces';

@Injectable()
export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor(@Inject('CONFIG_OPTIONS') private options) {
    const filePath = `${process.env.NODE_ENV || 'development'}.env`;
    const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
    this.envConfig = dotenv.parse(fs.readFileSync(envFile));
  }

  get(key: string): string {
    return this.envConfig[key];
  }
}

λ§ˆμ§€λ§‰ μ°Έκ³  사항: λ‹¨μˆœν™”λ₯Ό μœ„ν•΄ μœ„μ˜ λ¬Έμžμ—΄ 기반 μ£Όμž… 토큰 (CONFIG_OPTIONS)을 μ‚¬μš©ν–ˆμ§€λ§Œ λͺ¨λ²” μ‚¬λ‘€λŠ”μ΄λ₯Ό λ³„λ„μ˜ νŒŒμΌμ—μ„œ μƒμˆ˜ (λ˜λŠ” 심볼)둜 μ •μ˜ν•˜κ³  ν•΄λ‹Ή νŒŒμΌμ„ κ°€μ Έ μ˜€λŠ” κ²ƒμž…λ‹ˆλ‹€. 예λ₯Ό λ“€λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';

Example

이 μž₯의 μ½”λ“œμ— λŒ€ν•œ 전체 μ˜ˆλŠ” μ—¬κΈ°μ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

Last updated

Was this helpful?