# JavaScript 클로저 완전 정복 – 초보자를 위한 함수 기억하기 비법 (55자)

---

# # 서론 – “왜 함수가 기억을 할까?”

프로그래밍을 처음 시작한 사람이라면 “함수가 호출될 때마다 새로운 메모리를 할당한다”는 설명을 들어본 적이 있을 겁니다. 그런데 **JavaScript**에서는 함수가 **자신이 선언됐던 환경을 기억**하고, 그 기억을 바탕으로 동작한다는 사실, 알고 계셨나요?

이 현상을 제대로 이해하지 못하면 비동기 처리, 콜백, 모듈화 등 실무에서 마주치는 수많은 버그에 허덕이게 됩니다. 이번 글에서는 *클로저(Closure)* 라는 개념을 **쉽게** 풀어내고, 실제 예제와 함께 **함수가 어떻게 기억을 하는지**를 단계별로 살펴보겠습니다.

> **키워드**: JavaScript 클로저, 함수 스코프, 프로그래밍 기초

---

# # 1️⃣ 클로저란 무엇인가? – 기본 개념 정리

## # 1-1. 스코프와 렉시컬 스코프

- **스코프(scope)** : 변수와 함수가 접근 가능한 범위

- **렉시컬(Lexical) 스코프** : 함수가 **정의된 위치**에 따라 스코프가 결정

JavaScript는 **정적 스코프**를 사용합니다. 즉, 함수가 **어디서 호출되었는가**가 아니라 **어디서 정의되었는가**에 따라 변수에 접근할 수 있는 범위가 정해집니다.

## # 1-2. 클로저 정의

> **클로저**는 *함수가 선언된 렉시컬 환경(외부 변수들)을 함께 기억*하고, 그 환경에 접근할 수 있는 **함수와 그 환경의 조합**을 말합니다.

쉽게 말해, **함수가 살아있는 동안 자신이 선언된 스코프를 “포획”**하고, 그 포획된 스코프를 통해 변수에 접근하는 것이 클로저입니다.

## # 1-3. 왜 클로저가 필요한가?

- **데이터 은닉** : 외부에서 직접 접근할 수 없는 비공개 변수 생성

- **상태 유지** : 함수 호출 사이에 값을 유지(예: 카운터)

- **함수 팩토리** : 매개변수에 따라 다른 동작을 하는 함수를 동적으로 생성

---

# # 2️⃣ 클로저의 작동 원리 – 단계별 시각화

## # 2-1. 실행 컨텍스트와 변수 환경 레코드

1. **함수 선언** → 실행 컨텍스트가 생성되고, **변수 환경 레코드(Variable Environment Record)** 가 만든다.

2. 함수가 **호출**될 때마다 새로운 실행 컨텍스트가 스택에 쌓인다.

3. 함수가 **종료**되면 실행 컨텍스트는 사라지지만, **클로저가 참조**하고 있는 변수 환경 레코드는 가비지 컬렉션되지 않는다.

## # 2-2. 예제: 간단한 카운터 만들기

```
javascript

function makeCounter() {

  let count = 0;               // 외부 변수

  return function() {         // 내부 함수 (클로저)

    count += 1;

    return count;

  };

}

const counter = makeCounter(); // makeCounter 실행 → 내부 함수 반환

console.log(counter()); // 1

console.log(counter()); // 2

```

- `makeCounter` 실행이 끝나도 `count` 변수는 **소멸되지 않는다**.

- 반환된 내부 함수는 `count`를 **포획**하고 있기 때문에, 매 호출 시 이전 값을 기억한다.

## # 2-3. 클로저와 메모리 관리

- 클로저가 **불필요하게 큰 객체**를 포획하면 메모리 누수가 발생할 수 있다.

- 사용이 끝난 클로저는 **null** 로 할당하거나, **스코프를 제한**해 메모리를 해제한다.

---

# # 3️⃣ 실전 예제 – 비동기와 콜백에서 클로저 활용하기

## # 3-1. 타이머와 클로저

```
javascript

function startTimer(seconds) {

  let remaining = seconds;

  const id = setInterval(function() {

    remaining--;

    console.log(`남은 시간: ${remaining}s`);

    if (remaining <= 0) clearInterval(id);

  }, 1000);

}

startTimer(5);

```

- `setInterval` 내부 콜백은 `remaining` 변수를 **클로저**로 포획한다.

- 타이머가 진행되는 동안 `remaining` 값은 지속적으로 업데이트된다.

## # 3-2. 이벤트 리스너와 클로저

```
javascript

function attachClickHandler(btnId) {

  let clickCount = 0;

  document.getElementBy **getElementById**(btnId).addEventListener('click', function() {

    clickCount++;

    console.log(`버튼 클릭 횟수: ${clickCount}`);

  });

}
attachClickHandler('myBtn');

```

- 클릭 이벤트 콜백이 `clickCount`를 기억하므로, 버튼을 누를 때마다 누적된 횟수를 출력한다.

## # 3-3. 클로저를 이용한 모듈 패턴

```
javascript

const TodoModule = (function() {

  let todos = []; // 비공개 변수

  return {

    add: function(item) { todos.push(item); },

    list: function() { return [...todos]; }

  };

})();

TodoModule.add('Learn JavaScript');

console.log(TodoModule.list()); // ['Learn JavaScript']

```

- 즉시 실행 함수(IIFE) 안에 **비공개 데이터** `todos`를 두고, 외부에 공개된 메서드만 반환한다.

- 이 패턴은 **데이터 은닉**과 **API 제공**을 동시에 만족한다.

---

# # 4️⃣ 클로저 흔히 하는 실수와 해결법

| 흔한 실수 | 원인 | 해결 방법 |

|----------|------|-----------|

| **루프 안에서 클로저 사용 시 변수 공유** | `var` 로 선언된 변수는 함수 스코프라서 마지막 값만 기억 | `let` 사용하거나 즉시 실행 함수를 활용 |

| **메모리 누수** | 클로저가 큰 DOM 객체 등을 포획 | 필요 없는 참조는 `null` 로 초기화, 혹은 스코프 최소화 |

| **디버깅 어려움** | 클로저 내부 변수는 콘솔에 바로 보이지 않음 | 브라우저 DevTools의 **Closure Scope** 확인 기능 활용 |

```
javascript

// var 로 인한 문제 예시

for (var i = 0; i < 3; i++) {

  setTimeout(function() {

    console.log(i); // 3, 3, 3

  }, 100);

}

// let 로 해결

for (let i = 0; i < 3; i++) {

  setTimeout(function() {

    console.log(i); // 0, 1, 2

  }, 100);

}

```

---

# # 5️⃣ 클로저와 최신 JavaScript (ES6+)

- **arrow function** 은 `this` 바인딩이 lexical이지만, **클로저와는 별개**이다.

- **module** (ES6 `import`/`export`) 은 내부적으로 클로저를 활용해 **프라이빗 스코프**를 만든다.

- **async/await** 와 **Promise** 도 콜백 대신 클로저를 이용해 비동기 흐름을 관리한다.

```
javascript

// async 함수 안에서 클로저 활용

async function fetchData(url) {

  const response = await fetch(url);

  const data = await response.json();

  return function() {

    console.log('받은 데이터:', data); // data는 클로저에 포획

  };

}
const logData = await fetchData('/api/items');

logData(); // 콘솔에 데이터 출력

```

---

# # 결론 – 지금 바로 클로저를 활용해 보세요!

클로저는 **JavaScript 프로그래밍 기초** 중에서도 가장 강력하면서도 헷갈리기 쉬운 개념입니다.

하지만 위에서 살펴본 **스코프 이해 → 변수 환경 포획 → 실전 적용** 순서를 따라가면, 어느새 **데이터 은닉**, **상태 유지**, **비동기 제어** 등 다양한 상황에서 클로저를 자연스럽게 활용하고 있을 겁니다.

## # 📌 행동 유도

1. **직접 실습** – 오늘 배운 카운터 예제를 콘솔에 입력하고 동작을 확인해 보세요.

2. **코드 리팩터링** – 기존 프로젝트에 콜백이 많은 부분을 클로저 기반 모듈로 바꿔 보세요.

3. **블로그 공유** – 이 글이 도움이 되었다면 SNS에 공유하고, #JavaScript클로저 해시태그와 함께 의견을 남겨 주세요!

클로저를 제대로 이해하고 활용한다면, **복잡한 로직도 깔끔하게 관리**하고 **버그를 크게 줄일** 수 있습니다. 이제 여러분도 JavaScript의 비밀스러운 기억력을 손에 넣으세요! 🚀
