JavaScript에서 클로저(Closure)는 함수와 함수가 선언된 렉시컬 환경(Lexical Environment)의 조합을 의미합니다. 클로저는 함수가 생성될 때의 외부 변수의 상태를 기억하고, 그 함수가 호출될 때마다 그 외부 변수를 참조할 수 있게 해줍니다. 이번 글에서는 클로저의 개념을 설명하고, 다양한 활용 예제를 소개하겠습니다.
클로저의 개념
클로저란 무엇인가?
클로저는 함수와 그 함수가 선언된 환경의 조합입니다. 클로저를 이용하면 함수가 생성될 때의 외부 변수의 상태를 기억하고, 그 변수를 참조하거나 수정할 수 있습니다.
function outerFunction() {
let outerVariable = 'I am outside!';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closure = outerFunction();
closure(); // 'I am outside!'
위 예제에서 innerFunction
은 outerVariable
을 참조할 수 있는 클로저를 형성합니다. outerFunction
이 반환된 후에도 innerFunction
은 여전히 outerVariable
에 접근할 수 있습니다.
클로저의 활용 예제
데이터 은닉
클로저는 데이터 은닉(data encapsulation)을 위해 자주 사용됩니다. 이를 통해 객체의 상태를 외부에서 직접 접근할 수 없도록 보호할 수 있습니다.
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
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
이 예제에서 count
변수는 클로저를 통해 캡슐화되어 외부에서 직접 접근할 수 없습니다. increment
, decrement
, getCount
메서드만을 통해 count
변수의 값을 읽거나 수정할 수 있습니다.
함수 팩토리
클로저는 함수 팩토리를 생성하는 데 유용하게 사용됩니다. 함수 팩토리는 다른 함수를 생성하고 반환하는 함수입니다.
function createGreeting(message) {
return function(name) {
return `${message}, ${name}!`;
};
}
const sayHello = createGreeting('Hello');
const sayHi = createGreeting('Hi');
console.log(sayHello('Alice')); // 'Hello, Alice!'
console.log(sayHi('Bob')); // 'Hi, Bob!'
이 예제에서 createGreeting
함수는 클로저를 사용하여 message
변수를 기억하고, 생성된 함수에서 이를 사용할 수 있게 합니다.
부분 적용 함수
클로저를 이용하여 부분 적용 함수(partially applied function)를 만들 수 있습니다. 부분 적용 함수는 일부 인자를 고정한 새 함수를 생성합니다.
function multiply(a, b) {
return a * b;
}
function createMultiplier(multiplier) {
return function(value) {
return multiply(multiplier, value);
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
이 예제에서 createMultiplier
함수는 클로저를 사용하여 multiplier
값을 기억하고, 이를 사용하는 부분 적용 함수를 생성합니다.
이벤트 핸들러와 클로저
클로저는 이벤트 핸들러에서도 자주 사용됩니다. 클로저를 이용하면 이벤트가 발생할 때 필요한 데이터를 유지할 수 있습니다.
function setupButton() {
let clickCount = 0;
document.getElementById('myButton').addEventListener('click', function() {
clickCount++;
console.log(`Button clicked ${clickCount} times`);
});
}
setupButton();
이 예제에서 이벤트 핸들러 함수는 클로저를 통해 clickCount
변수를 기억하고, 버튼이 클릭될 때마다 이 값을 증가시키고 출력합니다.
클로저의 주의점
클로저는 매우 유용하지만, 잘못 사용하면 메모리 누수(memory leak)를 초래할 수 있습니다. 이는 클로저가 참조하는 외부 변수가 계속 메모리에 남아 있기 때문입니다. 따라서 클로저를 사용할 때는 필요하지 않은 외부 변수 참조를 피하고, 필요할 때 클로저를 해제하는 것이 좋습니다.
결론
JavaScript의 클로저는 함수와 함수가 선언된 렉시컬 환경의 조합으로, 함수가 생성될 때의 외부 변수의 상태를 기억하고 참조할 수 있게 해줍니다. 클로저는 데이터 은닉, 함수 팩토리, 부분 적용 함수, 이벤트 핸들러 등 다양한 상황에서 유용하게 사용됩니다. 클로저를 잘 이해하고 활용하면, 더 깔끔하고 효율적인 코드를 작성할 수 있습니다.