# [[Node.js 성능 디버깅]]

## 📌 Brief Summary
Node.js 성능 디버깅은 주로 V8 엔진의 힙(Heap) 메모리 사용량을 추적하고 가비지 컬렉션(GC) 동작을 분석하여 애플리케이션의 성능 저하 및 메모리 누수(Memory Leak)를 해결하는 과정이다 [1, 2]. 힙 스냅샷(Heap Snapshot), 할당 타임라인, GC 트레이싱 등의 진단 도구를 활용하여 메모리 내에서 불필요하게 유지되는 참조 객체를 식별한다 [3-5]. 이와 더불어, 실시간 모니터링 API 및 V8 명령줄 플래그 튜닝을 통해 메모리 한계를 조정하여 서버 안정성과 처리량을 최적화할 수 있다 [6-8].

## 📖 Core Content

*   **메모리 누수의 정의와 주요 패턴**
    Node.js에서 메모리 누수는 가비지 컬렉터(GC)에 의해 수거되어야 할 객체가 클로저(Closure), 전역 변수 등에 의해 여전히 참조를 유지하고 있을 때 발생한다 [3, 9]. 이로 인해 시간이 지남에 따라 메모리가 지속적으로 증가하며, 결과적으로 프로세스가 Out-Of-Memory(OOM) 오류로 인해 중단될 수 있다 [10, 11]. Node.js 환경에서 가장 흔하게 발생하는 누수 패턴은 다음과 같다:
    *   **이벤트 리스너 누적**: `on('event', fn)`을 지속적으로 추가하고 제거하지 않으면 해당 콜백 함수와 관련된 메모리가 누수된다 [12].
    *   **클로저(Closure) 변수 보존**: 비동기 체인이나 스코프를 공유하는 클로저 내부에 큰 객체를 캡처해 두면 해당 클로저의 수명 주기 동안 메모리가 유지된다 [13, 14].
    *   **무제한 캐시(Unbounded Cache)**: 인메모리 캐시의 크기나 만료를 제한하지 않으면 데이터가 끝없이 축적된다 [14].
    *   **정리되지 않은 타이머**: `clearInterval`이 호출되지 않은 `setInterval`은 콜백 클로저에 있는 모든 항목의 GC를 방지한다 [15, 16].
    *   **닫히지 않은 스트림 및 소켓**: 올바르게 종료되지 않은 스트림은 내부 버퍼와 네트워크 핸들을 점유한다 [16].

*   **메모리 디버깅 및 프로파일링 도구**
    *   **힙 스냅샷(Heap Snapshots)**: 특정 시점의 전체 메모리 상태를 캡처하는 기능으로, Chrome DevTools에서 스냅샷들을 비교('Comparison' 뷰)하여 메모리 누수를 검사할 수 있다 [4, 17-19]. `--inspect` 플래그로 연결하거나 `heapdump` 패키지를 사용해 캡처한 뒤 '할당된 후 해제되지 않은 객체(Objects allocated between snapshots)'를 찾아낸다 [4, 5, 18].
    *   **할당 타임라인(Allocation Timeline)**: 특정 시간 동안의 메모리 할당을 시각적으로 추적한다 [17, 20]. 가비지 컬렉션 후에도 살아있는 객체는 파란색 막대로 표시되며, 이를 통해 메모리 누수 후보를 특정할 수 있다 [5, 17, 20].
    *   **GC 트레이싱(--trace-gc)**: `--trace-gc` 플래그를 사용해 실행하면 가비지 컬렉션의 세부 발생 시간, 소요 시간, GC 전후의 힙 용량 변화를 콘솔에서 확인할 수 있다 [5, 21, 22]. 만약 Mark-sweep(오래된 세대 GC) 작업이 지나치게 빈번하거나 GC 이후에도 힙이 원래 수준으로 줄어들지 않으면 메모리 누수의 강력한 신호이다 [23].
    *   **메모리 모니터링 API**: 코드 내에서 `process.memoryUsage()`를 호출하여 프로세스에 할당된 전체 메모리(`rss`)와 힙의 총량(`heapTotal`), 현재 사용 중인 힙(`heapUsed`) 등을 실시간으로 파악할 수 있다 [6, 7].

*   **메모리 튜닝 및 최적화**
    Node.js는 V8 엔진의 메모리 제한 및 GC 빈도를 직접 제어할 수 있는 명령줄 플래그를 제공한다 [7].
    *   `--max-old-space-size`: 수명이 긴 객체들이 저장되는 Old Space의 크기 한도를 설정하여, 지속적인 데이터를 다룰 때 OOM을 지연시키거나 예방할 수 있다 [8, 24].
    *   `--max-semi-space-size`: 빈번하게 생성 및 소멸되는 객체가 저장되는 New Space의 크기를 조절한다. 처리량이 많은 상황에서 이 크기를 늘리면 잦은 Scavenge(마이너 GC) 사이클을 줄여 성능을 향상할 수 있다 [24, 25].
    *   `--expose-gc`: 이를 설정하면 애플리케이션 코드 내에서 `global.gc()`를 수동으로 호출하여 대량의 데이터 처리 후 명시적으로 메모리를 회수할 수 있다 [26, 27].

## 🔗 Knowledge Connections
- **Related Topics:** [[V8 JavaScript Engine]], [[Garbage Collection]], [[Heap Snapshot]], [[Memory Leak]]
- **Projects/Contexts:** [[Chrome DevTools Memory Panel]], [[Node.js Production Environment]]
- **Contradictions/Notes:** 애플리케이션 개발자가 `System.gc()` 또는 `global.gc()`를 사용하여 수동으로 가비지 컬렉션을 트리거할 수는 있으나, GC 동작을 임의로 예측 및 강제 실행하는 행위는 오히려 애플리케이션의 성능을 저하시킬 수 있으므로 주의해서 사용해야 한다 [27, 28].

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