[프론트엔드] 자바스크립트 클로저
📌 JavaScript 클로저(Closure) 자세히 알아보기
1️⃣ 클로저란?
클로저(Closure)는 "함수와 그 함수가 선언된 렉시컬 환경을 함께 기억하는 구조".
즉, 함수가 실행된 후에도 해당 함수의 지역 변수에 접근할 수 있는 개념.
📌 쉽게 말하면
"함수 안에서 선언된 함수가, 바깥 함수의 변수들을 기억하고 사용할 수 있도록 하는 것"
2️⃣ 클로저를 이해하는 기본 개념
🔹 렉시컬 스코프 (Lexical Scope)
자바스크립트는 렉시컬 스코프(정적 스코프) 를 사용해!
즉, 함수가 선언된 위치에서 변수를 찾는다. (실행 위치가 아니라 선언 위치 기준)
function outer() {
let outerVar = "나는 바깥 변수!";
function inner() {
console.log(outerVar); // "나는 바깥 변수!"
}
inner();
}
outer();
✅ inner() 함수는 outerVar 변수를 자신의 내부에서 선언하지 않았지만
✅ outerVar가 선언된 위치(렉시컬 환경)를 기억하고 접근 가능
3️⃣ 클로저의 핵심 기능
✅ 1. 데이터 은닉 (Encapsulation)
클로저를 이용하면 외부에서 직접 접근할 수 없는 변수를 만들 수 있어.
즉, 변수를 외부에서 변경하지 못하게 보호하는 것!
function createCounter() {
let count = 0; // 외부에서 접근 불가능한 변수
return {
increase: () => count++,
decrease: () => count--,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increase();
console.log(counter.getCount()); // 1
counter.decrease();
console.log(counter.getCount()); // 0
✅ count 변수는 createCounter() 내부에 있지만,
✅ increase, decrease, getCount를 통해 간접적으로 접근 가능
✅ 외부에서 count를 직접 변경할 수 없음! (데이터 은닉)
✅ 2. 상태 유지 (State Maintenance)
클로저를 사용하면, 함수가 실행된 후에도 내부 변수를 유지할 수 있어
function makeAdder(x) {
return function(y) {
return x + y;
};
}
const add5 = makeAdder(5); // x = 5 유지
console.log(add5(2)); // 7
console.log(add5(10)); // 15
const add10 = makeAdder(10); // x = 10 유지
console.log(add10(5)); // 15
✅ add5는 makeAdder(5) 호출 시 x = 5를 기억
✅ add10은 makeAdder(10) 호출 시 x = 10을 기억
✅ 각각 독립적인 상태를 유지하면서 동작
✅ 3. 비동기 처리 및 콜백 함수에서 유용
클로저는 이벤트 리스너, setTimeout, 비동기 처리에서 매우 유용해!
function delayedMessage(message, delay) {
setTimeout(() => {
console.log(message);
}, delay);
}
delayedMessage("3초 후 출력!", 3000);
✅ 클로저 덕분에 message 변수값이 유지되고,
✅ 3초 뒤에 "3초 후 출력!"이 출력됨
4️⃣ 클로저의 메모리 관리
❗ 클로저가 참조하는 변수만 메모리에 유지됨
- 함수 실행이 끝나도 클로저가 있는 한 해당 변수는 메모리에 남아있음.
- 하지만 더 이상 참조되지 않으면 GC(가비지 컬렉터)에 의해 자동 해제됨
function outer() {
let largeData = new Array(1000000).fill("📦"); // 메모리를 많이 차지하는 데이터
return function inner() {
console.log(largeData[0]);
};
}
const myFunction = outer(); // 클로저 생성
myFunction(); // "📦"
// 클로저를 더 이상 참조하지 않으면 메모리 해제됨
myFunction = null;
✅ myFunction = null; 하면 largeData도 더 이상 참조되지 않아 메모리에서 해제됨.
5️⃣ 클로저를 활용한 실전 예제
📌 1. 이벤트 리스너에서 사용
function attachEventHandlers() {
let count = 0;
document.getElementById("btn").addEventListener("click", () => {
count++;
console.log(`버튼 클릭 횟수: ${count}`);
});
}
attachEventHandlers();
✅ 클로저 덕분에 count 변수가 버튼 클릭 횟수를 유지함.
📌 2. 한 번만 실행되는 함수 (once function)
function once(fn) {
let executed = false;
return function(...args) {
if (!executed) {
executed = true;
return fn(...args);
}
};
}
const logOnce = once(() => console.log("한 번만 실행됨!"));
logOnce(); // "한 번만 실행됨!"
logOnce(); // 실행 안 됨
logOnce(); // 실행 안 됨
✅ 클로저를 이용해 한 번만 실행되는 함수를 만들 수 있음.
🎯 정리
✔️ 클로저(Closure)란?
➡ 함수와 함수가 선언된 렉시컬 환경을 기억하는 구조
✔️ 클로저의 주요 기능
1️⃣ 데이터 은닉: 외부에서 직접 접근할 수 없는 변수를 만들 수 있음.
2️⃣ 상태 유지: 함수 실행 후에도 변수 값을 유지할 수 있음.
3️⃣ 콜백과 비동기 처리: 클로저는 콜백 함수, 이벤트 리스너에서 유용하게 사용됨.
✔️ 클로저의 메모리 관리
- 클로저가 참조하는 변수는 메모리에 유지됨
- 더 이상 참조되지 않으면 GC(가비지 컬렉션)에서 해제됨
🎯 클로저를 사용하면 좋은 이유 (장점 정리)
✔ 1. 데이터 은닉 & 보안성 향상
- 변수를 외부에서 직접 접근할 수 없도록 보호
- private 변수 역할을 할 수 있음
✔ 2. 상태 유지 (Stateful Function)
- 이전 상태를 기억하면서 활용할 수 있음
- 이벤트 핸들러, 반복문에서 유용함
✔ 3. 메모리 효율적 사용
- 필요한 변수만 유지하면서 불필요한 메모리는 자동 해제
- GC(가비지 컬렉션) 에 의해 참조되지 않는 변수는 제거됨
✔ 4. 비동기 코드에서 활용 가능
- setTimeout, setInterval 같은 비동기 처리에서 변수 유지에 유리함
✔ 5. 함수형 프로그래밍에 적합
- 고차 함수(Higher-Order Function) 를 만들 때 활용 가능
- 콜백 함수, 이벤트 리스너, 커링(Currying) 등에 활용
🔥 결론
"클로저를 사용하면 변수를 외부에서 보호하면서도 유지할 수 있다!"
이런 특징 덕분에 보안성, 상태 관리, 비동기 처리, 고차 함수 활용 등에서 큰 장점을 가짐.
➡ 실무에서도 자주 사용되니 꼭 익혀두자! 🚀