# [[Node.js 프로덕션 메모리 누수 진단|Node.js 프로덕션 메모리 누수 진단]]

## 📌 Brief Summary
Node.js 프로덕션 메모리 누수는 단일 프로세스로 장기 실행되는 Node.js의 특성상 참조가 누적되어 V8 가비지 컬렉터(GC)가 메모리를 회수할 수 없게 되면서 발생합니다 [1, 2]. 정상적인 프로세스와 달리 가비지 컬렉션 이후에도 힙 메모리 사용량이 원래 수준으로 떨어지지 않고 계단식(Ratchet)으로 상승하는 패턴을 보이는 것이 주된 특징입니다 [3, 4]. 이를 진단하고 해결하려면 힙 스냅샷 비교, 힙 프로파일링, 메모리를 계속 참조하고 있는 요인(Retainer)을 추적하는 체계적인 과정이 필수적입니다 [4, 5].

## 📖 Core Content
**누수의 원리와 증상 (Principles and Symptoms)**
- Node.js 메모리 누수는 객체가 "유실"되는 것이 아니라 코드 어딘가에서 계속 참조되고 있어 GC가 도달할 수 없는 객체로 식별하지 못해 발생합니다 [2, 6].
- 정상적인 프로세스는 트래픽 발생 시 힙이 증가하고 GC 이후 기준선으로 떨어지는 '톱니바퀴(Sawtooth)' 패턴을 보이지만, 누수가 발생하면 GC 후에도 힙 사용량이 떨어지지 않는 '계단식(Ratchet)' 패턴을 나타냅니다 [3, 4].
- 주요 증상으로는 점진적인 메모리 증가, 잦고 긴 GC 일시 정지 시간, 응답 시간 저하, 그리고 궁극적으로 OOM(Out of Memory) 충돌 현상이 있습니다 [7].

**핵심 진단 도구 (Core Diagnostic Tools)**
- **`--inspect` 및 Chrome DevTools:** 서버를 `--inspect` 플래그로 실행하여 Chrome에 연결한 후, 메모리 패널에서 힙 스냅샷을 캡처해 스냅샷 사이에 할당된 객체를 비교 분석할 수 있습니다 [3, 8, 9].
- **`heapdump`:** 프로덕션 환경(Chrome DevTools 접근이 어려운 경우)에서 프로그래밍 방식으로 힙 스냅샷을 기록하여 로컬로 다운로드 및 분석할 수 있게 돕습니다 [8, 10, 11].
- **`--heap-prof` 플래그:** 외부 패키지 없이 Node.js 자체에 내장된 V8 네이티브 프로파일링을 활성화하여 함수 수준의 할당 세부 내역을 파악할 수 있습니다 [12].
- **`process.memoryUsage()`:** RSS(Resident Set Size), heapTotal, heapUsed 값을 지속적으로 확인하여 프로그래밍 방식으로 힙의 점진적인 증가 여부를 감시할 수 있습니다 [13, 14].

**일반적인 누수 발생 패턴 (Common Leak Patterns)**
- **이벤트 리스너 누적 (EventEmitter Listener Accumulation):** 요청 핸들러 내에서 리스너를 추가하고 제거하지 않으면 참조가 계속 누적되며, 프로덕션 환경에서는 보통 `MaxListenersExceededWarning` 경고가 명확한 누수 신호로 간주됩니다 [5, 11, 15].
- **클로저 변수 유지 (Closure Variable Retention):** 비동기 체인이나 타이머 콜백 등에서 대규모 데이터(예: 전체 요청/응답 객체)를 캡처하는 클로저를 사용하여 객체 수명이 불필요하게 늘어나는 경우입니다 [15-17].
- **무제한 캐시 증가 (Unbounded Cache Growth):** 최대 크기나 제한을 두지 않은 인메모리 캐시 변수에 객체가 무한정 쌓이는 패턴입니다 [15].
- **타이머/관찰자 및 소켓 누수:** `clearInterval` 처리되지 않은 `setInterval` 콜백이나, 데이터 송수신 후 닫히지 않은 스트림/소켓이 버퍼와 네트워크 핸들을 점유하여 메모리를 해제하지 못하게 만듭니다 [17, 18].

**진단 및 해결 워크플로우 (Diagnosis & Fix Workflow)**
- 모니터링을 통해 메모리의 계단식 증가 패턴(Ratchet)을 확인한 뒤 베이스라인 힙 스냅샷을 캡처합니다 [4].
- 트래픽 부하를 유발하는 행동을 실행한 후 두 번째 스냅샷을 캡처하고 두 스냅샷을 비교합니다 [4, 19].
- 비교 결과에서 유출된 객체를 찾은 후, 해당 객체를 유지하고 있는 리테이너(Retainer) 트리를 GC 루트까지 따라가 코드를 수정하고, 수정을 확인하기 위해 테스트를 반복합니다 [4, 20].

## 🔗 Knowledge Connections
- **Related Topics:** [[V8 가비지 컬렉션(Garbage Collection)|V8 Garbage Collection]], [[Heap Snapshot|Heap Snapshot]], [[Retaining Path|Retaining Path]], process.memoryUsage()
- **Projects/Contexts:** Node.js Production Environment, [[Chrome DevTools Memory Panel|Chrome DevTools Memory Panel]]
- **Contradictions/Notes:** 일반적으로 누수 후보를 찾기 위해 트래픽 전/후 두 개의 힙 스냅샷을 비교하는 방법이 자주 소개되지만, 일회성 메모리 할당으로 인한 오탐(False Positive)을 걸러내기 위해서는 세 개의 스냅샷을 연달아 캡처해 비교하는 "Three-snapshot technique" 기법이 가장 신뢰할 수 있는 수단이라는 점을 유의해야 합니다 [19].

---
*Last updated: 2026-04-19*