Skip to main content

JavaScript 클로저 완전 정복 – 함수가 기억하는 비밀을 파헤쳐 보자 (58자)

Published 4 min readMarkdown

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

프로그래밍을 처음 시작한 사람들은 함수를 “입력값을 받아 결과를 반환하는 작은 기계”라고만 생각합니다. 그런데 JavaScript에서는 함수가 자신이 선언될 때의 환경을 기억하고, 그 기억을 바탕으로 동작하는 클로저(Closure) 라는 특별한 메커니즘이 존재합니다.

이 글에서는 Chapter 4: Functions Remember 라는 주제에 맞춰, JavaScript 클로저가 무엇인지, 왜 필요한지, 그리고 실제 예제로 어떻게 활용할 수 있는지를 초보자도 쉽게 이해할 수 있도록 단계별로 설명합니다.

SEO 키워드 : JavaScript 클로저, 함수 기억, 스코프


# 1️⃣ 스코프와 클로저 – 기본 개념 잡기

| 용어 | 의미 |

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

| 스코프(Scope) | 변수가 접근 가능한 영역. 전역·함수·블록 스코프 등 |

| 렉시컬 스코프(Lexical Scope) | 함수는 정의된 위치의 스코프를 기억한다. |

| 클로저(Closure) | 함수가 외부 스코프에 있는 변수들을 함께 기억하고, 나중에 실행될 때도 그 변수에 접근할 수 있는 현상 |

간단한 예시

js

function makeCounter() {

  let count = 0;               // 외부 스코프 변수

  return function() {         // 내부 함수가 외부 변수에 접근

    count += 1;

    return count;

  };

}

const counter = makeCounter(); // 클로저가 생성

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

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

makeCounter가 실행된 뒤에도 count 변수는 사라지지 않고, 반환된 함수가 그 값을 기억합니다. 이것이 바로 함수가 기억하는 메커니즘, 즉 클로저입니다.


# 2️⃣ 클로저가 필요한 이유 – 데이터 은닉과 상태 유지

# 2-1. 비공개 변수 만들기

클로저를 이용하면 외부에서 직접 접근할 수 없는 변수를 만들 수 있습니다. 이는 객체 지향 언어의 private 속성과 비슷한 효과를 줍니다.

js

function createSecret() {

  let secret = '🔒 비밀';

  return {

    reveal() { return secret; },

    change(newVal) { secret = newVal; }

  };

}
const box = createSecret();

console.log(box.reveal()); // 🔒 비밀

box.change('새 비밀');

console.log(box.reveal()); // 새 비밀

# 2-2. 비동기 로직에서 상태 유지

버튼 클릭, 타이머, API 콜 등 비동기 상황에서도 클로저는 변수를 살아 있게 합니다.

js

function startTimer() {

  let seconds = 0;

  setInterval(() => {

    seconds++;

    console.log(`경과 시간: ${seconds}s`);

  }, 1000);

}
startTimer(); // 매초 증가하는 seconds를 클로저가 기억

# 3️⃣ 클로저 사용 시 주의할 점 – 메모리 누수 방지

클로저가 불필요하게 큰 객체를 참조하면 가비지 컬렉터가 메모리를 회수하지 못해 메모리 누수가 발생할 수 있습니다.

| 방지 방법 | 설명 |

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

| 필요 없는 레퍼런스 제거 | 사용이 끝난 변수는 null 혹은 undefined 로 초기화 |

| 스코프 최소화 | 클로저를 만들 때 가능한 한 작은 범위 안에서 선언 |

| WeakMap 활용 | 객체를 키로 사용할 때 메모리 회수를 돕는다 |

js

function heavyTask() {

  const largeObj = { /* 수백 MB 데이터 */ };

  return function() {

    // 작업 수행 후 레퍼런스 해제

    console.log('작업 완료');

    // largeObj = null; // ← 이렇게 해주면 메모리 회수 가능

  };

}

# 4️⃣ 실전 활용 사례 – 모듈 패턴 & 커링

# 4‑1. 모듈 패턴 (Module Pattern)

클로저를 이용해 비공개 데이터와 공개 API를 한 파일에 깔끔하게 구성할 수 있습니다.

js

const TodoModule = (function() {

  let todos = []; // 비공개

  return {

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

    list() { console.log(todos); }

  };

})();

TodoModule.add('JavaScript 공부');

TodoModule.list(); // ['JavaScript 공부']

# 4‑2. 커링 (Currying)

함수 호출 시 일부 인자를 미리 채워 두고, 나중에 남은 인자를 받아 실행하는 기법도 클로저 덕분에 가능합니다.

js

function multiply(a) {

  return function(b) {

    return a * b;

  };

}
const double = multiply(2);

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

# 5️⃣ 클로저 디버깅 팁 – 초보자를 위한 체크리스트

  1. 변수 선언 위치 확인 – 클로저가 참조하는 변수가 어디서 선언됐는지 파악한다.

  2. 콘솔 로그 활용 – 클로저 내부와 외부에서 변수 값을 출력해 흐름을 눈으로 확인한다.

  3. 메모리 탭 확인 – 브라우저 개발자 도구 → Memory → Heap Snapshot을 통해 누수 여부를 검사한다.

  4. 테스트 코드 작성 – Jest·Mocha 등으로 클로저가 기대한 대로 동작하는지 자동 검증한다.

js

test('counter increments correctly', () => {

  const counter = makeCounter();

  expect(counter()).toBe(1);

  expect(counter()).toBe(2);

});

# 결론 – 오늘부터 클로저를 활용해 보세요!

  • 클로저는 함수가 외부 스코프를 기억하고, 이를 통해 데이터 은닉, 상태 유지, 코드 재사용을 가능하게 하는 강력한 도구입니다.

  • 스코프와 렉시컬 환경을 정확히 이해하고, 우선순위와 메모리 관리를 염두에 두면 안전하게 사용할 수 있습니다.

실습 과제

  1. 위 예시 makeCounter를 직접 구현하고, 콘솔에 5번 호출해 보세요.

  2. TodoModule을 변형해 완료된 아이템만 필터링하는 메서드를 추가해 보세요.

클로저를 제대로 활용하면 코드가 더 깔끔해지고, 버그 발생 가능성이 낮아집니다. 오늘 배운 내용을 바탕으로 직접 코드를 짜보고, 질문이 있으면 댓글에 남겨 주세요. 함께 성장하는 개발 커뮤니티가 되길 바랍니다!


SEO 키워드 : JavaScript 클로저, 함수 기억, 스코프

참고 원문 : The Secret Life of JavaScript: Understanding Closures

No comments yet. Be the first to comment.

More from this blog