# JavaScript 클로저 완전 정복: 함수가 기억하는 비밀을 파헤치다 (50-60자)

---

# # 서론 – “함수가 왜 기억을 가지고 있을까?”

프로그래밍을 처음 시작한 초보 개발자라면, **함수**는 “입력값을 받아서 결과를 반환한다”는 간단한 개념만 떠올릴 것입니다. 그런데 실제 프로젝트에서 **함수가 이전에 만든 변수나 상태를 기억하고** 있다는 이야기를 들으면 “그게 어떻게 가능한가?” 하는 의문이 생깁니다. 바로 이때 등장하는 것이 **JavaScript 클로저(Closure)** 입니다.

클로저는 JavaScript의 **함수 스코프**와 **렉시컬 스코프** 개념을 기반으로 동작하는 강력한 메커니즘으로, 코드 재사용성·비동기 처리·데이터 은닉 등 다양한 상황에서 핵심적인 역할을 합니다. 이번 글에서는 *Chapter 4: Functions Remember* 라는 주제에 맞춰, 클로저가 어떻게 작동하고, 실무에서 어떻게 활용되는지 초보자도 쉽게 이해할 수 있도록 단계별로 풀어보겠습니다.

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

---

# # 본론

## # 1️⃣ 함수 스코프와 렉시컬 스코프 – 기억의 기반을 이해하라

### # 1-1. 함수 스코프란?

JavaScript는 **함수 단위**로 변수를 구분합니다. 함수 내부에서 선언된 변수는 그 함수 안에서만 접근 가능하고, 외부에서는 접근할 수 없습니다. 이를 **함수 스코프**라고 합니다.

```
js

function outer() {

  let message = 'Hello, world!';

  function inner() {

    console.log(message); // outer의 변수에 접근 가능

  }

  inner();

}
outer(); // 'Hello, world!'

```

위 예시에서 `inner` 함수는 `outer` 함수의 `message` 변수에 접근합니다. 이때 `inner`가 `message`를 **기억**하고 있다는 것이 바로 클로저의 시작점입니다.

### # 1-2. 렉시컬 스코프(Lexical Scope)

JavaScript는 **코드가 작성된 위치**에 따라 스코프 체인을 결정합니다. 즉, 함수가 **어디서 정의**되었는지가 실행 시점에 중요한 역할을 합니다. 이를 **렉시컬 스코프**라고 부릅니다.

- **정의 시점**에 스코프가 고정 → 실행 시점에 동적으로 변하지 않음

- 외부 함수의 변수에 접근할 수 있는 이유는 **정적 스코프 체인** 덕분

> **정리**: 함수 스코프와 렉시컬 스코프가 결합돼, 내부 함수가 외부 변수들을 “기억”하게 되는 구조가 바로 **클로저**입니다.

---

## # 2️⃣ 클로저란 무엇인가? – 함수가 기억을 저장하는 메커니즘

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

**클로저(Closure)**는 *함수가 선언될 때의 렉시컬 환경*을 **함수와 함께 저장**한 객체입니다. 이 환경에는 함수가 접근할 수 있는 모든 외부 변수와 매개변수가 포함됩니다.

> **핵심**: 함수가 실행이 끝난 뒤에도 그 함수가 참조하고 있던 외부 변수들은 가비지 컬렉터에 의해 바로 사라지지 않고, 클로저에 의해 살아남습니다.

### # 2-2. 클로저가 동작하는 흐름

1. **함수 선언** → 렉시컬 환경(스코프 체인) 생성

2. **함수 호출** → 내부 로직 실행

3. **함수 반환** → 반환값이 내부 함수를 포함하면, 그 내부 함수는 외부 변수에 대한 **참조**를 유지

4. **외부 변수는 메모리 상에 남아** → 이후 호출 시에도 동일한 값 사용 가능

### # 2-3. 간단한 예시 – 카운터 만들기

```
js

function createCounter() {

  let count = 0;               // 외부 변수

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

    count += 1;

    return count;

  };

}

const counter = createCounter(); // 클로저가 반환된 상태

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

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

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

```

`createCounter` 함수가 끝난 뒤에도 `count` 변수는 메모리에 남아 있습니다. `counter` 함수가 호출될 때마다 같은 `count`를 **계속해서 기억**하고 업데이트합니다.

---

## # 3️⃣ 클로저 활용 사례 – 실전에서 어떻게 쓰일까?

### # 3-1. 데이터 은닉 및 프라이버시

클로저를 이용하면 **공개/비공개** 속성을 구분할 수 있습니다. 객체 지향 언어에서 `private` 키워드가 없는 JavaScript에서도 클로저를 활용해 비공개 변수를 만들 수 있습니다.

```
js

function Person(name) {

  let _age = 0; // 비공개 변수

  this.getName = function() { return name; };

  this.getAge = function() { return _age; };

  this.setAge = function(age) {

    if (age >= 0) _age = age;

  };

}
const tom = new Person('Tom');

tom.setAge(25);

console.log(tom.getAge()); // 25

// console.log(tom._age); // undefined (외부에서 직접 접근 불가)

```

### # 3-2. 비동기 처리와 콜백 함수

`setTimeout`, `fetch`와 같은 비동기 함수에서 클로저는 **시점에 따른 값 보존**에 유용합니다.

```
js

for (let i = 1; i <= 5; i++) {

  setTimeout(function() {

    console.log(`Timer ${i}`); // 각각 다른 i 값을 기억

  }, i * 1000);

}

```

`let`을 사용해 블록 스코프를 만들었지만, `var`를 사용할 경우 클로저를 이용해 현재 루프 인덱스를 기억하도록 해야 합니다.

### # 3-3. 함수형 프로그래밍과 부분 적용(Partial Application)

클로저는 **함수형 프로그래밍**에서 자주 쓰이는 `partial` 혹은 `curry` 패턴을 구현하는 데 필수적입니다.

```
js

function multiply(a) {

  return function(b) {

    return a * b;

  };

}
const double = multiply(2);

console.log(double(5)); // 10

```

`multiply` 함수는 첫 번째 인자를 기억하고, 반환된 내부 함수가 두 번째 인자를 받아 최종 결과를 반환합니다.

---

## # 4️⃣ 클로저 사용 시 주의점 – 메모리 누수와 성능

### # 4-1. 메모리 누수 위험

클로저가 외부 변수를 계속 참조하면, 그 변수는 가비지 컬렉션 대상이 되지 않아 **메모리 누수**가 발생할 수 있습니다. 특히 큰 객체를 클로저 안에 넣고 오래된 참조를 유지하면 애플리케이션 성능에 악영향을 줍니다.

**예방 방법**

- 필요 없는 클로저는 **null** 또는 **undefined** 로 초기화

- 전역 변수 대신 **지역 변수** 사용

- 클로저 내부에서 큰 객체를 직접 다루지 말고 **복사본**을 사용

### # 4-2. 성능 최적화 팁

- **한 번만 생성**되는 클로저는 재사용하도록 설계

- 반복문 안에서 클로저를 만들 때는 **불필요한 생성**을 최소화

- 최신 브라우저는 클로저 최적화가 잘 되어 있지만, 오래된 환경을 지원해야 한다면 **테스트**를 반드시 수행

---

## # 5️⃣ 클로저와 최신 JavaScript 문법 – `let`, `const`, 화살표 함수

### # 5-1. `let`·`const`와 클로저

ES6부터 도입된 `let`·`const`는 **블록 스코프**를 제공해 클로저와 함께 사용할 때 변수 캡처가 더 직관적입니다. 위의 `for` 루프 예시처럼 `let`을 쓰면 각 반복마다 새로운 스코프가 생성돼 클로저가 올바른 값을 기억합니다.

### # 5-2. 화살표 함수와 `this` 바인딩

화살표 함수는 **lexical this**를 가지므로, 일반 함수와 달리 `this`가 클로저에 의해 자동으로 바인딩됩니다. 따라서 객체 메서드 내부에서 콜백을 사용할 때 유용합니다.

```
js

class Timer {

  constructor() {

    this.seconds = 0;

  }

  start() {

    setInterval(() => {

      this.seconds++;

      console.log(this.seconds);

    }, 1000);

  }

}
new Timer().start(); // 정상적으로 this가 바인딩됨

```

---

# # 결론 – 클로저를 활용해 더 스마트한 코드 작성하기

JavaScript 클로저는 **함수가 기억을 갖는 비밀**이라고 할 수 있습니다. 함수 스코프와 렉시컬 스코프가 결합돼, 외부 변수에 대한 **영구적인 접근 권한**을 제공함으로써 데이터 은닉, 비동기 처리, 함수형 프로그래밍 등 다양한 상황에서 강력한 도구가 됩니다.

하지만 클로저를 남용하면 메모리 누수와 성능 저하 위험이 있으니, **필요한 경우에만** 적절히 사용하고, 사용 후에는 참조를 해제하는 습관을 들이는 것이 중요합니다.

## # 📢 행동 유도 (Call to Action)

1. **직접 실습해 보기** – 위에서 소개한 카운터 예제와 데이터 은닉 코드를 코드 에디터에 복붙해 실행해 보세요.

2. **프로젝트에 적용** – 현재 진행 중인 작은 프로젝트(예: Todo 앱)에서 클로저를 활용해 상태 관리 로직을 구현해 보세요.

3. **블로그 구독 & 댓글** – 클로저와 관련해 궁금한 점이나 직접 겪은 에러 사례가 있다면 댓글로 공유해 주세요! 다음 포스트에서는 **클로저와 메모리 관리**를 심층 탐구합니다.

클로저를 제대로 이해하고 활용한다면, 당신의 JavaScript 실력이 한 단계 성장할 것입니다. 지금 바로 코드를 작성하고, “함수가 기억한다”는 마법을 직접 체험해 보세요! 🚀
