JavaScript, 인터프리터 언어일까?


약간의 배경지식..

1995년 자바스크립트는 유저와의 상호작용을 위해 10일만에 탄생한 언어다. "HTML 페이지를 동적으로 만드는 것"에 목적이 있어 가벼운 인터프리터 언어로 만들어졌다. 구글이 구글맵스를 개발하며 당시 브라우저, 특히 javascript 엔진에 한계를 느꼈다. 많은 유저 인터랙션이 필요했고, 이를 커버할 수 있는 좋은 엔진이 필요했다.

Google Maps

답답함을 느낀 구글은 2009V8 엔진을 출시했다. 크롬 V8 엔진이 기존과 어떻게 달라져 획기적인 성능 향상이 있었을까? 이번 글에서는 V8엔진을 비롯해 현대 자바스크립트 엔진이 성능을 어떻게 향상시켰는지, 인터프리팅과 컴파일을 방식을 비교하며 살펴본다.

interpreter vs compiler

ruslanspivak.com

Compile 언어

compile 언어와 interpreter 언어의 가장 큰 차이점은 pre-processing, 컴파일 유무다. 컴파일이란 A -> 컴파일 -> B 언어로 변환하는 과정이다. 이 글에서는 주로 Java, C#, Swift등 고급 언어로 작성된 소스코드를 기계어로 변환하는 것을 의미한다. 변환된 기계어는 고급언어를 바로 인터프리팅 방식으로 실행한 것보다 빠르다. 아래 예제를 통해 컴파일 방식의 성능이 월등히 좋은 대표적인 경우를 살펴보자.

function sum () { let result = 0 for (let i = 1 ; i <= 10 ; i++){ result += i; } return result; } sum() // for loop.. -> 55 sum() // for loop.. -> 55 sum() // for loop.. -> 55 // compile 결과 sum() = 55 sum() = 55 sum() = 55

컴파일을 마친 기계어는 sum 함수의 결과를 55로 기억하고 있다. 반면 인터프리터 언어는 매번 sum 함수를 실행해 10번의 loop 를 거쳐, 55를 반환한다. 자바스크립트 생태계에서 익숙한 컴파일러를 살펴보면 BabelTypescript가 있다. Babel은 ES6등 최신 자바스크립트 스펙을 모든 브라우저에서 사용 가능한 스크립트로 컴파일한다. Typescript는 TS로 작성된 코드를 JS로 변환한다. (위 링크에서 직접 컴파일 된 결과를 확인할 수 있다)

자바스크립트의 동작

자바스크립트는 interpreter 언어다. 개발자도구 콘솔에서 스크립트를 작성해 실행하는데 컴파일이 필요하지 않기 때문이다. 하지만, 결론부터 이야기하면 자바스크립트도 컴파일 과정을 거친다. 다만 자바스크립트 엔진 내부에서 실행중 컴파일이 필요한 경우에 내부에서 컴파일 한다. 대표적인 현대 자바스크립트 엔진인 크롬 V8의 내부를 조금 살펴보자.

chrome v8 엔진

출처 : advanced javascript from udemy

자바스크립트의 성능을 비약적으로 향상시킬 수 있었던 이유는 엔진 내부에서 컴파일 과정 거치기 때문이다. 엔진이 작동하는 원리는 이렇다. 먼저 엔진이 실행할 JS 파일을 받게 된다. 파싱, AST(Abstract Syntax Tree)를 구축하는 과정을 거친다. (위 과정에 대해 궁금하면 Lachezar 블로그를 읽어보자)

다음으로 Interpreter가 코드를 읽으며 실행한다. 코드를 수행하는 과정에서 프로파일러가 지켜보며 최적화 할 수 있는 코드를 컴파일러에게 전달해준다. 주로 반복해서 실행되는 코드 블록을 컴파일(최적화)한다. 그리고 원래 있던 코드와 최적화된 코드를 바꿔준다. 코드를 우선 인터프리터 방식으로 실행하고 필요할 때 컴파일 하는 방법을 JIT(Just-In-Time) 컴파일러 라고 부른다. 크롬의 V8 엔진을 포함해 Mozilla의 Rhino, Firefox의 SpiderMonkey도 같은 방법을 사용한다.

결론은, 자바스크립트는 실행되는 플랫폼에 따라 인터프리팅과 컴파일이 혼합되어 사용된다. 이 방식은 자바스크립트의 성능을 크게 향상시켰다.

처음부터 기계어로 개발하지 그래.

고도의 최적화가 필요한 어플리케이션(ex. 구글 맵스)에서 처음부터 기계어를 사용하면 되잖아? 라는 의문이 들 수 있다. 여기에는 그렇게 하지 못했던 웹 개발의 역사가 있다.

자바스크립트가 탄생했을 당시 브라우저가 우후죽순 생겨나던 시기였다 (firefox, IE 등). 웹에서 기계어를 사용하려면 모든 브라우저에서 실행가능한 기계어가 필요했다. 한마디로 기계어 버전의 자바스크립트가 필요했다. 한 회사가 웹에서 실행가능한 독자적인 기계어를 고안했다고 하자. 하지만 웹서버에서 .js 파일을 응답했다. 자바스크립트가 이렇게 발전할 수 있었던 원인이기도 하다.

하지만 근래에 WA(WebAssembly)가 등장했다. 브라우저에서 C/C++, Rust 와 같은 저수준의 언어를 바이너리 형식으로 컴파일 해주는 기술이다. 웹에서 네이티브에 가까운 속도를 낼 수 있게 해준다.

정리

  • 자바스크립트는 인터프리터 언어다
  • 하지만 플랫폼에 따라 엔진 내부에서 컴파일 과정을 거친다 (대부분의 현대 자바스크립트 엔진)

레퍼런스

반복의 느린 變化
oowgnoj github
© 2022, by oowgnoj