원문 제목: Frontend Memory Leaks: A 500-Repository Static Analysis and Five-Scenario Benchmark Study 원문 링크: https://stackinsight.dev/blog/memory-leak-empirical-study/ 번역일: 2026-03-11 KST
이 글은 프론트엔드 메모리 누수가 실제로 얼마나 흔한지, 그리고 누락된 cleanup이 어느 정도 비용을 만드는지 실증적으로 측정한 연구다. 작성자는 React, Vue, Angular용 AST 기반 탐지기를 만들어 공개 저장소 500개를 스캔했고, 별도로 통제된 벤치마크를 작성해 cleanup 유무에 따른 retained heap 증가량을 측정했다.
가장 먼저 짚는 포인트는 “자바스크립트는 GC가 있으니 메모리 누수가 크지 않다”는 오해다. GC는 개발자의 의도를 이해하지 못하고, 단지 도달 가능한 객체만 추적한다. 예를 들어 useEffect 안에서 이벤트 리스너를 등록하고 cleanup을 반환하지 않으면, window -> listener -> closure -> component state 참조 체인이 살아 남아 컴포넌트 상태가 계속 유지된다. 즉, GC 실패가 아니라 애플리케이션 레벨 버그다.
스캔 결과는 상당히 강하다. 총 714,217개 파일을 검사했고, 55,864개의 잠재적 누수 패턴을 발견했다. 500개 저장소 중 430개, 즉 86%가 최소 1개 이상의 누락된 cleanup 패턴을 가지고 있었다. React가 절대 건수는 가장 많았지만, Vue와 Angular 역시 비슷한 종류의 실수가 반복됐다.
탐지한 패턴은 다음과 같다.
- React: cleanup 없는
useEffect, 제거되지 않는addEventListener, 정리되지 않는 타이머,unsubscribe()없는 subscription - Vue:
onMounted만 있고onUnmounted가 없는 경우, stop 핸들을 잡지 않은watch()/watchEffect(), 제거되지 않는 이벤트 등록 - Angular:
unsubscribe()또는takeUntil없는.subscribe(), 누락된ngOnDestroy, 정리되지 않는Renderer2.listen() - 공통:
IntersectionObserver/MutationObserver/ResizeObserver의disconnect()누락,requestAnimationFrame취소 누락
벤치마크 파트도 실무적으로 중요하다. 작성자는 mount/unmount 100회, 50회 반복, 매 측정 전 강제 GC 조건에서 누락된 cleanup이 있을 때와 없을 때를 비교했다. 거의 모든 시나리오에서 cleanup이 없으면 사이클당 약 8KB 수준의 retained heap이 선형적으로 쌓였다. 숫자 자체는 작아 보여도 라우팅, 탭 전환, 실시간 대시보드처럼 반복 마운트가 많은 UI에서는 몇 분 안에 체감되는 성능 저하로 연결될 수 있다는 점을 강조한다.
엔지니어링 리드에게 권하는 30분 액션 플랜은 명확하다.
useEffect에 cleanup return이 없는지 검사한다..subscribe()호출 뒤에unsubscribe()또는takeUntil패턴이 있는지 확인한다.addEventListener/observer/timer 등록 뒤에 대응 정리 코드가 있는지 본다.- Vue의
watch()는 stop 핸들을 반드시 캡처하고 해제한다. - Angular 컴포넌트는
ngOnDestroy를 빠짐없이 구현한다.
실무 요약: 이 글의 핵심은 “메모리 누수는 희귀한 예외가 아니라, 잘 관리된 오픈소스 코드베이스에서도 매우 흔한 기본 실수”라는 점이다. 프론트엔드 팀은 lint, 코드 리뷰 체크리스트, 테스트 시나리오, 프로파일링 기준을 통해 cleanup을 ‘선택 사항’이 아니라 기본 규칙으로 다뤄야 한다.