전체 목록
JavaScriptMedium#01

클로저(Closure)란 무엇이며, 실제 코드에서 어떻게 활용할 수 있나요?

#JS#함수#스코프#핵심개념
힌트

함수가 선언될 때의 렉시컬 환경을 기억하는 특성을 생각해보세요.

정답 및 해설

클로저(Closure)란 무엇이며, 실제 코드에서 어떻게 활용할 수 있나요?

클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합입니다. 내부 함수가 외부 함수의 스코프에 있는 변수를 참조할 수 있고, 외부 함수의 실행이 끝난 후에도 그 참조가 유지됩니다.

핵심 개념

JavaScript에서 함수는 생성될 때 자신이 선언된 렉시컬 환경(Lexical Environment) 을 기억합니다. 이 환경에는 함수 내부에서 접근 가능한 변수들이 담겨 있습니다.

function outer() {
  const count = 0; // 외부 함수의 변수

  function inner() {
    console.log(count); // 외부 변수에 접근
  }

  return inner;
}

const fn = outer(); // outer()는 이미 종료됐지만
fn(); // 0 — count 변수가 여전히 살아있다

outer()가 실행을 마쳐도 innercount를 참조하고 있으므로, count는 가비지 컬렉션 대상이 되지 않습니다.

주요 활용 패턴

1. 데이터 은닉 (Private 변수)

function createCounter() {
  let count = 0; // 외부에서 직접 접근 불가

  return {
    increment() { count++; },
    decrement() { count--; },
    getCount() { return count; },
  };
}

const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 2
console.log(counter.count);      // undefined — 접근 불가

2. 팩토리 함수

function multiplier(factor) {
  return (number) => number * factor;
}

const double = multiplier(2);
const triple = multiplier(3);

double(5); // 10
triple(5); // 15

3. 메모이제이션

function memoize(fn) {
  const cache = new Map();

  return function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);

    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const expensiveCalc = memoize((n) => n * n);
expensiveCalc(10); // 계산 후 캐시
expensiveCalc(10); // 캐시에서 반환

4. 부분 적용 (Partial Application)

function add(a, b) {
  return a + b;
}

function partial(fn, ...presetArgs) {
  return (...laterArgs) => fn(...presetArgs, ...laterArgs);
}

const add5 = partial(add, 5);
add5(3); // 8
add5(10); // 15

주의사항

클로저를 반복문과 함께 사용할 때 흔히 실수하는 패턴이 있습니다.

// 문제 — var는 함수 스코프이므로 모두 같은 i를 참조
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 3, 3, 3
}

// 해결 1 — let (블록 스코프)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

// 해결 2 — IIFE로 클로저 생성
for (var i = 0; i < 3; i++) {
  ((j) => setTimeout(() => console.log(j), 100))(i); // 0, 1, 2
}

정리

활용설명
데이터 은닉모듈 패턴으로 private 변수 구현
팩토리 함수설정을 기억하는 함수 생성
메모이제이션이전 계산 결과 캐싱
부분 적용인자를 미리 고정한 함수 생성
이벤트 핸들러컨텍스트 유지