## 클로저가 메모리 누수를 일으키는 이유
[[클로저 (Closure)|클로저]]는 자신이 선언된 렉시컬 환경의 변수들을 참조하고, 그 참조가 유지되는 한 해당 변수들은 가비지 컬렉션의 대상이 되지 않는다.
그래서 클로저가 불필요하게 오래 유지되거나 많이 생기면 불필요하게 메모리에 남아 있는 변수들이 많아지면서 메모리 누수가 발생할 수 있다.
## 메모리 누수 방지 방법
### 불필요한 클로저 사용 자제
일회성 작업의 경우에는 클로저를 사용하지 않거나 즉시 해제 가능한 구조로 코드를 작성하는 것이 좋다.
### 명시적 참조 해제
클로저를 더 이상 사용하지 않을 때 클로저 함수 자체, 혹은 클로저에서 참조하고 있는 변수들을 null 등으로 명시적으로 초기화한다.
- 내부 변수 참조 해제
```javascript
function outer() {
let largeData = new Array(100000).fill("hello");
return function inner() {
console.log(largeData[0]);
largeData = null; // 불필요해지면 참조 해제
};
}
const fn = outer();
fn(); // 사용 후 참조 제거
```
- 클로저 참조 해제
```javascript
function outer() {
let secret = 123;
return function inner() {
console.log(secret);
};
}
let closure = outer();
closure(); // 123
closure = null; // 클로저 참조 해제 → GC 대상
```
> [!Note] 명시적 참조 해제 방법
> - 참조 해제가 목적이라면 `null`이나 `undefined`를 사용하는 것이 일반적
> - 참조는 유지하되 빈 상태로 만들고 싶다면 빈 배열, 빈 객체 등으로 초기화할 수 있다.
### 이벤트 리스너, 타이머 정리
```javascript
// 이벤트 리스너 정리하기
function bindClickHandler() {
function handleClick() {
console.log("clicked");
}
document.addEventListener("click", handleClick);
// 이후 필요 없어지면 정리
document.removeEventListener("click", handleClick);
}
```
```javascript
// 타이머 정리하기
const id = setInterval(() => {
console.log("running");
}, 1000);
// 나중에 중단
clearInterval(id);
```
### 스코프 제한
즉시 실행 함수나 블록 스코프를 이용해서 클로저의 스코프를 제한하는 방법으로 자연스럽게 수명을 종료시키고 가비지 컬렉션의 대상으로 만드는 방법도 있다.
<!--todo: 즉시 실행 함수 좋은 예시 고민해보기-->
## 메모리 누수 대처 방법
### 모니터링
개발자 도구의 Heap Snapshot을 통해 메모리를 점유하고 있는 객체를 추적하고, 클로저나 이벤트 리스너 등 불필요한 참조를 제거한다.
### 구조 개선
불필요하게 사용되는 클로저가 있는지 확인한다.
약한 참조를 사용하는 [WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap), [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet)을 활용해서 참조를 약화하는 것도 방법이 된다.