이번 글에서는 NestJS Lifecycle Events에 대해 정리해보도록 하겠습니다. NestJS Health Check 방법에 대해서는 이전 글에서도 몇 번 다룬 적이 있으니 참고하시길 바랍니다.
NestJS 애플리케이션 내 Health Check 추가
Terminus 기반 Health Check 방법 (NestJS)
NestJS Lifecycle 주기
Nest 애플리케이션과 모든 애플리케이션 요소에는 Nest에서 관리하는 수명 주기가 있습니다. Nest는 주요 수명 주기 이벤트에 대한 가시성을 제공하는 수명 주기 후크와 이러한 이벤트가 발생할 때 조치를 취할 수 있는 기능(모듈, 주입 가능 또는 컨트롤러에서 등록된 코드 실행)을 제공합니다.
다음 다이어그램은 애플리케이션이 부트스트랩된 시간부터 노드 프로세스가 종료될 때까지 주요 애플리케이션 수명 주기 이벤트의 시퀀스를 보여줍니다. 전체 수명 주기를 초기화, 실행 및 종료의 세 단계로 나눌 수 있습니다.
이 수명 주기를 사용하여 모듈 및 서비스의 적절한 초기화를 계획하고, 활성 연결을 관리하고, 종료 신호를 받으면 애플리케이션을 정상적으로 종료할 수 있습니다.
NestJS Lifecycle 이벤트
애플리케이션 부트스트래핑 및 종료 중에 수명 주기 이벤트가 발생합니다. Nest는 다음 수명 주기 이벤트 각각에서 모듈, 주입 가능 항목 및 컨트롤러에 등록된 수명 주기 후크 메서드를 호출합니다(아래 설명된 대로 종료 후크를 먼저 활성화해야 함). 위의 다이어그램에 표시된 것처럼 Nest는 적절한 기본 메서드를 호출하여 연결 수신을 시작하고 연결 수신을 중지합니다.
다음 표에서 onModuleDestroy, beforeApplicationShutdown 및 onApplicationShutdown은 명시적으로 app.close()를 호출하거나 프로세스가 특수 시스템 신호(예: SIGTERM)를 수신하고 애플리케이션 부트스트랩에서 enableShutdownHooks를 올바르게 호출한 경우에만 트리거됩니다(아래 애플리케이션 종료 참조).
주요 Lifecycle Hooks | 설명 |
onModuleInit() | 호스트 모듈의 종속성이 해결되면 호출됩니다. |
onApplicationBootstrap() | 모든 모듈이 초기화된 후 연결을 수신 대기하기 전에 호출됩니다. |
onModuleDestroy() | 종료 신호(예: SIGTERM)가 수신된 후 호출됩니다. |
beforeApplicationShutdown() | 모든 onModuleDestroy() 핸들러가 완료된 후 호출됩니다(약속 해결 또는 거부). 완료되면(약속 해결 또는 거부) 기존 연결이 모두 닫힙니다(app.close() 호출). |
onApplicationShutdown() | 연결 종료 후 호출됩니다(app.close() 해결). |
NestJS Lifecycle 사용 방법
각 수명 주기 후크는 인터페이스로 표시됩니다. 인터페이스는 TypeScript 컴파일 후에 존재하지 않기 때문에 기술적으로 선택 사항입니다. 그럼에도 불구하고 강력한 타이핑 및 편집기 도구의 이점을 얻으려면 이를 사용하는 것이 좋습니다. 수명 주기 후크를 등록하려면 적절한 인터페이스를 구현합니다.
예를 들어 특정 클래스(예: Controller, Provider 또는 Module)에서 모듈 초기화 중에 호출할 메서드를 등록하려면 아래와 같이 onModuleInit() 메서드를 제공하여 OnModuleInit 인터페이스를 구현합니다.
import { Injectable, OnModuleInit } from '@nestjs/common'; @Injectable() export class UsersService implements OnModuleInit { onModuleInit() { console.log(`The module has been initialized.`); } }
비동기 Initialization
OnModuleInit 및 OnApplicationBootstrap 후크를 사용하면 애플리케이션 초기화 프로세스를 연기할 수 있습니다(Promise를 반환하거나 메서드를 비동기로 표시하고 메서드 본문에서 비동기 메서드 완료를 기다림).
async onModuleInit(): Promise<void> { await this.fetch(); }
애플리케이션 종료
onModuleDestroy(), beforeApplicationShutdown() 및 onApplicationShutdown() 후크는 종료 단계에서 호출됩니다(app.close()에 대한 명시적 호출에 대한 응답 또는 옵트인된 경우 SIGTERM과 같은 시스템 신호 수신 시).
이 기능은 dynos 또는 유사한 서비스를 위해 Heroku에서 컨테이너의 수명 주기를 관리하기 위해 Kubernetes와 함께 자주 사용됩니다.
종료 후크 수신기는 시스템 리소스를 사용하므로 기본적으로 비활성화되어 있습니다. 종료 후크를 사용하려면 enableShutdownHooks()를 호출하여 수신기를 활성화해야 합니다.
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Starts listening for shutdown hooks app.enableShutdownHooks(); await app.listen(3000); } bootstrap();
고유한 플랫폼 제한으로 인해 NestJS는 Windows에서 애플리케이션 종료 후크에 대한 지원을 제한했습니다. SIGINT는 물론 SIGBREAK 및 어느 정도 SIGHUP도 작동할 것으로 예상할 수 있습니다. 그러나 작업 관리자에서 프로세스를 죽이는 것은 무조건적이기 때문에 SIGTERM은 Windows에서 작동하지 않습니다.
enableShutdownHooks는 리스너를 시작하여 메모리를 소비합니다. 이러한 이유로 enableShutdownHooks는 기본적으로 활성화되어 있지 않습니다. 단일 노드 프로세스에서 여러 인스턴스를 실행할 때 이 조건에 유의하십시오.
응용 프로그램이 종료 신호를 받으면 등록된 onModuleDestroy(), beforeApplicationShutdown(), onApplicationShutdown() 메서드(위에서 설명한 순서대로)를 해당 신호를 첫 번째 매개변수로 사용하여 호출합니다. 등록된 함수가 비동기 호출(약속 반환)을 기다리는 경우 Nest는 약속이 해결되거나 거부될 때까지 시퀀스를 계속하지 않습니다.
@Injectable() class UsersService implements OnApplicationShutdown { onApplicationShutdown(signal: string) { console.log(signal); // e.g. "SIGINT" } }
app.close()를 호출하면 노드 프로세스가 종료되지 않고 onModuleDestroy() 및 onApplicationShutdown() 후크만 트리거되므로 일부 간격, 장기 실행 백그라운드 작업 등이 있는 경우 프로세스가 자동으로 종료되지 않습니다.