많은 개발자가 JavaScript를 단순히 '인터프리터 언어'라고 생각합니다. 하지만 현대의 고성능 엔진(V8, SpiderMonkey 등)은 단순히 한 줄씩 읽어 실행하는 것을 넘어, 매우 복잡한 컴파일 과정을 거칩니다.
이러한 문제들을 해결하기 위해 현대 JavaScript 엔진은 JIT(Just-In-Time) 컴파일러를 도입했습니다.
엔진은 코드를 실행하기 전에 여러 단계를 거쳐 '해석하기 쉬운 구조'로 바꿉니다. 이 과정은 크게 구문 분석(Parsing), 바이트코드 생성, 최적화 컴파일 단계로 나뉩니다. 이를 통해 인터프리터의 장점(빠른 시작)과 컴파일러의 장점(빠른 실행)을 모두 취할 수 있습니다.
컴파일러 파이프라인의 전체적인 흐름은 다음과 같습니다.
const x = 5 + 3; 코드는 다음과 같이 분해됩니다.
const (Keyword)x (Identifier)= (Assignment Operator)5 (Literal)+ (Operator)3 (Literal)AST는 코드의 문법적 의미를 트리 형태로 나타냅니다.
{ "type": "VariableDeclaration", "kind": "const", "declarations": [ { "id": { "type": "Identifier", "name": "x" }, "init": { "type": "BinaryExpression", "operator": "+", "left": { "type": "Literal", "value": 5 }, "right": { "type": "Literal", "value": 3 } } } ] }
V8 같은 엔진은 변수의 타입을 예측하여 최적화합니다. 하지만 예측과 다른 타입의 값이 들어오면 최적화된 코드를 버리고 인터프리터로 되돌아가는데, 이를 Deoptimization이라고 합니다.
JavaScript 컴파일러의 동작 원리를 이해하는 것은 단순한 호기심을 넘어 성능 최적화의 핵심입니다. 우리가 작성한 한 줄의 코드가 내부적으로 어떻게 트리 구조가 되고 기계어가 되는지 상상하며 코딩한다면, 더욱 깊이 있는 개발자가 될 수 있을 것입니다.
참고 자료: