JavaScript 애플리케이션의 개발 및 배포 후 Out of Memory (OOM) 오류가 발생했다...😥
이 글에서는 OOM 오류의 원인과 이를 해결하기 위한 접근 방법을 중심으로 정리해 보겠습니다.

JavaScript Heap Out of Memory 오류란?
JavaScript Heap은 애플리케이션이 동적으로 생성하는 객체들이 저장되는 메모리 공간을 의미합니다.
JavaScript 애플리케이션의 힙 메모리는 고정된 크기를 가지고 있으며, 이 크기를 초과하면 Heap Out of Memory 오류가 발생하게 됩니다.
이 오류는 일반적으로 다음과 같은 상황에서 발생합니다.
- 데이터가 지나치게 많이 로드될 때
- 반복문 안에서 대규모 객체를 생성하고 해제하지 않을 때
- 메모리 누수가 발생할 때
오류 발생 및 문제 파악
초기 상황
배포 후 서버의 메모리 사용량이 급격히 증가하여 OOM 오류가 발생했으며, 이를 해결하기 위해 다음 단계를 수행했습니다.
- 개발 환경에서 실제 환경과 유사하게 데이터를 설정하고 동일 작업을 반복해 OOM 오류를 재현했습니다.
- Heapdump 파일을 생성하여 문제 발생 시 메모리 상태를 분석했습니다.
Heapdump란?
Heapdump는 애플리케이션의 힙 메모리 스냅샷을 의미합니다. 이를 통해 실행 중인 프로그램에서 메모리를 차지하고 있는 객체들을 분석하고, 누수 여부나 과도한 메모리 사용 객체를 추적할 수 있습니다.
수동 스냅샷 생성 방법
메모리 상태를 추적하기 위해 heapdump 모듈을 설치하고, 특정 경로로 HTTP 요청을 보낼 때 힙 스냅샷을 생성하도록 설정했습니다.
npm install heapdump
import * as heapdump from "heapdump";
app.use("/heapdump", (req, res) => {
const filename = `./heap_${Date.now()}.heapsnapshot`;
heapdump.writeSnapshot(filename, (err) => {
if (err) {
return res.status(500).send("Error generating heapdump: " + err);
}
return res.send("Heapdump has been generated in " + filename);
});
});
위 API를 호출하면 지정된 경로에 힙 스냅샷 파일이 생성되며, 이 파일을 통해 메모리 사용 상태를 확인할 수 있습니다.

메모리 스냅샷 분석 및 문제 해결
메모리 스냅샷 분석
스냅샷을 크롬 디버거에서 분석하여 메모리가 급격히 증가한 시점과 OOM 발생 후 안정화된 상태를 비교했습니다. 주요 분석 포인트는 다음과 같습니다.
- 일시적 부하인지 메모리 누수인지 구별: 누수는 계속 증가하지만 일시적 부하는 특정 시점에서 메모리가 안정됩니다.
- 과도한 메모리 사용 객체: 메모리를 많이 사용하는 객체를 식별하고, 이러한 객체가 반복적으로 생성되는지 확인했습니다.
결론적으로, 메모리 누수가 아닌 일시적인 부하로 인해 발생한 오류로 파악되었습니다.

해결 방안
문제의 원인을 바탕으로, 다음과 같은 최적화를 적용했습니다.
- 메모리 부하 분석을 위한 Heapdump 모듈 추가 및 로직 구현
Heapdump 모듈을 추가하여 힙 메모리 사용을 정기적으로 추적하고, 필요시 메모리 스냅샷을 생성하여 문제 발생 시 상태를 저장하고 분석할 수 있는 기반을 마련했습니다. - NxN 탐색 로직 최적화
기존에는 List를 이용해 반복적으로 find 작업을 수행했지만, Map을 사용하여 중첩 반복문을 제거했습니다. 이를 통해 시간 복잡도를 줄이고 성능을 향상시켰습니다. - 쿼리 성능 최적화
JOIN을 사용한 복잡한 조회 로직을 검토하고 최적화했습니다.- LEFT OUTER JOIN과 같은 복잡한 조인은 메모리와 성능에 부담을 줄 수 있어, 필요한 데이터만 별도로 조회하도록 개별 쿼리로 분리했습니다.
- 조회된 데이터를 애플리케이션 코드에서 조합하여 반환하는 방식으로 변경하여, 쿼리 복잡도를 낮추고 메모리 사용량을 줄였습니다.
적용 후 결과 및 정리
위의 개선 사항을 적용한 후 재테스트 결과, 메모리 사용량이 안정화되었고 OOM 오류가 발생하지 않는 것을 확인했습니다.

성능 최적화는 메모리 문제를 예방하는 데 매우 중요한 요소라는 점을 다시금 깨닫게 되었습니다.
이번 경험을 통해, 향후 개발 시에는 정기적인 메모리 모니터링과 부하테스트를 통해 문제가 발생하기 전에 감지할 수 있는 체계를 마련할 계획입니다.
'Backend > TypeScript' 카테고리의 다른 글
| [TypeScript] Type vs Interface (0) | 2024.01.10 |
|---|---|
| [TypeScript] type predicate (타입 프리디케이트) (0) | 2024.01.07 |
| [TypeScript] Statement와 Expression(문장과 표현식) (0) | 2024.01.06 |
| [TypeScript] Overloading 오버로딩 (0) | 2023.12.30 |
| [TypeScript] 함수 시그니처 타입으로 선언하기 (0) | 2023.12.22 |