클로저(Closure)
2025. 3. 30. 22:17ㆍWeb Development/JavaScript
클로저(Closure)
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합입니다. 함수가 자신이 생성된 스코프 밖에서 실행될 때도 원래 스코프에 접근할 수 있는 현상을 말합니다.
클로저의 기본 개념
function createCounter() {
let count = 0; // 외부에서 직접 접근할 수 없는 변수
return function() {
count++; // 외부 함수의 변수에 접근
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1 출력
console.log(counter()); // 2 출력
console.log(counter()); // 3 출력
// 새로운 카운터 생성 (독립적인 count 변수를 가짐)
const counter2 = createCounter();
console.log(counter2()); // 1 출력 (counter와 별개의 count 변수)
위 예제에서 createCounter 함수는 이미 실행이 끝났지만, 반환된 함수는 여전히 count 변수에 접근할 수 있습니다. 이것이 클로저의 핵심입니다.
클로저의 활용 예제
1. 데이터 캡슐화와 정보 은닉
function createBank() {
let balance = 0; // 외부에서 직접 접근 불가능한 변수
return {
deposit: function(amount) {
if (amount > 0) {
balance += amount;
return `${amount}원을 입금했습니다. 잔액: ${balance}원`;
}
return "유효하지 않은 금액입니다.";
},
withdraw: function(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return `${amount}원을 출금했습니다. 잔액: ${balance}원`;
}
return "출금할 수 없습니다.";
},
getBalance: function() {
return `현재 잔액: ${balance}원`;
}
};
}
const myAccount = createBank();
console.log(myAccount.deposit(5000)); // "5000원을 입금했습니다. 잔액: 5000원" 출력
console.log(myAccount.withdraw(2000)); // "2000원을 출금했습니다. 잔액: 3000원" 출력
console.log(myAccount.getBalance()); // "현재 잔액: 3000원" 출력
// console.log(myAccount.balance); // undefined (직접 접근 불가)
2. 함수 팩토리
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log(double(5)); // 10 출력
console.log(triple(5)); // 15 출력
console.log(quadruple(5)); // 20 출력
3. 이벤트 핸들러와 콜백
function setupButton(buttonId, message) {
// 버튼 클릭 시 message 변수에 접근하는 클로저 생성
document.getElementById(buttonId).addEventListener('click', function() {
alert(message);
});
}
setupButton('button1', '버튼 1을 클릭했습니다!');
setupButton('button2', '버튼 2를 클릭했습니다!');
4. 모듈 패턴
const calculator = (function() {
// 비공개 변수와 함수
let result = 0;
function validate(num) {
return typeof num === 'number' && !isNaN(num);
}
// 공개 API
return {
add: function(num) {
if (validate(num)) {
result += num;
}
return this; // 메서드 체이닝을 위한 this 반환
},
subtract: function(num) {
if (validate(num)) {
result -= num;
}
return this;
},
multiply: function(num) {
if (validate(num)) {
result *= num;
}
return this;
},
divide: function(num) {
if (validate(num) && num !== 0) {
result /= num;
}
return this;
},
getResult: function() {
return result;
},
reset: function() {
result = 0;
return this;
}
};
})(); // 즉시 실행 함수로 모듈 생성
console.log(calculator.add(5).multiply(2).subtract(3).getResult()); // 7 출력
calculator.reset();
console.log(calculator.getResult()); // 0 출력
클로저와 관련된 주의사항
1. 메모리 관리
클로저는 외부 함수의 변수를 참조하기 때문에 해당 변수들은 가비지 컬렉션에서 제외됩니다. 따라서 불필요한 클로저 사용은 메모리 누수를 일으킬 수 있습니다.
// 메모리 누수 가능성이 있는 코드
function createHugeArray() {
const hugeArray = new Array(1000000).fill("데이터");
return function(index) {
return hugeArray[index];
};
}
const getItem = createHugeArray(); // hugeArray 전체가 메모리에 유지됨
2. 루프에서의 클로저
루프 내에서 클로저를 생성할 때는 각 반복마다 새로운 렉시컬 환경이 필요합니다.
// 문제가 있는 코드 (var 사용)
function createButtons() {
for (var i = 1; i <= 5; i++) {
var button = document.createElement("button");
button.textContent = "Button " + i;
button.addEventListener("click", function() {
console.log("Button " + i + " clicked"); // 모든 버튼이 "Button 6 clicked" 출력
});
document.body.appendChild(button);
}
}
// 해결책 1: 즉시 실행 함수로 새로운 스코프 생성
function createButtonsFixed1() {
for (var i = 1; i <= 5; i++) {
(function(index) {
var button = document.createElement("button");
button.textContent = "Button " + index;
button.addEventListener("click", function() {
console.log("Button " + index + " clicked"); // 정상 작동
});
document.body.appendChild(button);
})(i);
}
}
// 해결책 2: let 사용 (블록 스코프)
function createButtonsFixed2() {
for (let i = 1; i <= 5; i++) {
const button = document.createElement("button");
button.textContent = "Button " + i;
button.addEventListener("click", function() {
console.log("Button " + i + " clicked"); // 정상 작동
});
document.body.appendChild(button);
}
}
'Web Development > JavaScript' 카테고리의 다른 글
객체 (0) | 2025.03.30 |
---|---|
함수 패턴 (0) | 2025.03.30 |
스코프 (0) | 2025.03.30 |
함수의 정의와 호출 (0) | 2025.03.30 |
반복문 (0) | 2025.03.30 |