Skip to content
뒤로가기

V8 자바스크립트 엔진

게시된 날짜:  at 

목차


V8 인트로 이미지

이 아티클을 읽게 된 계기는, 자바스크립트를 실행 가능한 코드로 변환하기 위해선 V8 같은 엔진이 필요하고 파싱, 최적화, 기계어 변환 과정을 거친다는 과거의 기억에 대한 궁금증을 해소하고 싶었기 때문이다. 따라서 이번 아티클을 통해 엔진 메커니즘을 깊이 이해해 추후에 성능 개선과 디버깅에 활용하고자 한다.

이 글에선 아티클의 핵심 내용과 이해 과정에서 마주한 익숙하지만 설명하기 어렵거나, 생소한 용어들에 대해 추가로 정리하였다.
(이 글은 노션에서 처음 작성되었으며, 노션의 원문 스타일이 블로그에 완전히 적용되지 않아, 일부 가독성이 떨어질 수 있는 점 양해 부탁드립니다.)

원문 번역 사이트


자바스크립트는 엔진에 의해 처리된다.

자바스크립트가 실행되기 위해선 엔진이 필요하며, 실행에 필요한 모든 것들은 엔진에 의해 처리된다. 이를 담당하는 엔진 종류 중 하나가 바로 구글에서 개발한 자바스크립트 및 웹어셈블리 엔진인 C++로 작성된 V8이다.

V8은 다양한 환경에서 사용될 수 있다.

  1. 크롬 또는 유사한 환경
  2. Node.js
  3. 브라우저 이외에 독립적 실행
  4. C++ 애플리케이션 속 임베드

웹어셈블리? (MDN)

최신 웹 브라우저에서 실행할 수 있는 새로운 유형의 코드로, C/C++, Rust 등의 소스 언어를 효과적으로 컴파일하도록 고안되었다.

자바스크립트가 있는데 왜 탄생? (MDN)

자바스크립트를 대체하기 위해 탄생한 것은 아니며, 나란히 돌아가면서 서로의 부족한 점을 보완해 두 언어의 강점을 동시에 취하기 위해 설계되었다. (자바스크립트만으로는 고성능 연산과 다양한 언어 지원에 한계가 있다.)


왜 V8일까?

추상화

프로그래밍 언어의 추상화 레벨을 나타내며, 아래로 내려갈수록 더 많은 책임이 따른다.
(+ 시스템에 대한 더 많은 권한, 제어를 가진다.)

  1. C/C++ → 어셈블리어 ⇒ 컴파일러 필요
  2. 어셈블리어 → 기계어 ⇒ 어셈블러 필요
  3. 자바스크립트 → 실행 가능한 코드 ⇒ 자바스크립트 엔진

스파이더몽키 vs V8

스파이더몽키(파이어폭스 엔진)

자바스크립트 → 바이트 코드 → 기계어

V8

자바스크립트 → 기계어


Node.js에서는 어떻게 사용될까?

V8 소스코드를 살펴보면 크롬의 document 객체, require() 등의 기능을 찾을 수 없다.

그 이유는 Node.js와 크롬에서 이 기능들을 C++로 구현하고,
V8을 사용해 자바스크립트 함수로 바인딩하기 때문이다!!


왜 이런 방식을 택했을까?

자바스크립트는 고수준 언어라 접근이 불가능하기 때문이다.
대신 C/C++을 통해 저수준 리소스에 직접 접근해 원하는 작업을 수행하는 것이다.

따라서 위에서 언급한 기능은 Node.js 소스 코드에서 찾아볼 수 있다.


여기서 눈여겨 봐야할 점은 이러한 방식(V8이 크롬과 Node.js에서 쓰이는 방식)은 다른 사람들도 자신의 사례에 맞게 적용과 사용이 가능하다. 다시 말해, C++로 기능 → 자바스크립트 함수로 바인딩 → 노출하는 방식이 가능하다. (그렇기에 자바스크립트가 로봇 공학 분야 등 다양한 분야에서 사용된다.)


V8은 어떻게 동작할까?

V8은 코드를 두 단계로 컴파일한다.

  1. 코드 → 기계어 (빠르게 컴파일 but 최적화 X)

    1. ⬆️ 현재 단계로도 자바스크립트 실행 충분
    2. 동시에 최적화 코드가 컴파일 (느림 but 훨씬 최적화) → 완료 시 다음 단계를 진행
  2. 자바스크립트 → 최적화 컴파일 코드로 전환 (또 두 단계를 거침)

    1. 이그니션 (빠른 저수준 레지스터 기반 인터프리터)

      ⇒ 추상 구문 트리(AST) → 바이트 코드 생성 (이그니션만으론 한계 존재… → 이때 터보팬 사용)

    2. 터보팬 (최적화 컴파일러)

      ⇒ 어떤 함수가 자주 사용 → 훨씬 빠른 최적화

    3. JIT 컴파일레이션 (새로운 접근방식, 인터프레테이션 + 컴파일레이션의 장점을 결합)

단계별로 정리하자면

V8의 동작과정 요약

V8의 동작과정 요약

  1. V8이 자바스크립트 코드 이해를 위해 소스 코드 파싱 + 추상 구문 트리(AST)로 변환 ⇒ V8이 더 쉽게 처리 가능한 형태 (이 과정에서 스코프 함께 생성됨)

  2. AST + 스코프 기반으로 → 인터프리터 → 바이트 코드 생성

    1. 이 시점에서 엔진은 코드 실행 + 타입 피드백(어떤 타입의 값들이 들어왔는지) 수집 (즉, 실행 단계에서 코드에 대한 타입 피드백을 제공)
  3. (실행 속도를 높이기 위해) 바이트 코드 + 피드백 데이터 → 최적화 컴파일러(터보팬) 전달될 수도 있음

    1. 최적화 컴파일러 → 고도로 최적화된 기계어 생성

      • 최적화 vs 비최적화 기계어 예시(V8 공식문서)
        공식문서를 이해시킨 뒤 GPT에게 예시를 요청함 (틀린 예시일 수 있음)

        // 범용(비최적화) 기계어 스타일
        ; a와 b 타입 검사
        if (typeof a !== 'number' || typeof b !== 'number') {
            goto generic_add_handler ; 문자열이면 concat, 객체면 toString
        }
        
        ; 숫자일 경우만 처리
        result = a + b
        return result
        
        ---
        
        // 최적화된 기계어 스타일 (TurboFan 결과)
        ; 타입 피드백: a, b는 항상 number
        ; 따라서 타입 체크 생략
        result = a + b
        return result
    2. 이 과정은 병렬로 처리, 자주 사용되는 바이트 코드 → 코드로 표시 → 더 효율적인 기계어로 변환

      • 바이트 코드가 아닌 기계어를 직접 사용하지 않는 이유는?

        1. 기계어 → 많은 양의 메모리가 필요
        2. 속도: 기계어 > 바이트 코드 ⇒ X (항상 빠른건 아님)
          1. 기계어: 실행 속도 ⬆️, 컴파일 속도 ⬇️
          2. 바이트 코드: 실행 속도 ⬇️, 컴파일 속도 ⬆️
  4. 만약 어느 시점에서 최적화 컴파일러(터보팬)의 가정 중 하나라도 잘못된다면 → 최적화 취소 → 다시 인터프리터로


나만의 자바스크립트 런타임 만들기

원문 작성자의 깃허브 참고


추후 읽고 싶은 아티클

V8과 WebAssembly: 현대 자바스크립트 엔진의 구조와 성능 최적화(상하편)


수정 제안하기

다음 게시글
React.memo 완벽 해부 언제 쓸모 있고 언제 쓸모없는가