๐Ÿ“‘ ๊ณต์‹ ๋ฌธ์„œ ๋ฒˆ์—ญ/Nest.js

[Nest.js] Fundamentals | Lifecycle events, Discovery service

dev_writer 2025. 7. 29. 09:13

Lifecycle events

Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์„ฑ ์š”์†Œ๋Š” Nest๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. Nest๋Š” ์ฃผ์š” ์ƒ๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๊ฐ€์‹œ์„ฑ๊ณผ, ํ•ด๋‹น ์‹œ์ ์— ๋ชจ๋“ˆ, ํ”„๋กœ๋ฐ”์ด๋” ๋˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋“ฑ๋ก๋œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒ๋ช… ์ฃผ๊ธฐ ํ›„ํฌ (lifecycle hook)๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ƒ๋ช… ์ฃผ๊ธฐ ์ˆœ์„œ

๋‹ค์Œ ๋‹ค์ด์–ด๊ทธ๋žจ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ถ€ํŠธ์ŠคํŠธ๋žฉ๋œ ์‹œ์ ๋ถ€ํ„ฐ Node ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ฃผ์š” ์ƒ๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ ์ˆœ์„œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ „์ฒด ์ƒ๋ช… ์ฃผ๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ธ ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: ์ดˆ๊ธฐํ™” (initializing), ์‹คํ–‰ (running), ์ข…๋ฃŒ (terminating). ์ด ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ชจ๋“ˆ๊ณผ ์„œ๋น„์Šค์˜ ์ ์ ˆํ•œ ์ดˆ๊ธฐํ™”, ํ™œ์„ฑ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ, ์ข…๋ฃŒ ์‹ ํ˜ธ ์ˆ˜์‹  ์‹œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ •์ƒ์ ์ธ ์ข…๋ฃŒ๋ฅผ ๊ณ„ํšํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ

์ƒ๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ๋ฐ ์ข…๋ฃŒ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. Nest๋Š” ๊ฐ ์ƒ๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ ์‹œ์ ์— ๋Œ€ํ•ด ๋ชจ๋“ˆ, ํ”„๋กœ๋ฐ”์ด๋”, ์ปจํŠธ๋กค๋Ÿฌ์— ๋“ฑ๋ก๋œ ์ƒ๋ช… ์ฃผ๊ธฐ ํ›„ํฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค (๋‹จ, ์ข…๋ฃŒ ํ›„ํฌ๋Š” ์•„๋ž˜ ์„ค๋ช…์ฒ˜๋Ÿผ ๋จผ์ € ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค). ์œ„์˜ ๋‹ค์ด์–ด๊ทธ๋žจ์—์„œ ๋ณด๋“ฏ, Nest๋Š” ์—ฐ๊ฒฐ์„ ์ˆ˜์‹ ํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ค‘๋‹จํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€ ๋ฉ”์„œ๋“œ๋„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

 

๋‹ค์Œ ํ‘œ์—์„œ onModuleInit๊ณผ onApplicationBootstrap์€ app.init() ๋˜๋Š” app.listen()์„ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ๋งŒ ํŠธ๋ฆฌ๊ฑฐ ๋ฉ๋‹ˆ๋‹ค.

 

๋˜ํ•œ onModuleDestroy, beforeApplicationShutdown, onApplicationShutdown์€ app.close()๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜, ํ”„๋กœ์„ธ์Šค๊ฐ€ SIGTERM๊ณผ ๊ฐ™์€ ์‹œ์Šคํ…œ ์ข…๋ฃŒ ์‹ ํ˜ธ๋ฅผ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ, ๊ทธ๋ฆฌ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์‹œ enableShutdownHooks๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ˜ธ์ถœํ•œ ๊ฒฝ์šฐ์—๋งŒ ํŠธ๋ฆฌ๊ฑฐ ๋ฉ๋‹ˆ๋‹ค (์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ "Application shutdown" ํ•ญ๋ชฉ ์ฐธ์กฐ).

์ƒ๋ช… ์ฃผ๊ธฐ ํ›„ํฌ ๋ฉ”์„œ๋“œ ํ›„ํฌ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ์ƒ๋ช… ์ฃผ๊ธฐ ์ด๋ฒคํŠธ
onModuleInit() ํ•ด๋‹น ๋ชจ๋“ˆ์˜ ์˜์กด์„ฑ์ด ๋ชจ๋‘ ํ•ด๊ฒฐ๋œ ํ›„ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
onApplicationBootstrap() ๋ชจ๋“  ๋ชจ๋“ˆ์ด ์ดˆ๊ธฐํ™”๋œ ํ›„, ์—ฐ๊ฒฐ ์ˆ˜์‹ ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
onModuleDestroy() ์ข…๋ฃŒ ์‹ ํ˜ธ (SIGTERM ๋“ฑ)๋ฅผ ์ˆ˜์‹ ํ•œ ํ›„ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
beforeApplicationShutdown() ๋ชจ๋“  onModuleDestroy() ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ํ˜ธ์ถœ๋˜๋ฉฐ (Promise๊ฐ€ resolve ๋˜๋Š” reject๋œ ์ƒํƒœ), ์ด ๋‹จ๊ณ„๊ฐ€ ๋๋‚˜๋ฉด ๊ธฐ์กด ์—ฐ๊ฒฐ์ด ๋ชจ๋‘ ๋‹ซํž™๋‹ˆ๋‹ค (app.close() ํ˜ธ์ถœ๋จ).
onApplicationShutdown() ์—ฐ๊ฒฐ์ด ๋ชจ๋‘ ๋‹ซํžŒ ํ›„ (app.close()๊ฐ€ resolve๋œ ํ›„) ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

 

* ์œ„ ์ด๋ฒคํŠธ๋“ค์˜ ๊ฒฝ์šฐ, app.close()๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, SIGTERM ๊ฐ™์€ ์‹œ์Šคํ…œ ์ข…๋ฃŒ ์‹ ํ˜ธ์— ๋ฐ˜์‘ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ๋ณ„๋„๋กœ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ "Application shutdown" ์„น์…˜์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์ฃผ์˜
์œ„์— ๋‚˜์—ด๋œ ์ƒ๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋“ค์€ request-scoped ํด๋ž˜์Šค์—๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. request scope ํด๋ž˜์Šค๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ๋ช… ์ฃผ๊ธฐ์™€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉฐ, ์ˆ˜๋ช…์ด ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋“ค์€ ๊ฐ ์š”์ฒญ๋งˆ๋‹ค ๋ณ„๋„๋กœ ์ƒ์„ฑ๋˜๋ฉฐ, ์‘๋‹ต์ด ์™„๋ฃŒ๋˜๋ฉด ์ž๋™์œผ๋กœ garbage-collected ๋ฉ๋‹ˆ๋‹ค.
ํžŒํŠธ
onModuleInit()๊ณผ onApplicationBootstrap()์˜ ์‹คํ–‰ ์ˆœ์„œ๋Š” ๋ชจ๋“ˆ์˜ import ์ˆœ์„œ์— ์ง์ ‘์ ์œผ๋กœ ์˜์กดํ•˜๋ฉฐ, ์ด์ „ ํ›„ํฌ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ

๊ฐ ์ƒ๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค. ์ด ์ธํ„ฐํŽ˜์ด์Šค๋“ค์€ TypeScript ์ปดํŒŒ์ผ ํ›„์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์ˆ ์ ์œผ๋กœ๋Š” ์„ ํƒ ์‚ฌํ•ญ์ด์ง€๋งŒ, ๊ฐ•ํ•œ ํƒ€์ž… ์ง€์›๊ณผ ์—๋””ํ„ฐ ๋„๊ตฌ์˜ ๋„์›€์„ ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ด€๋ก€์ž…๋‹ˆ๋‹ค. ์ƒ๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋ฅผ ๋“ฑ๋กํ•˜๋ ค๋ฉด, ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ํด๋ž˜์Šค (์˜ˆ: ์ปจํŠธ๋กค๋Ÿฌ, ํ”„๋กœ๋ฐ”์ด๋”, ๋ชจ๋“ˆ)์—์„œ ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” ์‹œ ํ˜ธ์ถœ๋  ๋ฉ”์„œ๋“œ๋ฅผ ๋“ฑ๋กํ•˜๋ ค๋ฉด, OnModuleInit ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  onModuleInit() ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค:

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

@Injectable()
export class UsersService implements OnModuleInit {
  onModuleInit() {
    console.log(`The module has been initialized.`);
  }
}

๋น„๋™๊ธฐ ์ดˆ๊ธฐํ™”

OnModuleInit๊ณผ OnApplicationBootstrap ํ›„ํฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ดˆ๊ธฐํ™” ๊ณผ์ •์„ ์ง€์—ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ค๋‹ˆ๋‹ค. ์ฆ‰, ํ•ด๋‹น ๋ฉ”์„œ๋“œ์—์„œ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ async๋กœ ์„ ์–ธํ•˜๊ณ  ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ await๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

async onModuleInit(): Promise<void> {
  await this.fetch();
}

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ

onModuleDestroy(), beforeApplicationShutdown(), onApplicationShutdown() ํ›„ํฌ๋Š” ์ข…๋ฃŒ ๋‹จ๊ณ„์—์„œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด๋“ค์€ app.close()๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ๋‚˜, SIGTERM๊ณผ ๊ฐ™์€ ์‹œ์Šคํ…œ ์ข…๋ฃŒ ์‹ ํ˜ธ๋ฅผ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ(์˜ตํŠธ์ธ ํ•œ ๊ฒฝ์šฐ)์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ Kubernetes์—์„œ ์ปจํ…Œ์ด๋„ˆ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜, Heroku์—์„œ dyno ์ข…๋ฃŒ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์šฉ๋„๋กœ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

์ข…๋ฃŒ ํ›„ํฌ ๋ฆฌ์Šค๋„ˆ๋Š” ์‹œ์Šคํ…œ ์ž์›์„ ์†Œ๋น„ํ•˜๋ฏ€๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ข…๋ฃŒ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, enableShutdownHooks()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฆฌ์Šค๋„ˆ๋ฅผ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // ์ข…๋ฃŒ ํ›„ํฌ ๋ฆฌ์Šค๋„ˆ ํ™œ์„ฑํ™”
  app.enableShutdownHooks();

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
์ฃผ์˜
ํ”Œ๋žซํผ์˜ ๋ณธ์งˆ์ ์ธ ์ œํ•œ์œผ๋กœ ์ธํ•ด, NestJS๋Š” Windows์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ ํ›„ํฌ์— ๋Œ€ํ•ด ์ œํ•œ์ ์ธ ์ง€์›๋งŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. SIGINT, SIGBREAK, ๊ทธ๋ฆฌ๊ณ  ์–ด๋А ์ •๋„๋Š” SIGHUP์ด ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค - ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฐธ๊ณ ํ•˜์„ธ์š”, ํ•˜์ง€๋งŒ SIGTERM์€ Windows์—์„œ ์ ˆ๋Œ€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ž‘์—… ๊ด€๋ฆฌ์ž์—์„œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•˜๋Š” ๊ฒƒ์ด ๋ฌด์กฐ๊ฑด์ ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ฆ‰, "์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ด๋ฅผ ๊ฐ์ง€ํ•˜๊ฑฐ๋‚˜ ๋ฐฉ์ง€ํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค"๋Š” ๋œป์ž…๋‹ˆ๋‹ค. Windows์—์„œ SIGINT, SIGBREAK ๋“ฑ์ด ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ๋Š” libuv ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. ๋˜ํ•œ, Node.js์˜ Process Signal Events ๋ฌธ์„œ๋„ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ •๋ณด
enableShutdownHooks๋Š” ๋ฆฌ์Šค๋„ˆ๋ฅผ ์‹œ์ž‘ํ•˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์†Œ๋น„ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ Node ํ”„๋กœ์„ธ์Šค์—์„œ ์—ฌ๋Ÿฌ Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ (์˜ˆ: Jest๋กœ ๋ณ‘๋ ฌ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ), Node๊ฐ€ ๋ฆฌ์Šค๋„ˆ ์ˆ˜๊ฐ€ ๊ณผ๋„ํ•˜๋‹ค๊ณ  ๊ฒฝ๊ณ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ enableShutdownHooks๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ Node ํ”„๋กœ์„ธ์Šค์—์„œ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ด ์กฐ๊ฑด์„ ๋ฐ˜๋“œ์‹œ ์ธ์ง€ํ•˜์„ธ์š”.

 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ ์‹ ํ˜ธ๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด, ๋“ฑ๋ก๋œ onModuleDestroy(), beforeApplicationShutdown(), onApplicationShutdown() ๋ฉ”์„œ๋“œ๋ฅผ ํ•ด๋‹น ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœํ•˜๋ฉฐ, ์ด๋•Œ ํ•ด๋‹น ์‹ ํ˜ธ๋ฅผ ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋“ฑ๋ก๋œ ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ ํ˜ธ์ถœ์„ await ํ•˜๋Š” ๊ฒฝ์šฐ (Promise๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฒฝ์šฐ), Nest๋Š” ํ•ด๋‹น Promise๊ฐ€ resolve ๋˜๊ฑฐ๋‚˜ reject ๋  ๋•Œ๊นŒ์ง€ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ง„ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@Injectable()
class UsersService implements OnApplicationShutdown {
  onApplicationShutdown(signal: string) {
    console.log(signal); // ์˜ˆ: "SIGINT"
  }
}
์ •๋ณด
app.close()๋ฅผ ํ˜ธ์ถœํ•ด๋„ Node ํ”„๋กœ์„ธ์Šค๋Š” ์ข…๋ฃŒ๋˜์ง€ ์•Š์œผ๋ฉฐ, ๋‹จ์ง€ onModuleDestroy()์™€ onApplicationShutdown() ํ›„ํฌ๋งŒ ํŠธ๋ฆฌ๊ฑฐ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰ ์ค‘์ธ interval, ์ •๊ธฐ ์‹คํ–‰ ์ž‘์—… ๋“ฑ์ด ์žˆ๋‹ค๋ฉด ํ”„๋กœ์„ธ์Šค๋Š” ์ž๋™์œผ๋กœ ์ข…๋ฃŒ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Discovery service

@nestjs/core ํŒจํ‚ค์ง€์—์„œ ์ œ๊ณตํ•˜๋Š” DiscoveryService๋Š” NestJS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ํ”„๋กœ๋ฐ”์ด๋”, ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ๊ธฐํƒ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ํƒ์ƒ‰ํ•˜๊ณ  ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ํŠนํžˆ ํ”Œ๋Ÿฌ๊ทธ์ธ, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋˜๋Š” ๋Ÿฐํƒ€์ž„ ๋ฆฌํ”Œ๋ ‰์…˜์— ์˜์กดํ•˜๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ๊ตฌ์ถ•ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. DiscoveryService๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋” ์œ ์—ฐํ•˜๊ณ  ๋ชจ๋“ˆํ™” ๋œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ž๋™ํ™” ๋ฐ ๋™์  ๋™์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ

DiscoveryService๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์—, ์ด๋ฅผ ์‚ฌ์šฉํ•  ๋ชจ๋“ˆ์— DiscoveryModule์„ Import ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ํ†ตํ•ด DiscoveryService๊ฐ€ ์˜์กด์„ฑ ์ฃผ์ž… ๋Œ€์ƒ์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ NestJS ๋ชจ๋“ˆ์—์„œ ์ด๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

import { Module } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';
import { ExampleService } from './example.service';

@Module({
  imports: [DiscoveryModule],
  providers: [ExampleService],
})
export class ExampleModule {}

 

๋ชจ๋“ˆ ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด, DiscoveryService๋Š” ๋™์  ํƒ์ƒ‰์ด ํ•„์š”ํ•œ ์–ด๋–ค ํ”„๋กœ๋ฐ”์ด๋”๋‚˜ ์„œ๋น„์Šค์—๋„ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Injectable()
export class ExampleService {
  constructor(private readonly discoveryService: DiscoveryService) {}
}

ํ”„๋กœ๋ฐ”์ด๋” ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ํƒ์ƒ‰ํ•˜๊ธฐ

DiscoveryService์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋“ฑ๋ก๋œ ๋ชจ๋“  ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋Š” ๋ชจ๋“  ํ”„๋กœ๋ฐ”์ด๋”์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

const providers = this.discoveryService.getProviders();
console.log(providers);

 

๊ฐ ํ”„๋กœ๋ฐ”์ด๋” ๊ฐ์ฒด์—๋Š” ์ธ์Šคํ„ด์Šค, ํ† ํฐ, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋“ฑ๋ก๋œ ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์กฐํšŒํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const controllers = this.discoveryService.getControllers();
console.log(controllers);

 

์ด ๊ธฐ๋Šฅ์€ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ, ์˜ˆ๋ฅผ ๋“ค์–ด ๋ถ„์„ ์ถ”์  (analytics tracking)์ด๋‚˜ ์ž๋™ ๋“ฑ๋ก ๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์— ํŠนํžˆ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ถ”์ถœํ•˜๊ธฐ

ํ”„๋กœ๋ฐ”์ด๋” ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด, DiscoveryService๋Š” ์ด๋Ÿฌํ•œ ๊ตฌ์„ฑ ์š”์†Œ์— ๋ถ€์ฐฉ๋œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Ÿฐํƒ€์ž„์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํŠนํžˆ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ํƒœ๊ทธ ํ•˜๋Š” ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด ๋ด…์‹œ๋‹ค:

import { DiscoveryService } from '@nestjs/core';

export const FeatureFlag = DiscoveryService.createDecorator();

 

์ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์„œ๋น„์Šค์— ์ ์šฉํ•˜๋ฉด, ํ•ด๋‹น ์„œ๋น„์Šค์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋Š” ๋‚˜์ค‘์— ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { Injectable } from '@nestjs/common';
import { FeatureFlag } from './custom-metadata.decorator';

@Injectable()
@FeatureFlag('experimental')
export class CustomService {}

 

์ด์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ํ”„๋กœ๋ฐ”์ด๋”์— ๋ถ€์ฐฉ๋˜๋ฉด, DiscoveryService๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๋‹น ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฐ’์— ๋”ฐ๋ผ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์‰ฝ๊ฒŒ ํ•„ํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋Š” ํŠน์ • ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฐ’์œผ๋กœ ํƒœ๊ทธ ๋œ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:

const providers = this.discoveryService.getProviders();

const [provider] = providers.filter(
  (item) =>
    this.discoveryService.getMetadataByDecorator(FeatureFlag, item) ===
    'experimental',
);

console.log(
  'Providers with the "experimental" feature flag metadata:',
  provider,
);

๊ฒฐ๋ก 

DiscoveryService๋Š” NestJS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋Ÿฐํƒ€์ž„ ๋ฆฌํ”Œ๋ ‰์…˜์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๋‹ค์žฌ๋‹ค๋Šฅํ•˜๊ณ  ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋ฐ”์ด๋”, ์ปจํŠธ๋กค๋Ÿฌ, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ์œผ๋กœ์จ, ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ, ํ”Œ๋Ÿฌ๊ทธ์ธ, ์ž๋™ํ™” ๊ธฐ๋ฐ˜ ๊ธฐ๋Šฅ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์Šค์บ” ๋ฐ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ๊ณ ๊ธ‰ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, ๋ชจ๋“ˆํ™” ๋˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋งŒ๋“ค๊ณ ์ž ํ•  ๋•Œ, DiscoveryService๋Š” ์ด๋Ÿฌํ•œ ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ํšจ์œจ์ ์ด๊ณ  ๊ตฌ์กฐํ™”๋œ ๋ฐฉ์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Reference