JavaScript Execution Context가 무엇인지 이해하고, 내부 구조와 동작 방식을 한눈에 파악할 수 있도록 정리합니다.
JavaScript 실행 컨텍스트(Execution Context)란, 자바스크립트 코드가 평가되고 실행되는 환경을 추상적으로 표현한 개념입니다.
쉽게 말해, 코드 실행에 필요한 모든 정보(변수, 함수 선언, 스코프, this 바인딩 등)를 담는 컨테이너라고 볼 수 있습니다.
Global Execution Context
Function Execution Context
Eval Execution Context
eval()
함수를 호출할 때 생성되는 컨텍스트ES6 이후 스펙(ECMAScript 2015+)에서 Execution Context는 크게 아래 두 가지 환경(Environments)으로 구성됩니다.
최근 ECMAScript 스펙(ES2024 등)에서도 여전히 같은 구조를 유지하고 있습니다.
let, const, class 선언처럼 블록 스코프를 가지는 선언들을 관리
블록 스코프(Block Scope) 선언을 관리하는 환경 레코드(Environment Record)
LexicalEnvironment = {
EnvironmentRecord: { /* 현재 블록/함수의 식별자와 값 */ },
outer: [null or 상위 LexicalEnvironment]
}
현재 스코프에 있는 변수, 함수, 상수 등이 어떤 값에 연결되어 있는지를 관리하는 맵(딕셔너리) 형태의 저장소를 의미합니다.
즉, 각 식별자(identifier)가 어떤 값과 바인딩되어 있는지를 기록하고 있습니다.
outer
를 따라 상위 Lexical Environment로 거슬러 올라가며 검색하는 구조(스코프 체인, Scope Chain)를 형성합니다.즉, var
와 달리 let/const
는 블록 스코프를 형성하므로, 실행 시점에 별도의 Lexical Environment가 만들어지고 이를 통해 관리됩니다.
Q. Variable Environment와 Lexical Environment는 완전히 다른가요?
A. 내부적으로 동일한EnvironmentRecord
구조를 공유하지만, 함수 스코프와 블록 스코프를 구분하기 위해 ‘논리적’으로 나누어 사용하는 개념입니다.
실제 구현에서는 같은 객체를 참조하다가, 블록({ }
)이 생성되면 새 Lexical Environment가 만들어져let/const
선언을 관리하게 됩니다.
아래 예시 코드를 통해 Execution Context가 생성되고 소멸되는 과정을 단계별로 살펴보겠습니다.
function outer(a) {
var x = 10;
let y = 20;
{
const z = 30;
let y = 40;
}
function inner(b) {
var x = 50;
return x + b;
}
return inner(a);
}
outer(10);
outer
함수 등이 여기서 등록됩니다.outer
함수 호출 → Outer Execution Context 생성컨텍스트 생성(Context Creation)
var
, let
, const
로 선언된 변수, 내부에 정의된 다른 함수 등이 먼저 등록됩니다. let
과 const
는 초기화되기 전에 TDZ(Temporal Dead Zone) 상태에 놓여 있습니다.ExecutionContext_outer = {
EnvironmentRecord: {
a: 10, // 매개변수 초기화
x: undefined, // var 선언(초기값 undefined)
y: uninitialized, // let 선언(TDZ)
inner: function inner(b) { ... } // 함수 선언
},
outer: GlobalExecutionContext.LexicalEnvironment
}
컨텍스트 초기화(Context Initialization) & 실행(Execution)
함수 내부 코드가 실행되면서 var
, let
, const
로 선언된 변수들이 실제 값(또는 undefined
)을 할당받고, TDZ가 해제됩니다.
ExecutionContext_outer.EnvironmentRecord = {
a: 10,
x: 10,
y: 20,
inner: function inner(b) { ... }
}
블록({ }
) 실행
{ }
블록이 시작되면, 새로운 Lexical Environment가 잠시 생성되어 블록 내 변수들을 관리합니다.
블록이 끝나면 이 임시 Lexical Environment는 더 이상 참조되지 않아 GC(가비지 컬렉션)의 대상이 됩니다.
블록 내부에 동일한 이름의 변수가 있다면 Shadowing이 발생하여, 블록 내부 변수와 외부 변수를 구분합니다.
// 블록 진입 시
BlockLexicalEnvironment = {
EnvironmentRecord: {
z: uninitialized,
y: uninitialized // 블록 내부 y (외부 y와 shadowing)
},
outer: ExecutionContext_outer.LexicalEnvironment
}
// 블록 실행 후
BlockLexicalEnvironment = {
EnvironmentRecord: {
z: 30,
y: 40
},
outer: ... // 바깥 스코프 참조
}
// 블록 종료 시점에서
// BlockLexicalEnvironment에 대한 참조는 사라짐
inner
함수 호출 → Inner Execution Context 생성함수 내부에 정의된 함수를 호출하면, 또 다른 Function Execution Context가 생성됩니다.
매개변수와 내부 변수들이 등록되고, 실행 단계에서 실제 값이 할당됩니다.
필요한 연산을 마치고 결과를 반환하면, 해당 컨텍스트는 소멸합니다.
ExecutionContext_inner = {
EnvironmentRecord: {
b: 10, // 매개변수 b에 10 할당
x: undefined // var x 선언
},
outer: ExecutionContext_outer.LexicalEnvironment
}
이 과정을 통해 결과만 확인하는 것이 아니라, 자바스크립트 내부에서 어떤 정보를 바탕으로 스코프와 변수를 처리하는지 이해할 수 있었습니다.
특히,
등을 종합적으로 살펴볼 수 있었습니다.
위 내용들은 추가적으로 포스팅하겠습니다 😎
내부 메커니즘을 이해하는 것이 단순 ‘코더’가 아닌 진정한 ‘개발자’로 나아가는 데 중요한 과정임을 느꼈습니다.
앞으로도 자바스크립트 엔진의 핵심 메커니즘을 비롯해, 최신 라이브러리·프레임워크 구조까지 단계적으로 살펴볼 예정입니다.
더 알차고 깔끔한 내용으로 찾아뵙겠습니다.
읽어주셔서 감사합니다!