JavaScript 실행 컨텍스트의 내부 메커니즘

자바스크립트 엔진이 코드를 해석하고 실행할 때, 특정 환경 정보를 담고 있는 추상적인 개념이 바로 실행 컨텍스트(Execution Context)입니다. 모든 코드는 이 컨텍스트 내부에서 평가되며, 개발자가 직접 조작할 수 없는 엔진 내부의 객체 형태로 관리됩니다.

컨텍스트의 분류

실행 컨텍스트는 생성 조건에 따라 세 가지로 구분됩니다.

  • 글로벌 실행 컨텍스트: 최상위 레벨의 코드가 실행되는 환경입니다. 브라우저 환경에서는 window, Node.js 환경에서는 global 객체가 생성되며 this가 해당 객체를 참조합니다.
  • 함수 실행 컨텍스트: 함수 호출 시마다 고유한 컨텍스트가 새로 생성됩니다. 호출 횟수만큼 동적으로 만들어지며, 각각 독립적인 환경을 구성합니다.
  • Eval 실행 컨텍스트: eval() 함수 내에서 실행되는 코드를 위한 전용 컨텍스트입니다.

핵심 구성 요소

실행 컨텍스트는 세 가지 핵심 요소로 이루어집니다: 변수 객체(Variable Object), 스코프 체인(Scope Chain), 그리고 this 바인딩입니다.

변수 객체와 활성 객체

변수 객체(VO)는 해당 컨텍스트에서 선언된 변수와 함수를 저장하는 컨테이너입니다. 글로벌 컨텍스트에서는 글로벌 객체 자체가 변수 객체의 역할을 합니다.

함수 컨텍스트에서는 활성 객체(AO)라는 이름으로 구현됩니다. 함수가 호출되는 순간 생성되며, arguments 객체를 초기 속성으로 포함합니다.

함수 컨텍스트의 생성 과정을 살펴보겠습니다:

function demo(x) {
    var y = 10;
    var z = function() {};
    function inner() {}
    y = 20;
}

demo(5);

단계 1: 생성 단계

엔진이 함수 코드를 실행하기 전, 활성 객체를 초기화합니다:

  1. AO 객체를 생성하고 매개변수와 변수 선언을 수집
  2. 수집된 이름을 AO의 속성으로 등록, 값은 undefined로 초기화
  3. 전달된 인자로 매개변수 값을 갱신
  4. 함수 선언문을 찾아 해당 참조로 속성 값을 대체

생성 단계 완료 시점의 AO:

AO = {
    arguments: { 0: 5, length: 1 },
    x: 5,
    y: undefined,
    z: undefined,
    inner: reference to function inner() {}
}

단계 2: 실행 단계

코드를 위에서부터 순차적으로 실행하며 변수 값을 실제로 할당합니다:

AO = {
    arguments: { 0: 5, length: 1 },
    x: 5,
    y: 20,
    z: reference to FunctionExpression "z",
    inner: reference to function inner() {}
}

스코프 체인

식별자를 검색할 때, 엔진은 현재 컨텍스트의 변수 객체에서 시작하여 상위 컨텍스트의 변수 객체를 순차적으로 탐색합니다. 이러한 계층적 연결 구조가 스코프 체인입니다.

함수가 정의되는 시점에 내부 슬롯 [[Environment]]에 상위 스코프에 대한 참조가 저장됩니다. 이 참조는 함수 객체의 불변 속성으로, 렉시컬 스코프를 구현하는 핵심 메커니즘입니다.

function outer() {
    let outerVar = 'I am outer';
    
    function middle() {
        let middleVar = 'I am middle';
        
        function inner() {
            console.log(outerVar, middleVar);
        }
        
        return inner;
    }
    
    return middle;
}

const myFunc = outer()();

각 함수의 환경 참조는 다음과 같이 구성됩니다:

outer.[[Environment]] → GlobalEnvironment
middle.[[Environment]] → outer's Environment
inner.[[Environment]] → middle's Environment

this 바인딩

this의 값은 컨텍스트 생성 방식에 따라 결정됩니다:

  • 글로벌 컨텍스트: 글로벌 객체
  • 함수 컨텍스트: 호출 패턴에 따라 동적으로 결정
  • 메서드로 호출 시: 해당 메서드를 소유한 객체
  • 일반 함수로 호출 시: 글로벌 객체(엄격 모드에서는 undefined)

실행 스택의 동작

실행 컨텍스트는 호출 스택(Call Stack)이라는 LIFO 구조로 관리됩니다. 스크립트 로드 시 글로벌 컨텍스트가 최하단에 위치하며, 함수 호출 시 새로운 컨텍스트가 스택 상단에 쌓입니다.

function first() {
    console.log('Executing first');
    second();
    console.log('Back to first');
}

function second() {
    console.log('Executing second');
    third();
}

function third() {
    console.log('Executing third');
}

first();
console.log('Global context continues');

실행 흐름에 따른 스택 변화:

// 초기 상태
[GlobalContext]

// first() 호출
[GlobalContext, firstContext]

// second() 호출
[GlobalContext, firstContext, secondContext]

// third() 호출
[GlobalContext, firstContext, secondContext, thirdContext]

// third 완료 및 제거
[GlobalContext, firstContext, secondContext]

// second 완료 및 제거
[GlobalContext, firstContext]

// first 완료 및 제거
[GlobalContext]

생명 주기

실행 컨텍스트는 생성 → 실행 → 소멸의 단계를 거칩니다.

단계수행 작업
생성변수 객체 구성, 스코프 체인 설정, this 바인딩 확정
실행코드 순차 실행, 변수 값 할당 및 계산
소멸컨텍스트 스택에서 제거, 가비지 컬렉션 대상 여부 판단

글로벌 컨텍스트는 페이지가 로드될 때 생성되어 브라우저 탭이 닫힐 때까지 유지됩니다. 함수 컨텍스트는 호출 즉시 생성되며 함수 종료 시 스택에서 제거됩니다.

스코프와의 차이점

스코프와 실행 컨텍스트는 밀접한 관련이 있지만 본질적으로 다른 개념입니다.

스코프(Scope)는 코드를 작성하는 시점에 결정되는 정적 개념입니다. 함수가 어디에서 선언되었는지에 따라 상위 스코프가 확정되며, 이는 실행 중에 변경되지 않습니다.

실행 컨텍스트는 런타임에 동적으로 생성되는 개념입니다. 같은 함수라도 호출 시점과 위치에 따라 서로 다른 컨텍스트가 만들어집니다.

let value = 'global';

function checkScope() {
    let value = 'local';
    console.log(value); // 'local'
}

checkScope();

위 코드에서 value의 스코프는 소스 코드가 작성될 때 확정되지만, checkScope의 실행 컨텍스트는 실제 호출되는 순간에야 생성됩니다.

태그: JavaScript Execution Context Scope Chain Hoisting Call Stack

6월 30일 00:48에 게시됨