개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

실행 컨텍스트 — 코드가 실행되는 환경

2025년 8월 23일
JavaScript실행 컨텍스트호이스팅콜스택

실행 컨텍스트는 JavaScript 엔진이 코드를 실행할 때 만드는 환경 정보 객체입니다. 함수 호이스팅, var의 undefined 초기화, 스코프 체인이 모두 이 객체 안에서 결정됩니다. 코드가 어떻게 동작하는지 근본부터 이해하려면 실행 컨텍스트가 출발점입니다.

실행 컨텍스트란

실행 컨텍스트(Execution Context)는 코드가 실행되기 위해 필요한 환경 정보를 담은 객체입니다. 코드는 반드시 어떤 실행 컨텍스트 안에서 동작하며, JavaScript 엔진은 세 가지 시점에 실행 컨텍스트를 생성합니다.

  1. 전역 코드 실행 시 — 스크립트가 처음 실행될 때 전역 실행 컨텍스트가 생성됩니다.
  2. 함수 호출 시 — 함수가 호출될 때마다 새로운 함수 실행 컨텍스트가 생성됩니다.
  3. eval 실행 시 — eval 함수 내부 코드에 대한 컨텍스트가 생성됩니다. (사용 비권장)

콜 스택 — 실행 컨텍스트의 관리 방식

실행 컨텍스트는 콜 스택에 쌓이고 제거되는 방식으로 관리됩니다. 함수가 호출되면 스택에 push, 함수가 종료되면 스택에서 pop됩니다.

function inner() {
  console.log("inner");
}

function outer() {
  inner();
  console.log("outer");
}

outer();

콜 스택(Call Stack): 실행 컨텍스트를 순서대로 쌓고 제거하는 구조입니다. LIFO(후입선출) 방식으로 동작합니다.

push: 스택 맨 위에 항목을 추가하는 동작.

pop: 스택 맨 위에서 항목을 꺼내는 동작.

위 코드의 콜 스택 변화는 아래와 같습니다.

호출 순서:
outer() 호출 → inner() 호출 → inner() 종료 → outer() 종료

[ call stack ]

1단계         2단계         3단계         4단계
┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐
│         │   │ inner() │   │         │   │         │
│ outer() │   │ outer() │   │ outer() │   │         │
│ global  │   │ global  │   │ global  │   │ global  │
└─────────┘   └─────────┘   └─────────┘   └─────────┘

재귀 함수 등으로 스택이 한계 이상 쌓이면 Stack Overflow 에러가 발생합니다.

Stack Overflow: 콜 스택의 최대 허용 크기를 초과했을 때 발생하는 오류로, 무한 재귀 호출이 대표적인 원인입니다.

실행 컨텍스트 내부 구조

실행 컨텍스트는 내부적으로 세 가지 구성 요소를 가집니다.

구성 요소내용
Variable Environmentvar 선언 변수와 함수 선언을 초기 상태로 저장합니다. 생성 단계의 스냅샷이며 이후 변경되지 않습니다.
Lexical Environment실행 단계에서 실제 식별자 바인딩이 이루어지는 공간입니다. let, const, 함수 선언이 여기서 관리됩니다. 외부 환경에 대한 참조(outer)를 포함합니다.
this 바인딩해당 컨텍스트에서 this가 가리키는 값을 저장합니다. 전역에서는 window(브라우저) 또는 global(Node.js)입니다.

생성·실행 단계 — 컨텍스트 처리 흐름

실행 컨텍스트는 생성 단계(Creation Phase)와 실행 단계(Execution Phase) 두 단계를 거칩니다.

생성 단계

코드를 실행하기 전, 엔진이 컨텍스트를 준비하는 단계입니다.

  • var 변수: 선언을 인식하고 값을 undefined로 초기화합니다.
  • 함수 선언: 함수 전체를 메모리에 등록합니다.
  • let / const: 선언만 인식하고 초기화하지 않습니다. (TDZ 상태)

TDZ(Temporal Dead Zone): let/const 변수가 선언은 되었지만 아직 초기화되지 않은 구간입니다. 이 구간에서 변수에 접근하면 ReferenceError가 발생합니다.

실행 단계

코드를 한 줄씩 순서대로 실행하며 실제 값이 할당됩니다.

Creation Phase               Execution Phase
┌─────────────────────┐    ┌─────────────────────┐
│ var x → undefined   │    │ var x → 10          │
│ function foo → full │    │ let y → 20          │
│ let y → TDZ         │    │ foo() runs          │
└─────────────────────┘    └─────────────────────┘

호이스팅

호이스팅은 생성 단계에서 변수와 함수 선언이 미리 등록되는 현상입니다. 코드가 물리적으로 위로 올라가는 것이 아니라, 엔진이 실행 전에 미리 등록해두기 때문에 생기는 동작입니다.

함수 선언 — 전체 호이스팅

// 선언보다 먼저 호출해도 동작합니다.
sayHello(); // "Hello"

function sayHello() {
  console.log("Hello");
}

var 변수 — undefined로 호이스팅

console.log(name); // undefined (에러 아님)
var name = "Alice";
console.log(name); // "Alice"

let / const — TDZ로 인한 에러

console.log(age); // ReferenceError: Cannot access 'age' before initialization
let age = 30;

let과 const도 호이스팅은 됩니다. 단, 초기화 전이므로 TDZ 상태에 있어 접근하면 에러가 발생합니다.

함수 표현식·화살표 함수 — 선언 전 호출 불가

함수를 변수에 담는 방식을 함수 표현식이라고 합니다. 화살표 함수도 함수 표현식의 일종입니다.

// 함수 표현식
const sayHi = function() { console.log("Hi"); };

// 화살표 함수 (함수 표현식의 단축 문법)
const sayHi = () => console.log("Hi");

function sayHello처럼 선언식으로 만든 함수는 선언 전에 호출해도 동작합니다. 반면 함수 표현식은 변수에 함수가 담기는 시점이 해당 코드 줄에 도달했을 때입니다. 그 전에 호출하면 변수는 존재하지만 아직 함수가 아닌 상태라 에러가 납니다.

// const에 할당 — 선언 전 접근 자체가 에러 (TDZ)
sayHi(); // ReferenceError: Cannot access 'sayHi' before initialization
const sayHi = () => console.log("Hi");

// var에 할당 — 변수는 undefined, 함수가 아니라서 호출 에러
sayBye(); // TypeError: sayBye is not a function
var sayBye = () => console.log("Bye");

함수 표현식(Function Expression): 함수를 변수에 값으로 담는 방식입니다. 변수의 스코프 규칙을 따르므로 선언 전 접근이 제한됩니다.

렉시컬 환경과 외부 참조

렉시컬 환경(Lexical Environment)은 현재 스코프의 식별자 정보와 함께 외부 렉시컬 환경에 대한 참조(outer)를 가집니다. 이 참조가 연결되어 스코프 체인이 형성됩니다.

[ inner — lexical env ]
┌─────────────────────────────────┐
│ env record: { x: 10 }          │
│ outer → outer's lexical env    │
└─────────────────────────────────┘
         │
         ▼
[ outer — lexical env ]
┌─────────────────────────────────┐
│ env record: { y: 20 }          │
│ outer → global lexical env     │
└─────────────────────────────────┘
         │
         ▼
[ global — lexical env ]
┌─────────────────────────────────┐
│ env record: { ... }             │
│ outer → null                    │
└─────────────────────────────────┘

inner 함수에서 x를 찾지 못하면 outer 참조를 따라 상위 스코프를 탐색합니다. 함수가 정의된 위치에 따라 outer 참조가 결정되기 때문에, 함수가 어디서 호출되든 정의된 시점의 스코프를 기억합니다. 이 구조가 클로저(Closure)의 토대가 됩니다.

요약

개념핵심
실행 컨텍스트코드 실행에 필요한 환경 정보 객체
콜 스택실행 컨텍스트를 push/pop으로 관리
생성 단계변수 등록, 함수 선언 메모리 등록, TDZ 진입
실행 단계코드 순차 실행, 실제 값 할당
선언 방식호이스팅 동작
함수 선언전체 등록, 선언 전 호출 가능
varundefined로 초기화
let / constTDZ, 선언 전 접근 불가
함수 표현식변수 규칙을 따름

호이스팅, TDZ, 스코프 체인은 각각 별개의 규칙처럼 보이지만 실제로는 실행 컨텍스트 생성 단계라는 하나의 메커니즘에서 비롯됩니다. JavaScript 코드가 실행되기 전에 보이지 않는 준비 단계가 먼저 일어난다는 것을 인식하면, 예상 밖의 동작 대부분이 자연스럽게 설명됩니다.

← 이전 글스코프 — 변수가 살아있는 범위
다음 글 →this — 호출 방식과 바인딩 규칙