[JS] JavaScript Closure
목차 보기
📌 JavaScript 클로저
🧠 클로저(Closure)란?
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합 함수 내부에서 외부 함수의 변수에 접근할 수 있도록 만드는 기능 자바스크립트에서는 함수가 평가될 때의 상위 스코프 체인도 참조함. (외부 렉시컬 환경 참조에 대한 결정)
📦 렉시컬 스코프(Lexical Scope)란?
렉시컬(Lexical) 스코프는 정적 스코프라고도 불리며, 함수가 어디서 정의되었는지에 따라 상위 스코프가 결정되는 방식. 실행되는 위치가 아니라 정의된 위치가 기준이라는 점이 핵심.
또한, 함수 객체 내부 슬롯 [[Environment]]에 저장함.
함수 자신이 정의된 환경인 상위 스코프의 참조를 저장
즉, 함수가 평가될 당시의 실행 중인 실행 컨텍스트의 렉시컬 환경을 가르킴.
🧪 클로저 예시
function outer() {
const x = 10;
const inner = function () {
console.log(x); // 외부 함수의 변수 x에 접근
};
return inner;
}
const closureFn = outer();
closureFn(); // 결과: 10
이 예제에서 outer()는 내부 함수 inner를 반환하고 종료되지만, inner는 여전히 x를 기억하고 있음.
이게 클로저이다.
🔁 외부 함수보다 오래 살아남는 중첩 함수
중요한 개념
중첩 함수가 외부 함수보다 오래 생존하면서 외부 함수의 지역 변수를 참조하면 클로저가 만들어지고, 외부 함수의 변수들을 계속 참조할 수 있게 됨. (값 변경도 가능) 외부 함수의 스코프를 참조하지 않을 경우에는 모던 브라우저의 최적화 기능에 의해 클로저로 판단되지 않음. (메모리 효율 굿)
🧵 메모리 관리와 클로저
외부 함수가 종료되었더라도, 내부 함수가 그 환경을 참조하고 있다면 그 렉시컬 환경은 GC(Garbage Collection)의 대상이 되지 않음.
즉, 클로저는 메모리를 점유한 채 유지될 수 있음 모던 자바스크립트 엔진(V8 등)은 최적화를 수행해 실제로 사용되지 않는 상위 스코프의 식별자는 기억하지 않아서 메모리 낭비를 줄일 수 있음.
📌 클로저가 만들어지는 일반적인 조건
클로저가 성립되려면 아래 두 조건이 충족되어야 함
- 중첩 함수가 외부 함수보다 오래 유지되어야 함
- 중첩 함수가 상위 스코프의 식별자(자유 변수)를 참조해야 함
📚 자유 변수(Free Variable)란?
내부 함수가 사용하는 변수 중, 자신의 스코프에 존재하지 않는 변수를 자유 변수라고 부른다. 클로저는 이런 자유 변수들을 자신이 선언될 당시의 렉시컬 환경에 저장하여 계속 사용할 수 있도록 해주는 것
클로저라는 이름의 의미 또한 함수가 자유 변수에 의해 닫혀있다는 의미
자유 변수를 사용할 수 있게 해주는데 오프너 아닌지
🛡️ 클로저의 활용 예시: 상태 은닉
클로저는 상태를 은닉하거나 특정 함수에서만 접근 가능하게 만들고 싶을 때 유용함
function counter() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
const myCounter = counter();
myCounter(); // 1
myCounter(); // 2
여기서 count는 외부에서 접근할 수 없고 myCounter를 통해서만 접근 가능하다.
이런 구조는 정보 은닉이나 모듈 패턴 구현에 탁월함
📏 클로저와 함수형 프로그래밍
함수형 프로그래밍에서는 불변성을 중요시한다. 외부 상태를 변경하지 않는 것이 핵심인데, 클로저를 통해 부수 효과(Side Effects)를 줄일 수 있다.
클로저를 이용하면 상태를 함수 내부로 숨길 수 있어 오류 가능성이 줄고 유지보수도 쉬워져 자주 사용되는 패턴이다.
📈 클로저 사용 시 주의할 점
- 메모리 누수: 클로저가 불필요하게 상위 환경을 오래 참조하면 메모리가 해제되지 않을 수 있음. (모던 브라우저 최적화에 의해 관리되기 때문에 메모리 낭비 및 누수에 대해 고민하지 않아도 되지만 기본적으로 클로저를 대할 때 알아두면 좋을 듯 하다.)
- 순수 함수 위반: 클로저는 상태를 유지하므로, 함수형 프로그래밍 관점에서는 순수하지 않은 함수로 간주될 수 있음.
🙋♂️ 클로저 활용 질문
클로저는 언제 만들어지나
함수가 정의될 때 클로저가 만들어짐. 실행 시점이 아니라 선언 시점이라는 게 중요하다.
클로저는 메모리에 얼마나 오래 남나
참조가 계속되는 한 GC에 의해 해제되지 않고 유지됩니다.
모든 중첩 함수가 클로저인지
아니다. 상위 스코프의 변수를 참조하지 않는다면 클로저가 아니다.
클로저는 왜 메모리 누수나 낭비를 유발하는지
사용하지 않는 렉시컬 환경도 계속 유지되기 때문입니다. 모던 브라우저의 최적화를 통해 해결 가능하지만, 다른 환경이나 구형 브라우저에서 클로저를 사용해야한다면 유의해야할 부분이다.
클로저를 활용한 실무 예제
- Vue, React의
setup함수 내부 상태 - JavaScript 모듈 구현
- private 변수 구현 등
JS에서 클로저 없이 비슷한 기능을 구현할 수 있는지
ES6의 클래스나 Symbol 등을 통해 유사한 구조를 구현할 수는 있지만, 클로저만큼 직관적이거나 명시적이지 않음.
✍️ 마무리
클로저는 자바스크립트에서 가장 강력하면서도 오해받기 쉬운 개념이다. 제대로 이해하고 나면 상태 관리, 정보 은닉, 모듈화 등 여러 상황에서 아주 유용하게 활용 가능하다.
🔗 참고 링크