Frontend/JAVASCRIPT

자바스크립트의 심장, 실행 컨텍스트와 호이스팅: 코드의 흐름을 지배하는 내부 메커니즘

미니임 2026. 3. 2. 11:47

 

자바스크립트 엔진이 우리가 작성한 코드를 실행할 때, 단순히 위에서 아래로 한 줄씩 읽는다고 생각하기 쉽습니다. 하지만 실제로는 코드를 실행하기 전 일종의 **'사전 준비 단계'**를 거칩니다. 이 과정에서 발생하는 현상이 바로 많은 개발자를 혼란에 빠뜨리는 **실행 컨텍스트(Execution Context)**와 **호이스팅(Hoisting)**입니다.

현대 웹 애플리케이션의 복잡한 비동기 로직과 클로저를 완벽히 이해하려면, 반드시 거쳐 가야 할 관문인 이 두 개념의 내부 작동 원리를 깊이 있게 파헤쳐 보겠습니다.


1. 실행 컨텍스트(Execution Context): 코드의 실행 환경

실행 컨텍스트는 **"코드를 실행하는 데 필요한 환경 정보를 모아놓은 객체"**입니다. 자바스크립트 엔진은 코드를 실행할 때 콜 스택(Call Stack)에 이 컨텍스트를 쌓아 올리며 실행 순서를 보장합니다.

실행 컨텍스트의 비유: 요리 레시피와 주방

이해를 돕기 위해 요리에 비유해 봅시다.

  • 소스 코드: 요리 레시피
  • 실행 컨텍스트: 실제 요리가 진행되는 '주방 환경' (사용 가능한 도구, 재료의 상태, 현재 불의 세기 등)

주방에 들어서면 가장 먼저 전체적인 조리대 세팅(전역 컨텍스트)이 이루어지고, 특정 요리(함수)를 시작할 때마다 별도의 전용 조리 구역(함수 컨텍스트)이 할당되는 것과 같습니다.

내부 구조 (Deep Dive)

실행 컨텍스트는 크게 세 가지 정보를 담고 있습니다:

  1. VariableEnvironment: 현재 컨텍스트 내의 식별자 정보와 외부 환경 정보(초기 상태 유지).
  2. LexicalEnvironment: VariableEnvironment와 같지만, 코드 실행 중 변경 사항이 실시간으로 반영됩니다.
  3. ThisBinding: this 식별자가 참조할 객체.

2. 호이스팅(Hoisting): 선언의 "끌어올림"

호이스팅은 실행 컨텍스트가 생성될 때, 자바스크립트 엔진이 코드의 선언부만 수집하여 최상단으로 끌어올리는 것처럼 동작하는 현상입니다. 실제 코드가 옮겨지는 것이 아니라, 메모리 공간을 미리 확보하는 과정입니다.

선언과 할당의 분리

호이스팅은 변수 생성 단계에서 **선언(Declaration)**과 **초기화(Initialization)**가 분리되어 발생합니다.

  • var: 선언과 동시에 undefined로 초기화됩니다.
  • let, const: 선언은 되지만 초기화되지 않습니다. 이 사이의 구간을 **TDZ(Temporal Dead Zone)**라고 부릅니다.
  • 함수 선언문: 함수 전체가 메모리에 통째로 올라가 선언 전에도 호출이 가능합니다.

3. 실전 예제: 이커머스 장바구니 로직으로 본 호이스팅

단순한 예제 대신, 실제 비즈니스 로직에서 발생할 수 있는 호이스팅 사례를 코드로 살펴보겠습니다.

JavaScript
 
/**
 * 이커머스 장바구니 할인 계산 로직
 */

function processOrder(price) {
  // 1. 함수 선언문 호이스팅: calculateDiscount는 어디서든 호출 가능
  const finalPrice = calculateDiscount(price);
  console.log(`최종 결제 금액: ${finalPrice}원`);

  // 2. var 변수 호이스팅: 선언만 위로 올라가고 값은 undefined 상태
  console.log(`현재 적용된 쿠폰: ${couponCode}`); // 출력: undefined (에러 발생 X)
  
  var couponCode = "WELCOME_2024";

  // 3. let 변수 호이스팅 및 TDZ: 선언 전 접근 시 ReferenceError 발생
  // console.log(taxRate); // ReferenceError: Cannot access 'taxRate' before initialization
  let taxRate = 0.1;

  function calculateDiscount(amount) {
    // 10% 고정 할인
    return amount * 0.9;
  }
}

processOrder(10000);

코드 분석 및 트러블슈팅

  1. calculateDiscount 함수: 함수 선언문 방식으로 정의되었기에 processOrder 내부 최상단에서 호출해도 문제없이 작동합니다.
  2. couponCode (var): 호이스팅에 의해 메모리 공간은 확보되었지만, 할당문(="WELCOME_2024")은 그 자리에 남습니다. 따라서 undefined가 출력됩니다. 이는 런타임 버그의 주요 원인이 됩니다.
  3. taxRate (let): 엔진이 변수를 인지하고는 있으나, 실제 코드 위치에 도달하기 전까지 접근을 차단(TDZ)합니다. **"안전한 코딩"**을 위해 let과 const 사용이 권장되는 이유입니다.

4. 기술적 트레이드오프 (Trade-offs)

호이스팅과 실행 컨텍스트의 동작 방식을 이해하는 것은 단순히 지식을 쌓는 것을 넘어, 설계 역량에 영향을 미칩니다.

  • 함수 선언문의 편의성 vs. 코드 가독성: 함수를 코드 하단에 배치하고 로직을 먼저 읽게 할 수 있어 가독성이 좋아질 수 있지만, 대규모 프로젝트에서는 실행 순서의 모호함을 초래할 수 있습니다.
  • var의 유연성 vs. 위험성: var는 재선언이 가능하여 유연해 보이지만, 예기치 않은 데이터 덮어쓰기로 인해 디버깅이 매우 어려워집니다.
  • 성능 고려: 실행 컨텍스트가 너무 깊게 쌓이면(재귀 함수 등) Stack Overflow가 발생할 수 있습니다. $O(n)$의 복잡도를 가지는 깊은 호출은 지양해야 합니다.

5. 결론 및 제언

실행 컨텍스트는 자바스크립트라는 언어가 돌아가는 **'무대'**이며, 호이스팅은 그 무대 위에 배우(식별자)들이 미리 자리를 잡는 **'리허설'**입니다.

이 원리를 제대로 파악하면 단순히 "왜 에러가 나지?"를 고민하는 단계에서 벗어나, 엔진의 관점에서 코드를 설계하는 시야를 갖게 됩니다. 특히 TDZ를 활용한 엄격한 변수 관리는 코드의 예측 가능성을 높이는 핵심입니다.

반응형