내용 정리 JS - 꼬리물기 식으로 개념 이해 해보기, 클로저 -> 실행 컨텍스트 -> var/let/const -> 클로저

이유승·2025년 3월 2일
0

내용 정리

목록 보기
33/33
post-thumbnail
  • 파편적으로만 알고 있던 자바스크립트의 주요 개념들을..

  • 서로 관련이 있는 녀석들끼리 꼬리물기식으로 이해하고, 내용을 정리해보는 시간.


1. 클로저란 무엇인가?

클로저는 내부 함수가 외부 함수의 변수(렉시컬 환경)에 접근할 수 있도록 하는 메커니즘입니다.

🔹 왜 사용하는가?

  • 객체 지향 프로그래밍의 특성 중 '캡슐화' 원칙에 의거하여..

  • 자바스크립트의 객체 중 하나인 함수에서는 그 내부의 선언된 변수나 함수를 외부에서 접근하는 것을 엄격하게 제한하고 있다.

  • 외부에서 불필요하거나 잘못된 변수 혹은 함수를 참조할 가능성을 줄이고, 외부에서 접근해서는 안되는 데이터를 지켜야하기 때문.

  • 따라서 자바스크립트는 사전에 자신들이 준비한, 제한되고 안전한 방법으로 내부의 선언된 변수나 함수를 외부에서 접근하도록 하고 있다.

  • 이 것이 바로 클로저.

🔹 주요 특징

  • 외부에서는 직접 접근할 수 없는 데이터를 내부 함수만을 통해 안전하게 활용할 수 있다.
  • 데이터 은닉 및 캡슐화와 유사한 효과를 얻을 수 있으며, 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


2. 실행 컨텍스트와 렉시컬 환경

  • 그런데, 이 클로저라는 녀석은 무슨 원리를 통해 동작하는 것인가?

  • 실행 컨텍스트(Execution Context) 덕분에 동작할 수 있다.

  • 실행 컨텍스트에 대한 조금 더 자세한 내용은 이전 포스팅 참조.

🔹 실행 컨텍스트?

  • 자바스크립트에서 실제 코드를 동작시키는 역할을 수행.

  • 코드 실행에 필요한 정보들을 지니고 있다.

  • 전역 실행 컨텍스트가 먼저 실행되고, 함수를 만나면 그 해당 함수의 실행 컨텍스트가 중첩 실행된다.

  • 함수 내부의 또 다른 함수가 있다면? 그 함수의 실행 컨텍스트가 별도로 실행된다.

  • 그리고 자신보다 상위에 있는 실행 컨텍스트와 연결되어 있기 때문에, 그 컨텍스트가 지닌 정보에 접근할 수 있다.

  • 자신이 생성될 때의 렉시컬 환경(Lexical Environment, 외부 함수의 변수, 함수 선언 등)을 기억한다고 하는 것이 더 기술적인 표현.]

  • 내부 함수의 실행 컨텍스트는, 외부 함수의 실행 컨텍스트에 접근하여 정보를 조회할 수 있다.

  • 따라서 클로저라는 개념이 실행될 수 있게 되는 것.

🔹 실행 컨텍스트의 계층 구조

  1. 전역 실행 컨텍스트: 코드 실행의 기본 환경
  2. 외부 함수 실행 컨텍스트: 전역 컨텍스트 내부에서 실행되는 함수의 환경
  3. 내부 함수 실행 컨텍스트: 외부 함수 내부에서 실행되는 함수의 환경

이런 계층적 구조 덕분에, 내부 함수는 외부 함수의 변수에 접근할 수 있으며, 이것이 바로 클로저의 핵심 동작 원리입니다.


3. 호이스팅과 변수 선언 (var vs let/const)

  • 그런데, 위와 같은 실행 컨텍스트의 특징 덕분에 자바스크립트에서는 하나의 현상이 발생하게 된다.

  • 호이스팅.

🔹 호이스팅(Hoisting)이란?

  • 인터넷 등지에서는 자바스크립트에서 변수나 함수를 호출하는 부분이, 선언하는 부분보다 앞에 있을 때..

  • 변수나 함수를 선언하는 부분을 위로 끌어올린다. 정도로 설명하고 있다.

  • 실제 원리는 조금 더 복잡한데..

  • 자바스크립트 엔진은 실행 컨텍스트를 이용하여 코드가 실제로 실행되기 전에, 변수와 함수 선언을 메모리에 등록한다.

  • 실행 컨텍스트에는 코드 실행에 필요한 변수나 함수에 대한 정보가 미리 저장된다는 것.

🔹 다만 호이스팅은 바람직한 현상은 아니다..

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가 호이스팅만 가지고 만들어진 개념은 아니고, 자바스크립트에서 변수나 함수를 이전 보다 조금 더 엄격하게 관리하기 위해서 개발된 녀석들이다.

let/const는 어떻게 호이스팅을 방지하는가?

var의 경우

  • 선언과 동시에 초기값이 undefined로 설정되므로, 실제 값이 할당되기 전에도 호출하면 undefined가 반환됩니다.

letconst의 경우

  • 선언부만 먼저 등록되고, 초기화는 실제 코드 실행 시점에 이루어짐
  • 이로 인해 Temporal Dead Zone(TDZ)가 발생하여, 선언 전에 해당 변수에 접근하면 에러가 발생합니다.

코드 예제

// var 사용 예제
console.log(a); // undefined (호이스팅 발생)
var a = 10;

// let 사용 예제
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

🔹 스코프(Scope) 차이

변수 타입스코프(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은 블록 스코프)

4. 클로저와 캡슐화의 관계

클로저는 단순히 외부 함수의 변수를 내부 함수로 노출하는 기능 이상입니다.

🔹 데이터 은닉

  • 외부에서는 직접 접근할 수 없는 데이터를 내부 함수만을 통해 안전하게 다룰 수 있음
  • 불필요하거나 위험한 외부 접근을 차단

🔹 캡슐화

  • 객체 지향 프로그래밍의 캡슐화와 유사
  • 클로저를 통해 내부 상태를 보호하고, 필요한 인터페이스(예: 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 변수는 클로저를 통해 내부 상태로 유지되며,
  • 외부에서는 직접 수정할 수 없게 보호됨

5. 결론 및 요약

클로저의 정의

  • 내부 함수가 외부 함수의 렉시컬 환경에 접근할 수 있는 자바스크립트의 기능

실행 컨텍스트

  • 전역, 외부, 내부 함수의 계층 구조를 통해 클로저가 형성됨

호이스팅과 변수 선언

  • var는 초기값이 undefined호이스팅되며,
  • let/constTDZ로 인해 더 엄격한 관리가 이루어짐

데이터 은닉과 캡슐화

  • 클로저를 통해 안전하게 내부 상태를 유지하며,
  • 필요한 인터페이스만 외부에 제공 가능

이처럼 클로저는 자바스크립트의 함수형 프로그래밍 기법에서 핵심적인 역할을 하며, 올바르게 이해하고 활용한다면 보다 견고하고 안전한 코드 작성이 가능합니다. 🚀


📌 참고 자료:

profile
프론트엔드 개발자를 준비하고 있습니다.

0개의 댓글