본문으로 건너뛰기
개발 머꼬
개발 노트NestJS
hohyeon.dev22

배포할 때마다 처리 중이던 요청이 끊기고 OnModuleDestroy가 안 불린 이유

  • #DevOps
  • #Engineering Note
  • #NestJS

문제 발생

배포 때마다 진행 중이던 요청이 502로 끊기고, 큐 컨슈머는 처리 중이던 메시지를 잃었습니다. 정리 코드를 분명히 넣어뒀는데 로그가 한 줄도 남지 않았습니다.

@Injectable()
export class QueueConsumer implements OnModuleDestroy {
  async onModuleDestroy() {
    await this.channel.close(); // 실행되지 않음
  }
}

원인 분석

종료 훅이 켜져 있지 않았습니다. NestJS 문서가 짚는 그대로 — 종료 훅은 먼저 활성화해야 하며, 부트스트랩에서 app.enableShutdownHooks()를 명시적으로 호출해야 합니다. 기본으로 동작하지 않습니다.

기본값이 꺼짐인 이유도 문서에 있습니다 — enableShutdownHooks는 리스너를 등록하므로 메모리를 소비하며, 한 프로세스에서 여러 Nest 인스턴스를 띄우는 병렬 테스트에서는 Node 경고가 날 수 있습니다.

켜고 나면 종료 순서도 정해져 있습니다. 애플리케이션이 종료 시그널을 받으면 등록된 종료 메서드들이 순차적으로 호출되고, 어떤 핸들러가 비동기 작업을 반환하면 그 프로미스가 완료될 때까지 다음 순서로 넘어가지 않습니다.

해결 방안

  1. 부트스트랩에서 켭니다.
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
await app.listen(3000);
  1. 정리 작업을 올바른 훅에 나눠 담습니다. 문서가 제공하는 인터페이스는 OnModuleInit, OnApplicationBootstrap, OnModuleDestroy, beforeApplicationShutdown, OnApplicationShutdown입니다. 새 작업 수신을 멈추는 일과 커넥션을 닫는 일은 단계가 다릅니다.

  2. 컨테이너 환경의 시그널을 확인합니다. Docker/Kubernetes는 SIGTERM을 보냅니다. 문서는 Windows에서 SIGINT, SIGBREAK, 부분적으로 SIGHUP만 지원되고 SIGTERM은 OS 제약으로 특별 처리되지 않는다고 적습니다 — 로컬이 Windows라면 이 차이 때문에 재현되지 않습니다.

  3. 훅에 무한정 기다리는 코드를 두지 않습니다. 오케스트레이터의 유예 시간(terminationGracePeriodSeconds 등)을 넘기면 프로세스는 어차피 강제 종료됩니다. 정리에도 타임아웃을 겁니다.

  4. 테스트에서는 켜지 않는 편이 낫습니다. 문서가 경고하는 리스너 누적이 그대로 나타납니다. 애플리케이션 부트스트랩 경로에서만 호출합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.