logo
Javascript 동작 원리 (1) - The Engine

Javascript 동작 원리 (1) - The Engine

개요

JavaScript를 쓴 지는 벌써 4년이 됐고, “제대로 이해하면서” 쓰기 시작한 건 1년 정도 된 것 같다. 그런데 막상 기본 동작 원리가 필요한 순간이 오면 Closure, Hoisting, Execution Context 같은 키워드가 반복해서 튀어나온다. 나는 그때그때 기능 구현에 급해서 “대충 훑고 넘어간” 경우가 많았다.

하지만 React / Next.js 같은 프레임워크를 정확히 이해하려면, 결국 JavaScript 자체의 실행 모델을 피할 수 없다. 그래서 이번 글에서는 가장 바닥에 있는 개념들(환경/엔진/파싱/JIT 흐름)을 한 번 정리해보려고 한다.

Environment

JavaScript가 실행되는 “환경(Environment)”은 크게 두 가지로 나눌 수 있다.

  • Browser: 브라우저에서 실행되는 환경
  • Server Runtime: Node.js, Deno 같은 런타임에서 실행되는 환경

여기서 말하는 환경은 JavaScript가 어디에서 돌아가느냐에 대한 이야기다. 이 차이가 중요한 이유는, 환경에 따라 사용할 수 있는 전역 객체와 내장 API가 달라지기 때문이다.

  • 브라우저 환경: window, document, fetch, DOM API 등 Web API
  • Node.js 환경: 파일 시스템(fs), 프로세스(process) 등 Node API

여기서 헷갈리기 쉬운 포인트 하나:

  • 환경(Environment/Runtime) 은 “실행 장소 + 제공 API”
  • 엔진(Engine) 은 “JavaScript 코드를 실제로 실행하는 프로그램”

즉, “브라우저에서 돈다”가 “특정 엔진을 의미한다”는 뜻은 아니다. 브라우저마다 엔진이 다를 수 있고(Chrome/Edge, Safari, Firefox), Node/Deno도 각자 런타임이지만 내부적으로는 특정 엔진을 사용한다.

Engine

엔진은 우리가 작성한 JavaScript 소스 코드를 읽고, 컴퓨터가 이해할 수 있는 형태로 변환해 실행한다. 소스 코드는 사람이 읽기 쉬운 텍스트일 뿐이고, CPU가 실행하려면 결국 기계어(Machine Code) 수준으로 내려가야 한다.

대표적인 JavaScript 엔진은 보통 아래가 자주 언급된다.

  • V8 (Chrome/Edge, Node.js, Deno 등에서 사용)
  • JavaScriptCore (Safari)
  • SpiderMonkey (Firefox)

참고: “Node.js, Deno”는 엔진이라기보다 런타임(환경) 이고, 내부적으로 V8 같은 엔진을 사용한다는 점이 핵심이다.

추가로: 엔진은 ECMAScript(자바스크립트 언어 사양) 을 실행하는 역할이고, 환경이 제공하는 Web API/Node API는 표준과 별개라서 브라우저/버전에 따라 차이가 날 수 있다.

Parser

엔진은 먼저 코드를 읽고 문법적으로 유효한지 확인한다. 문법 오류가 있으면 이 단계에서 실행이 멈추고 에러가 발생한다.

문법 검사가 끝나면 보통 AST(Abstract Syntax Tree, 추상 구문 트리) 를 만든다.

AST는 JavaScript만의 개념이 아니라, 대부분의 언어 처리에서 등장하는 표준적인 중간 표현이다. “코드 구조를 트리로 만든 것”이라고 생각하면 된다.

The Interpreter

대부분의 현대 엔진은 AST를 바로 기계어로 바꾸기보다, 바이트코드(Bytecode) 같은 중간 형태로 만들어 빠르게 실행을 시작한다.

웹은 “일단 빨리 화면이 떠야” 하니까, 초기 응답 속도가 중요하다.

The Compiler

실행을 하다 보면 어떤 코드가 “자주 반복 실행되는지(hot path)”가 보인다. 이 구간은 성능 최적화 가치가 크기 때문에, 엔진은 이런 부분을 골라 더 빠른 기계어로 컴파일하고 최적화한다.

Interpreter vs Compiler: 왜 둘 다 있을까?

조사를 하면서 든 의문은 "왜 Interpreter와 Compiler가 동시에 존재하는가?"였다.

보통 C, C++, C#처럼 한 번에 모든 소스코드를 머신코드로 변환 후 사용하는 언어들은 오로지 Compiler만 존재했다.

마찬가지로 Python 처럼 코드를 한줄 씩 읽고 즉시 결과를 출력하는 Interpreter 언어는 Compiler가 존재하지 않았다.

사실 구시대 Javascript Engine들은 Inpterpreter로만 동작했기에 인터프리터 언어로 불렸다고 한다. 그러나 최근 대부분의 사용자가 사용하는 크롬 브라우저의 엔진 V8은 Interpreter와 Compiler가 결합된 JIT(Just In Time) 컴파러로 구현되어 있다고 한다.

빠르게 실행 결과를 보여주어야 하는 웹의 특성 때문에 성능 개선이 필요했지만 Javascript는 Interpreter에 벗어나기엔 힘들었고, 이러한 환경에 느린 실행속도란 단점을 메우기 위해 Compiler와 결합한 JIT 컴파일 기술이 생겨났다고 한다.

JIT 컴파일러는 이외에도 다양한 최적화 기법이 사용된 진보된 기술이라 하지만 이 이상 자세한 학습은 필요하지 않다 느껴 더이상 깊게 들어가보지 않았다.

다음으로 이어서 Javascript의 Environment Context에 대해 정리해보려 합니다.

출처

https://www.webdevlog.com/p/how-js-engine-works-behind-the-scenes/

https://www.freecodecamp.org/news/javascript-execution-context-and-hoisting/

Written on

2024-09-08 18:08

Comments