🔍 JavaScript Scope & Closure 시뮬레이터
클로저와 스코프 체인을 통해 변수 캡처 메커니즘을 시각적으로 학습해보세요
⏳ 실행 중인 setTimeout0개
📋 실행 로그0개
🚨 문제 시연: 빠르게 버튼 누르기
실험해보세요: "올바른 방법 사용" 체크박스를 해제하고 "1초 후 증가" 버튼을 빠르게 여러 번 클릭해보세요!
예상: 클릭한 만큼 증가 | 실제: 1만 증가하는 문제 발생
❌ 잘못된 방법
setTimeout 콜백에서 캡처된 count 값을 사용하면, 모든 콜백이 동일한 값을 참조하게 됩니다.
✅ 올바른 방법
함수형 업데이트를 사용하면 최신 상태를 기반으로 올바르게 증가합니다.
📚 학습 가이드: JavaScript Scope & Closure
실제 코드와 함께 클로저와 스코프의 작동 원리를 이해해보세요
❌ 문제가 있는 코드
const delayedIncrement = () => {
const currentCount = count; // 현재 값 캡처
setTimeout(() => {
// 잘못된 방법: 캡처된 값 사용
const newValue = currentCount + 1;
setCount(newValue);
}, 1000);
};
// 빠르게 3번 클릭하면:
// 모든 setTimeout이 동일한 count 값을 캡처
// 결과: 모두 같은 값으로 업데이트 (1만 증가)✅ 올바른 해결 코드
const delayedIncrement = () => {
setTimeout(() => {
// 올바른 방법: 함수형 업데이트 사용
setCount(prev => prev + 1);
}, 1000);
};
// 빠르게 3번 클릭하면:
// 각 setTimeout이 실행될 때마다
// 최신 상태를 기반으로 업데이트
// 결과: 정확히 3만큼 증가🧠 핵심 개념 이해
1. 클로저 (Closure)
함수가 생성될 때 외부 변수에 대한 참조를 "기억"하는 메커니즘입니다. setTimeout 콜백은 클로저를 통해 count 변수를 캡처합니다.
2. 변수 캡처
setTimeout이 생성되는 순간의 count 값이 캡처되어, 나중에 실행될 때도 그 값을 사용하게 됩니다.
3. 함수형 업데이트
setCount(prev => prev + 1)을 사용하면 실행 시점의 최신 상태를 받아서 업데이트하므로 클로저 문제를 해결할 수 있습니다.
4. 스코프 체인
JavaScript는 변수를 찾을 때 현재 스코프부터 시작해서 외부 스코프로 올라가며 검색합니다.
💡 클로저 패턴을 사용하는 이유
🔒 1. 데이터 캡슐화
외부에서 직접 접근할 수 없는 프라이빗 변수를 만들 수 있습니다.
function createCounter() {
let count = 0; // 프라이빗 변수
return {
increment: () => ++count,
getCount: () => count
};
}🏭 2. 팩토리 함수
설정값을 기억하는 맞춤형 함수를 생성할 수 있습니다.
function createMultiplier(factor) {
return (num) => num * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);🔧 3. 모듈 패턴
네임스페이스를 만들고 전역 변수 오염을 방지할 수 있습니다.
const MyModule = (() => {
let privateVar = 'hidden';
return {
publicMethod: () => privateVar,
setPrivate: (val) => privateVar = val
};
})();⚡ 4. 콜백 컨텍스트 유지
이벤트 핸들러나 비동기 함수에서 특정 값을 기억할 수 있습니다.
buttons.forEach((btn, index) => {
btn.onClick = () => {
console.log(`Button ${index} clicked`);
};
});🌟 실제 개발에서의 클로저 활용 예시
디바운스 함수
검색 입력이나 리사이즈 이벤트에서 자주 사용됩니다.
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
const debouncedSearch = debounce(searchAPI, 300);메모이제이션
계산 결과를 캐싱하여 성능을 최적화할 수 있습니다.
function memoize(func) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = func.apply(this, args);
cache[key] = result;
return result;
};
}
const memoizedFib = memoize(fibonacci);💡 주의사항
- • 클로저는 메모리를 계속 참조하므로 메모리 누수에 주의해야 합니다.
- • 순환 참조를 만들지 않도록 조심해야 합니다.
- • 과도한 클로저 사용은 성능에 영향을 줄 수 있습니다.
- • 디버깅이 어려울 수 있으므로 명확한 네이밍이 중요합니다.
1nnovator 김민성
JavaScript 학습 센터 개발자
🌟 이 프로젝트가 도움이 되셨다면 블로그에서 더 많은 개발 이야기를 확인해보세요!
🤖이 페이지는 생성형 AI의 도움을 받아 제작되었습니다.