
파편적으로만 알고 있던 자바스크립트의 주요 개념들을..
서로 관련이 있는 녀석들끼리 꼬리물기식으로 이해하고, 내용을 정리해보는 시간.
클로저는 내부 함수가 외부 함수의 변수(렉시컬 환경)에 접근할 수 있도록 하는 메커니즘입니다.
객체 지향 프로그래밍의 특성 중 '캡슐화' 원칙에 의거하여..
자바스크립트의 객체 중 하나인 함수에서는 그 내부의 선언된 변수나 함수를 외부에서 접근하는 것을 엄격하게 제한하고 있다.
외부에서 불필요하거나 잘못된 변수 혹은 함수를 참조할 가능성을 줄이고, 외부에서 접근해서는 안되는 데이터를 지켜야하기 때문.
따라서 자바스크립트는 사전에 자신들이 준비한, 제한되고 안전한 방법으로 내부의 선언된 변수나 함수를 외부에서 접근하도록 하고 있다.
이 것이 바로 클로저.
getter/setter 패턴 등에서 유용하게 활용됩니다.// 클로저를 활용한 카운터 예제
function createCounter() {
  // 외부 함수의 지역 변수 'count'는 외부에서는 직접 접근할 수 없습니다.
  let count = 0;
  
  // 내부 함수는 'count'에 접근할 수 있는 클로저를 형성합니다.
  return function increment() {
    count++;
    console.log(`Current count: ${count}`);
  };
}
const counter1 = createCounter();
counter1(); // 출력: Current count: 1
counter1(); // 출력: Current count: 2
const counter2 = createCounter();
counter2(); // 출력: Current count: 1
counter2(); // 출력: Current count: 2
// counter1과 counter2는 서로 독립적인 클로저를 갖기 때문에, 각기 다른 'count' 값을 유지합니다.
📌 참고: MDN - Closures
그런데, 이 클로저라는 녀석은 무슨 원리를 통해 동작하는 것인가?
실행 컨텍스트(Execution Context) 덕분에 동작할 수 있다.
실행 컨텍스트에 대한 조금 더 자세한 내용은 이전 포스팅 참조.
자바스크립트에서 실제 코드를 동작시키는 역할을 수행.
코드 실행에 필요한 정보들을 지니고 있다.
전역 실행 컨텍스트가 먼저 실행되고, 함수를 만나면 그 해당 함수의 실행 컨텍스트가 중첩 실행된다.
함수 내부의 또 다른 함수가 있다면? 그 함수의 실행 컨텍스트가 별도로 실행된다.
그리고 자신보다 상위에 있는 실행 컨텍스트와 연결되어 있기 때문에, 그 컨텍스트가 지닌 정보에 접근할 수 있다.
자신이 생성될 때의 렉시컬 환경(Lexical Environment, 외부 함수의 변수, 함수 선언 등)을 기억한다고 하는 것이 더 기술적인 표현.]
내부 함수의 실행 컨텍스트는, 외부 함수의 실행 컨텍스트에 접근하여 정보를 조회할 수 있다.
따라서 클로저라는 개념이 실행될 수 있게 되는 것.
✅ 이런 계층적 구조 덕분에, 내부 함수는 외부 함수의 변수에 접근할 수 있으며, 이것이 바로 클로저의 핵심 동작 원리입니다.
var vs let/const)그런데, 위와 같은 실행 컨텍스트의 특징 덕분에 자바스크립트에서는 하나의 현상이 발생하게 된다.
호이스팅.
인터넷 등지에서는 자바스크립트에서 변수나 함수를 호출하는 부분이, 선언하는 부분보다 앞에 있을 때..
변수나 함수를 선언하는 부분을 위로 끌어올린다. 정도로 설명하고 있다.
실제 원리는 조금 더 복잡한데..
자바스크립트 엔진은 실행 컨텍스트를 이용하여 코드가 실제로 실행되기 전에, 변수와 함수 선언을 메모리에 등록한다.
실행 컨텍스트에는 코드 실행에 필요한 변수나 함수에 대한 정보가 미리 저장된다는 것.
console.log(a); // 출력: undefined
var a = 5;
console.log(a); // 출력: 5
호이스팅이 발생할 경우, 변수 a의 값은 undefined로 간주된다.
실행 컨텍스트는 변수의 값을 undefined로 초기화시켜 놓고, 그 다음에 실제 선언부를 만나면 값을 변경하기 때문.
따라서 호이스팅이 발생한다는 것은, 개발자가 의도하지 않은 값이 변수에 할당되었다는 뜻이 된다. 대단히 바람직하지 않은 상황인 것.
함수는 변수와 달리 undefined로 초기화 되지 않고, 모든 내용을 실행 컨텍스트에서 가지고 있다.
그 이유는 아래와 같다.
var vs let/const호이스팅이 바람직한 현상이 아니다보니.. ES6에서는 기존의 var 대신 let과 const라는 새로운 선언식이 개발되었다.
var는 ES6 이후 더 이상 사용이 권장되지 않는다!
물론 let, const가 호이스팅만 가지고 만들어진 개념은 아니고, 자바스크립트에서 변수나 함수를 이전 보다 조금 더 엄격하게 관리하기 위해서 개발된 녀석들이다.
var의 경우undefined로 설정되므로, 실제 값이 할당되기 전에도 호출하면 undefined가 반환됩니다.let과 const의 경우// var 사용 예제
console.log(a); // undefined (호이스팅 발생)
var a = 10;
// let 사용 예제
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;
| 변수 타입 | 스코프(Scope) | 특징 | 
|---|---|---|
var | 함수 스코프(Function Scope) | 함수 내부 어디서든 접근 가능 | 
let, const | 블록 스코프(Block Scope) | {}로 구분된 영역 내에서만 유효 | 
✅ 예제: if 문 내부에서 선언된 변수의 유효 범위 차이
if (true) {
    var x = 10;
    let y = 20;
}
console.log(x); // 10 (var는 함수 스코프이므로 접근 가능)
console.log(y); // ReferenceError: y is not defined (let은 블록 스코프)
클로저는 단순히 외부 함수의 변수를 내부 함수로 노출하는 기능 이상입니다.
getter, setter)를 제공할 수 있음function createCounter() {
  let count = 0; // 외부에서는 직접 접근할 수 없는 변수
  return {
    increment() {
      count++;
      console.log(count);
    },
    getCount() {
      return count;
    }
  };
}
const counter = createCounter();
counter.increment(); // 1 출력
console.log(counter.getCount()); // 1 출력
✅ 설명
count 변수는 클로저를 통해 내부 상태로 유지되며,var는 초기값이 undefined로 호이스팅되며,let/const는 TDZ로 인해 더 엄격한 관리가 이루어짐이처럼 클로저는 자바스크립트의 함수형 프로그래밍 기법에서 핵심적인 역할을 하며, 올바르게 이해하고 활용한다면 보다 견고하고 안전한 코드 작성이 가능합니다. 🚀
📌 참고 자료: