레슨 9 / 12·5개 토픽
클로저와 스코프
스코프(Scope)의 종류
JavaScript의 스코프는 변수에 접근할 수 있는 범위를 의미합니다. var는 함수 스코프, let과 const는 블록 스코프를 가집니다. 렉시컬 스코프(Lexical Scope)란 함수가 정의된 위치를 기준으로 상위 스코프가 결정되는 것을 말합니다.
javascript
// 블록 스코프 vs 함수 스코프
if (true) {
var varX = 10; // 함수 스코프 — 블록 밖에서도 접근 가능
let letY = 20; // 블록 스코프 — 블록 안에서만 접근 가능
const constZ = 30;
}
console.log(varX); // 10
// console.log(letY); // ReferenceError
// console.log(constZ); // ReferenceError
// 렉시컬 스코프 — 함수가 정의된 위치 기준
const outer = "전역";
function outerFn() {
const middle = "중간";
function innerFn() {
const inner = "내부";
console.log(outer); // "전역" — 렉시컬 스코프 체인
console.log(middle); // "중간"
console.log(inner); // "내부"
}
innerFn();
}
outerFn();클로저(Closure)란?
클로저는 함수가 자신이 정의된 환경(렉시컬 환경)을 기억하는 것입니다. 외부 함수가 실행을 마친 후에도, 내부 함수는 외부 함수의 변수에 접근할 수 있습니다. 이를 통해 데이터를 캡슐화하고 상태를 유지할 수 있습니다.
javascript
// 카운터 — 클로저로 private 상태 만들기
function createCounter() {
let count = 0; // 외부에서 직접 접근 불가
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; },
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount()); // 1
// console.log(count); // ReferenceError — 외부에서 접근 불가
// private 변수 패턴
function createUser(name) {
let _password = ""; // 클로저로 보호
return {
getName() { return name; },
setPassword(pw) { _password = pw; },
checkPassword(pw) { return _password === pw; },
};
}
const user = createUser("김철수");
user.setPassword("1234");
console.log(user.checkPassword("1234")); // true
console.log(user.checkPassword("0000")); // false클로저 실전 활용
javascript
// 이벤트 핸들러에서 클로저 활용
function setupButtons() {
const colors = ["red", "green", "blue"];
colors.forEach(function(color) {
const btn = document.createElement("button");
btn.textContent = color;
// color 변수가 클로저로 캡처됨
btn.addEventListener("click", function() {
document.body.style.backgroundColor = color;
});
document.body.appendChild(btn);
});
}
// 함수 팩토리 — 클로저로 설정값 기억
function createMultiplier(factor) {
return function(number) {
return number * factor; // factor를 클로저로 기억
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15IIFE (즉시 실행 함수 표현식)
IIFE(Immediately Invoked Function Expression)는 정의와 동시에 실행되는 함수입니다. 전역 스코프를 오염시키지 않고 독립적인 스코프를 만들 때 사용합니다.
javascript
// 기본 IIFE 패턴
(function() {
const secret = "IIFE 안에서만 접근 가능";
console.log(secret);
})();
// console.log(secret); // ReferenceError
// 값을 반환하는 IIFE
const module = (function() {
let privateData = 0;
return {
getData() { return privateData; },
setData(val) { privateData = val; },
};
})();
module.setData(42);
console.log(module.getData()); // 42클로저의 흔한 함정: 반복문과 변수
javascript
// 문제: var는 함수 스코프 → 모든 콜백이 같은 i를 참조
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // 3, 3, 3 (모두 3 출력)
}, 100);
}
// 해결 1: let 사용 (블록 스코프)
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // 0, 1, 2 (정상 출력)
}, 100);
}
// 해결 2: IIFE로 각 반복마다 새 스코프 생성
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 0, 1, 2
}, 100);
})(i);
}💡
클로저는 메모리에 렉시컬 환경을 유지하므로, 불필요한 클로저는 메모리 누수를 일으킬 수 있습니다. 이벤트 리스너를 제거하거나 참조를 null로 설정하여 가비지 컬렉션이 되도록 해주세요.