Javascript 구동원리

안효민·2021년 3월 28일
0

들어가며

Javascript는 C나 Java와 같은 언어보다 사용하기 훨씬 간단하고, 신경써야하는 부분이 적습니다.

그렇기에 남들과 차별점을 가지는 개발자, 고급 개발자가 되고자 한다면 비단 문법이나 API 공부에 그치지 않고 언어가 어떤 방식으로 컴파일되고 작동되는지 알아야 한다고 생각합니다.

이 전에 웹앱 개발을 진행하면서 자바스크립트의 멀티쓰레딩 동작에 대해서 공부해본 적이 있었는데, 이 때 Javascript의 엔진 중 하나인 V8에 대해서 알게 되었습니다. 이번 글에서는 V8의 구조와 동작 원리등에 대해서 공유해보고자 합니다.

Javascript 엔진

Javascript엔진은 개발자가 작성한 자바스크립트 코드를 해석하고 실행시켜주는 프로그램 혹은 인터프리터로, 가능한 짧은 시간 내에 가장 최적화된 코드를 생성하는 것이 목표입니다. 표준적인 인터프리터로 구현될 수도 있고, 혹은 자바스크립트 코드를 바이트코드로 컴파일하는 JIT 컴파일러로 구현할 수도 있습니다. 여러 목적으로 사용될 수 있지만 주로 웹 브라우저를 위해 사용됩니다.

Javascript 엔진의 종류에는 V8뿐만 아니라 애플이 개발한 웹킷, 라이노, 최초의 Javascript 엔진인 스파이더 몽키 등이 있습니다.

이 글에서는 이 중 크롬에서 사용되며, 가장 널리 알려진 V8엔진에 대해서 알아보겠습니다.

V8

V8 엔진은 구글이 주도하여 C++로 작성된 고성능의 오픈소스 자바스크립트 & 웹 어셈블리 엔진으로 대표적으로 구글 크롬과 Node.js에서 사용되고 있습니다. V8은 대부분의 ES2016+(ES7+)의 기능을 구현했기 때문에 const/let, class, 화살표 함수 및 spread 연산자를 사용할 수 있습니다.

V8 엔진의 주요 두 구성요소는 Memory Heap과 Call Stack입니다.

Memory Heap

메모리 할당이 일어나는 곳입니다.

Call Stack

코드 실행에 따라 함수 호출 스택이 쌓이는 곳입니다.
JS는 싱글쓰레드 기반 언어로, Call Stack이 하나 뿐입니다. 즉, 한 번에 하나의 작업만 처리할 수 있습니다.

Call Stack은 현재 프로그램 상에서 어떤 위치에 있는지 기록하는 공간입니다.
함수를 실행하면 해당 함수는 Call Stack의 최상단에 위치하며, 함수의 실행이 끝나면 해당 함수는 Call Stack에서 제거되고 다음 함수로 커서가 이동합니다.

이벤트 루프

위에서 말씀드렸다싶이 JS는 싱글쓰레드 기반 언어인데, 만약 별다른 처리가 없다면 수행시간이 오래 걸리는 함수에 걸렸을 때, 다른 모든 작업이 해당 함수가 Call Stack에서 제거될 때까지 중단될 것입니다.
그렇게 된다면 사용자는 애플리케이션이 매끄럽지 않고 느리다고 생각하게 될 겁니다. 이를 방지하고 UX를 향상시키기 위해서 이벤트 루프가 존재합니다.
Ajax와 같은 비동기 함수는 Web API를 호출하고. Web API는 콜백 함수를 Callback queue에 밀어넣습니다. 그리고 이벤트 루프는 Call Stack이 비어있을 때마다 Callback queue에서 콜백함수를 Call Stack으로 가져와 수행합니다.

JS에서 위의 일련의 과정을 정리하면 다음과 같습니다.

  1. 콜 스택에 함수가 쌓인다.
  2. 스택에 마지막에 쌓인 함수부터 실행된다.
  3. 만약 함수가 비동기 함수라면 Web API를 호출한다.
  4. 호출된 Web API는 자신을 호출한 비동기 함수의 콜백함수를 Callback queue에 밀어 넣는다.
  5. 이벤트 루프는 Call Stack과 Callback queue의 상태를 체크하여, 콜스택이 비어있을 경우, Callback queue의 콜백함수를 Call Stack으로 이동시킨다.

작동원리


1. V8은 다른 컴파일러들과 마찬가지로 코드를 parser에 가져옵니다. Parser는 코드를 토큰화한 뒤 AST(Abstract Syntax Tree)로 변환하고 Ignition에게 넘깁니다.
2. Ignition은 JS를 컴퓨터가 해석하기 쉬운 바이트코드로 변환하여 코드의 양을 줄이고, 코드 실행시의 메모리 공간을 절약합니다.
3. Ignition이 변환한 바이트 코드를 실행하여 소스코드가 실제로 머신에서 동작하게 됩니다.

자주 사용되는 코드는 Compiler TurboFan으로 보내져서 Optimized Machine Code, 즉 최적화된 코드로 다시 컴파일됩니다. 그러다가 다시 사용이 덜 된다 싶으면 Deoptimizing됩니다.

마치며

프론트엔드 분야는 기타 분야와 비교해서 입문하기가 비교적 쉬운 분야입니다. 실제로 저희 회사는 시스템 소프트웨어를 주로 개발하는 회사이기 때문인지, 프론트엔드 개발자를 웹 디자이너와 동일시하는 이전 세대의 관념이 남아있기 때문인지 웹 개발의 난이도와 성과를 종종 낮게 평가하기도 합니다.

하지만 프론트엔드의 기술스택이 점점 복잡해지고, 사용자 친화적인 UX와 UI에 신경써야하는 부분이 많아져감에 따라, 프론트엔드 개발도 많은 공부가 필요한 분야라고 생각합니다.

Javascript엔진에 대해서 알아보는 것은 더 나은 프론트엔드 개발자가 되기 위한 필수 과정 중 하나라고 생각합니다.
감사합니다.

참고자료

https://edu.goorm.io/learn/lecture/557/%ED%95%9C-%EB%88%88%EC%97%90-%EB%81%9D%EB%82%B4%EB%8A%94-node-js/lesson/174354/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84%EC%9D%B4%EB%9E%80
https://joshua1988.github.io/web-development/translation/javascript/how-js-works-inside-engine/
https://evan-moon.github.io/2019/06/28/v8-analysis/

profile
어제보다 많이 알고, 어제보다 많이 이해하자

0개의 댓글