MDN 정의
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
위 정의에서
즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(스코프)을 기억하여 자신이 선언됐을 때의 스코프 밖에서 호출되어도 그 스코프에 접근할 수 있는 함수를 의미합니다.
간단하게 보면, 함수가 자신이 생성된 환경을 기억하는 것이라고 할 수 있습니다.
function outer() {
const x = 10; // 외부 함수의 지역 변수
return function () {
console.log(x); // 내부 함수에서 외부 함수 변수 참조
};
}
const inner = outer();
inner(); // 10
코드로 확인해 보면,
함수 outer는 내부 함수를 반환 후 생명 주기를 종료합니다. 하지만, 우리는 inner()을 통해 outer의 내부 함수 x를 출력할 수 있습니다.
이처럼 자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저(Closure)라고 부릅니다.
클로저는 변수의 상태를 안전하게 유지하기 위해 사용됩니다.
1. 데이터 은닉
변수를 외부에서 직접 접근하지 못하게 하고, 특정 함수만 접근할 수 있도록 제한하는 방식으로 사용합니다.
function counter() {
let count = 0; // 외부에서 직접 접근 불가능한 변수
return {
increment: function () {
count++;
console.log(count);
},
decrement: function () {
count--;
console.log(count);
},
getCount: function () {
return count;
}
};
}
// count 변수는 외부에서 직접 접근할 수 없고, increment, decrement, getCount 메서드를 통해서만 접근 가능
const myCounter = counter();
myCounter.increment(); // 1
myCounter.increment(); // 2
console.log(myCounter.getCount()); // 2
console.log(myCounter.count); // undefined (외부에서 접근 불가)
2. 비동기 작업
비동기 작업에서 클로저를 활용하면, 비동기 함수가 실행될 때 특정 변수의 상태를 유지합니다.
function createTimer() {
let startTime = Date.now();
return function () {
console.log(`경과 시간: ${(Date.now() - startTime) / 1000}초`);
};
}
// startTime 변수는 createTimer 함수가 종료된 이후에도 timer 함수 내부에서 유지되므로, 이후에도 참조 가능
const timer = createTimer();
setTimeout(timer, 2000); // 2초 후 실행
setTimeout(timer, 4000); // 4초 후 실행
3. 모듈 패턴 구현
클로저를 활용해 전역 변수 오염을 방지하고, 모듈화된 코드를 작성할 수 있다.
const Module = (function () {
let privateVar = '비공개 데이터';
function privateFunction() {
console.log('이 함수는 외부에서 직접 호출할 수 없음');
}
return {
getPrivateVar: function () {
return privateVar;
},
publicFunction: function () {
console.log('이 함수는 외부에서 호출 가능');
privateFunction();
}
};
})();
// privateVar와 privateFunction은 외부에서 접근할 수 없으며,
// Module.getPrivateVar()와 Module.publicFunction()을 통해서만 접근할 수 있다.
console.log(Module.getPrivateVar()); // '비공개 데이터'
Module.publicFunction();
// "이 함수는 외부에서 호출 가능"
// "이 함수는 외부에서 직접 호출할 수 없음"
클로저
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합입니다. 내부 함수가 외부 함수의 변수를 참조하면, 외부 함수의 실행이 끝나도 그 변수가 포함된 렉시컬 환경은 가비지 컬렉터에 의해 수거되지 않고 메모리에 유지되어 내부 함수가 해당 변수를 계속 사용할 수 있습니다. 예를 들어, 클로저를 사용해 카운터를 만들면 각 카운터가 독립적인 상태를 유지할 수 있습니다.
활용 사례
1. 데이터 은닉 - 변수를 직접 접근하지 못하게 보호
2. 비동기 처리 - 특정 상태를 유지하며 비동기 작업 수행
3. 모듈 패턴 - 전역 변수 오염 방지 및 코드 모듈화
클로저가 메모리 관리에 어떤 영향을 미치나요?
클로저는 외부 함수의 렉시컬 환경을 유지하므로, 참조된 변수가 가비지 컬렉터에 의해 수거되지 않아 메모리에 남습니다. 이는 상태 유지를 위해 유용하지만, 불필요한 클로저가 많으면 메모리 누수가 발생할 수 있습니다.
클로저와 스코프 체인은 어떻게 관련 있나요?
클로저는 내부 함수가 외부 함수의 스코프에 접근할 때 스코프 체인을 통해 변수를 참조합니다. 스코프 체인은 렉시컬 환경의 외부 환경 참조로 구성되어, 클로저가 상위 스코프의 변수를 찾을 수 있게 합니다.
클로저와 IIFE(즉시 실행 함수)는 어떻게 다른가요?
클로저는 함수가 외부 스코프의 변수를 참조하며 렉시컬 환경을 유지하는 구조이고, IIFE는 즉시 실행되는 함수로 주로 스코프를 격리해 전역 오염을 방지합니다. IIFE는 클로저를 생성할 때 자주 사용됩니다.
클로저가 성능에 미치는 영향은 (단점)?
클로저는 메모리를 추가로 사용하므로, 과도하게 사용하면 성능에 영향을 줄 수 있습니다. 하지만 현대 JavaScript 엔진은 가비지 컬렉션을 효율적으로 처리해 적절히 사용하면 큰 문제가 되지 않습니다.
실행 컨텍스트와 클로저의 관계는?
클로저는 외부 함수의 실행 컨텍스트가 종료된 후에도 그 렉시컬 환경을 유지합니다. 내부 함수가 외부 환경 참조를 통해 해당 환경에 접근할 수 있게 해, 실행 컨텍스트의 스코프 체인이 클로저의 핵심 역할을 합니다.