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?