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()가 실행을 마쳐도 inner가 count를 참조하고 있으므로, 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 변수 구현 |
| 팩토리 함수 | 설정을 기억하는 함수 생성 |
| 메모이제이션 | 이전 계산 결과 캐싱 |
| 부분 적용 | 인자를 미리 고정한 함수 생성 |
| 이벤트 핸들러 | 컨텍스트 유지 |