# [[Node.js 메모리 누수 분석|Node.js 메모리 누수 분석]]

## 📌 Brief Summary
Node.js의 메모리 누수는 가비지 컬렉션(GC)되어야 할 객체들이 클로저, 이벤트 리스너, 타이머 등의 루트(Root) 객체에 계속 참조되어 메모리에서 해제되지 않을 때 발생합니다 [1, 2]. Node.js는 단일 프로세스로 장기간 실행되는 특성이 있어, 누수된 참조는 모든 요청에 걸쳐 지속적으로 축적되며 결국 V8 힙 한계에 도달하여 OOM(Out-Of-Memory) 크래시를 유발합니다 [3, 4]. 이 문제를 해결하기 위해서는 힙 스냅샷과 메모리 할당 타임라인 도구를 활용하여, 지속적으로 증가하는 객체의 참조 경로(Retaining Path)를 추적하고 참조를 끊어 GC가 정상 작동하도록 근본적인 원인을 수정해야 합니다 [5-7].

## 📖 Core Content

*   **메모리 누수 패턴 및 주요 원인**
    *   정상적인 Node.js 프로세스는 트래픽 발생 시 힙 메모리가 증가하고 가비지 컬렉션(GC) 이후 원래 수준으로 회복되는 톱니바퀴(Sawtooth) 패턴을 보입니다 [8]. 그러나 누수가 발생하면 GC가 동작한 후에도 메모리가 떨어지지 않고 지속적으로 상승하는 래칫(Ratchet) 패턴이 나타납니다 [7, 8].
    *   프로덕션 환경에서 가장 흔히 발생하는 7가지 누수 패턴은 다음과 같습니다: EventEmitter 리스너 누적(가장 흔함), 클로저(Closure) 변수의 의도치 않은 상태 유지, 제한 없이 증가하는 인메모리 캐시, 정리되지 않은 타이머(Timer) 및 인터벌, 복잡한 순환 참조, 닫히지 않은 스트림(Stream) 및 소켓, 그리고 AsyncLocalStorage 컨텍스트 누수입니다 [9-12].

*   **탐지 및 분석 도구**
    *   **힙 스냅샷(Heap Snapshots):** 의심스러운 작업을 수행하기 전(Baseline)과 부하 발생 후를 나누어 스냅샷을 촬영하고, 이 두 스냅샷 사이에서 할당된 후 해제되지 않은 객체들("Objects allocated between snapshots")을 비교하여 누수 후보를 도출합니다 [6, 13]. 브라우저나 프론트엔드 앱 분석 시 일회성 할당에 의한 오탐지를 필터링하기 위해 스냅샷을 3번 캡처하여 비교하는 3-스냅샷 기법(Three-snapshot technique)이 신뢰성이 높습니다 [14].
    *   **할당 타임라인(Allocation Timeline):** Chrome DevTools를 `--inspect` 플래그와 함께 연결하여 시간에 따른 메모리 할당 기록을 수집합니다 [5, 8]. GC 이후에도 회수되지 않아 파란색 막대로 남은 객체들을 통해 어떤 함수나 생성자가 누수를 유발하는지 추적할 수 있습니다 [8, 15-17].
    *   **프로그램 및 패키지 기반 모니터링:** `process.memoryUsage()`를 이용해 RSS(Resident Set Size) 및 `heapUsed` 값의 지속적 증가를 확인하거나 [18, 19], `heapdump`, `clinic.js` 등의 도구를 사용해 자동화된 분석으로 메모리 누수 발생 위치를 식별할 수 있습니다 [5, 9].

*   **진단 로깅 및 GC 튜닝**
    *   `--trace-gc` 플래그를 적용하면 콘솔에 V8 엔진의 GC 이벤트(Scavenge 및 Mark-sweep) 발생 시간, 빈도, 회수된 메모리양 등이 기록되어 애플리케이션의 메모리 부족 현상과 누수를 파악할 수 있습니다 [20-23].
    *   만약 V8의 힙 영역 중 장기 생존 객체가 저장되는 공간에 큰 메모리가 필요하다면, `--max-old-space-size` 명령줄 플래그로 Old Space 크기를 늘려 애플리케이션 충돌과 과도한 GC 지연을 방지할 수 있습니다 [24]. 반대로, 짧은 주기의 객체 생성이 많은 경우에는 `--max-semi-space-size` 플래그로 New Space를 늘려 마이너 GC 주기를 조절할 수 있습니다 [25].

## 🔗 Knowledge Connections
- **Related Topics:** [[가비지 컬렉션 (Garbage Collection)|가비지 컬렉션 (Garbage Collection)]], [[V8 엔진 (V8 Engine)|V8 엔진 (V8 Engine)]], [[힙 스냅샷 (Heap Snapshots)|힙 스냅샷 (Heap Snapshots)]], [[Mark-Sweep|Mark-Sweep]]
- **Projects/Contexts:** [[Chrome DevTools|Chrome DevTools]], [[clinic.js|clinic.js]], [[Node.js Production Monitoring|Node.js Production Monitoring]]
- **Contradictions/Notes:** 소스에 따르면 모던 프론트엔드 환경의 브라우저에서는 메모리 누수의 가장 주요한 원인(1위)으로 SPA(Single Page Application) 경로 전환을 꼽고 있지만 [26], Node.js 프로덕션 서버 환경에서는 EventEmitter 리스너 누적이 가장 흔한 메모리 누수 패턴으로 언급되는 차이가 있습니다 [9].

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