Event Loop 이전에 일어나는 일
Source: Dev.to
JavaScript 코드는 작업이 콜 스택에 나타나는 순간 엔진에 의해 즉시 실행되지 않는다. 대부분의 글은 이벤트 루프가 어떻게 동작하는지에 초점을 맞추고, 개발자가 작성한 명령이 그 지점에 도달하기까지 거치는 경로를 생략한다. 그 결과 스코프, 클로저, Temporal Dead Zone 등과 같은 개념 및 기타 실행 세부 사항이 일종의 “기술 마법”처럼 외워지곤 한다.
이 짧은 연재 글에서는 소스 코드 입력부터 실제 실행까지의 경로를 추적한다. V8, SpiderMonkey, JavaScriptCore 등 특정 엔진의 구현 세부 사항에 지나치게 파고들지 않기 위해, 논의는 ECMAScript 262 사양이 정의한 추상화 수준에 머문다.
사양은 의도적으로 JavaScript 실행을 선형적인 단계 순서로 설명하지 않는다—구현 세부 사항은 엔진 제작자에게 맡겨진다. 그럼에도 불구하고, 사양(및 엔진 구현에 대한 실무 지식) 덕분에 네 가지 조건부 단계를 식별할 수 있다:
- 실행 환경 결정: Host, Agent, Realm
- 소스 코드 파싱
- 메타 객체 생성: Module Record / Script Record
- 프로그램 실행
Execution Preparation Phase
스크립트의 파싱 및 초기화가 여기서 이루어진다. 엔진은 필요한 내부 레코드(ScriptRecord, ModuleRecord 등)를 생성하고 사용자 코드가 실행되기 전에 전역 환경을 설정한다.
Instruction Execution Phase
엔진은 컴파일된 바이트코드/추상 구문 트리를 순회하면서 실행 컨텍스트를 생성하고, 표현식을 평가하며, 궁극적으로 프로그램이 정의한 부수 효과를 수행한다.
Execution Environment Layers
사양은 개발자 명령이 실행되기 전에 존재해야 하는 실행 환경의 세 가지 핵심 레이어를 정의한다.
Host
JavaScript 코드가 실행되는 외부 프로그램: 브라우저, Node.js, Deno 등.
Agent
다른 에이전트와 독립적인 실행 환경의 격리된 부분으로, 자체 실행 컨텍스트 스택과 현재 실행 중인 컨텍스트를 가진다. 예시로는 브라우저 탭, Web Worker, iframe 등이 있다.
Realm
전역 객체, 전역 메서드 및 전역 변수를 정의하고 전역 실행 컨텍스트를 생성하는 책임을 지는 논리적 엔티티. V8 엔진 구현에서는 이것이 v8::Context 클래스(C++ 기준)와 대응한다:
// Example: creating a new V8 context
v8::Isolate* isolate = ...;
v8::Local context = v8::Context::New(isolate);
실행 환경의 구조와 격리 모델을 이해하면 다음을 설명할 수 있다:
- 동일 브라우저 내 탭 간에 경쟁 상태가 발생하지 않는 이유.
- 워커가 메인 애플리케이션의 전역 객체와 변수에 접근할 수 없는 이유.
- 단일 Host 내에서 오버라이드된 전역 JavaScript 메서드가 격리되는 방식.