클로저(Closure)는 함수와 그 함수가 선언된 렉시컬 환경(Lexical Environment)을 함께 묶은 구조다. 내부 함수가 외부 함수의 지역 변수에 접근하고, 외부 함수 실행 후에도 해당 변수를 유지한다.
기본 개념
python
def make_counter(start=0):
count = start # 외부 함수의 지역 변수
def increment(): # 내부 함수 (클로저)
nonlocal count
count += 1
return count
return increment # 함수 객체 반환
counter = make_counter(10)
print(counter()) # 11 ← make_counter 종료 후에도 count 기억
print(counter()) # 12
print(counter()) # 13
# 독립적인 카운터 생성 가능
counter2 = make_counter(100)
print(counter2()) # 101 (counter와 독립)
javascript
function createMultiplier(factor) {
return (num) => num * factor; // factor를 클로저로 캡처
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 모듈 패턴 — 캡슐화
function createBank(initial) {
let balance = initial; // private (클로저로 보호)
return {
deposit: (amt) => { balance += amt; },
withdraw: (amt) => { balance -= amt; },
getBalance: () => balance
};
}
const account = createBank(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// balance에 직접 접근 불가
루프와 클로저 (흔한 실수)
javascript
// 문제: 클로저가 같은 변수를 공유
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);
}
메모리 고려
클로저가 외부 변수를 참조하는 한 해당 변수는 GC되지 않는다. 불필요한 대형 객체를 클로저로 캡처하면 메모리 누수 발생 가능.
관련 개념