CS

브라우저가 화면을 표시하는 과정5 : Executing the Javascript

변기원 2023. 9. 28. 15:28

1. Navigation

2. Fetching Data

3. parsing the HTML

4. parsing the CSS

5. executing the Javascript

6. creating the accessibility tree 

7. the Render Tree

단계 중 다섯 번째입니다.

 

세 번째 단계(parsing the HTML)에서 언급했던 pre-loader덕분에 css가 파싱 되어 cssom이 만들어지는 동안 자바스크립트 파일이 다운로드됩니다.

pre-loader의 역할을 간단히 다시 설명하자면, html파싱이 시작되면 메인 파서가 html구문분석을 하는 동안 stylesheet, scripts, image같이 추가적으로 다운로드 요청해야 하는 태그들을 찾아서 미리 다운로드 요청하는 것입니다. 메인 파서가 구문분석을 하려고 도달했을 때는 이미 다운로드를 완료하는 것을 목표로 합니다.

 

JAVASCRIPT EXECUTION

드디어 자바스크립트를 서버로부터 받았습니다. 서버로부터 파일을 받은 후에 코드가 인터프리티드 되고, 컴파일되고, 파싱 되고, 실행됩니다. 컴퓨터는 자바스크립트 코드를 이해할 수 없습니다. 오직 브라우저만 이해할 수 있습니다. 자바스크립트 코드는 컴퓨터가 이해할 수 있는 코드로 변환되어야 하며 이것은 자바스크립트 엔진의 역할입니다(브라우저엔진이 아닙니다). 브라우저에 따라 자바스크립트 엔진은 이름과 일하는 방법이 다를 수 있습니다.

 

Javascript engines

자바스크립트 엔진이란 브라우저 안에서 자바스크립트 코드를 실행해 주는 소프트웨어입니다. 자바스크립트 엔진은 전통적으로 브라우저 제조사에 의해 개발됩니다. 오늘날 우리가 주로 사용하는 브라우저에는 크롬, 사파리, 엣지, 파이어폭스 등이 있습니다. 각 브라우저는 각자 다른 자바스크립트 엔진을 사용합니다.

v8엔진은 구글이 만든 높은 성능의 자바스크립트 엔진입니다. v8엔진은 c++로 만들어졌습니다. 그리고 이것은 크롬브라우저와 node.js 등에 사용됩니다. 이것은 ECMAScript와 WebAssembley를 구현합니다.

이 외에도 JavaScriptCore, Chakra, SpiderMonkey 같은 자바스크립트 엔진이 있습니다. 

처음에 자바스크립트 엔진은 그저 단순한 인터프리터였습니다. 요즘 우리가 사용하는 브라우저의 자바스크립트 엔진은 인터프리터와 컴파일러의 기능이 혼합된 JIT(Just In Time) 컴파일 기능이 있습니다.

(처음에 자바스크립트는 인터프리터 언어, 자바는 컴파일언어라고 배웠는데, 모던 브라우저에서는 그 의미가 점점 희석되는 것 같습니다. 모던 브라우저의 자바스크립트 엔진은 대부분 빠른 성능을 위해 컴파일과 인터프린터를 모두 지원합니다. 이 얘기 같아요.)

 

Compilation

컴파일은 컴파일러라는 소프트웨어가 high-level code를 가져다가 머신코드로 변환합니다. 중간파일이 생성되고, 이 중간파일은 모든 기계에서 실행될 수 있습니다. 이 컴파일 단계 이후로는 코드가 실행될 수 있습니다.

Interpretation

interpretation이 진행되는 동안, 인터프리터는 자바스크립트 코드를 한 줄 한줄 해석하고 바로바로 실행합니다. 컴파일과정을 거치지 않기 때문에 컴파일러처럼 중간파일이 생성되지 않습니다. 예전 버전의 자바스크립트는 이러한 방식의 코드실행을 사용합니다.

JIT Compilation

JIT 컴파일은 컴파일러와 인터프리터의 장점을 모두 취하는 인터프리터의 기능입니다. 순수한 컴파일러는 코드를 실행하기 전에 컴파일 과정부터 완료해야 합니다. 반면에 JIT컴파일러는 코드가 인터프리터처럼 런타임에 한 줄씩 실행되는 동안 컴파일을 진행합니다. 따라서 소스코드가 즉석에서 바로 기계어 코드로 변환된다고 할 수 있습니다

JIT컴파일의 매우 중요한 포인트는 소스코드를 실행 중인 시스템의 기계코드로 컴파일 한다는 것입니다. 이는 곧 컴파일의 결과물이 실행중인 기계의 cpu아키텍처에 최적화됨을 뜻합니다. 

 

간단하게 요약하면 다음과 같습니다.

컴파일러: 코드를 변환한다.

인터프리터: 코드를 실행한다.

JIT컴파일러: 코드가 실행되는 동안 변환한다.

인터프리터 언어로 알고 있던 자바스크립트도 결국 성능을 위해 (대부분의 브라우저에서) 컴파일과 인터프린터를 모두 사용하는 것입니다. 따라서 요즘에는 컴파일과 인터프린트의 결과가 모호해졌습니다.

 

How is the Javascript code processed

자바스크립트 코드가 자바스크립트 엔진에 들어오면 첫 번째로 파싱이 시작됩니다. 이는 코드를 읽는다는 뜻이고, 이 과정에서 코드는 AST(Abstract Syntax Tree)라는 데이터 구조로 변경됩니다. 코드는 언어와 관련된 조각(함수, 또는 const 등 과 같은 키워드)으로 분할된 후 모든 코드조각이 AST를 구성합니다.

변수만 선언하는 일 하나만 하는 프로그램이 있다고 가정해 보겠습니다.

const age = 25;

위 간단한 코드가 AST로 변환되면 아래와 같이 보입니다.

{
  "type": "Program",
  "start": 0,
  "end": 15,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 15,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 6,
          "end": 14,
          "id": {
            "type": "Identifier",
            "start": 6,
            "end": 9,
            "name": "age"
          },
          "init": {
            "type": "Literal",
            "start": 12,
            "end": 14,
            "value": 25,
            "raw": "25"
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}

최신 브라우저는 JIT컴파일러를 사용하므로 JS code가 위와 같은 AST로 변환된 후에는 기계어 코드로 컴파일되어 즉시 실행됩니다. 이런 코드의 실행은 자바스크립트 엔진의 콜스택으로 수행됩니다.

 

콜 스택이란 인터프리터가 여러 함수를 호출하는 스크립트에서 자신의 위치(현재 실행 중인 함수, 함수 내부에서 실행되는 함수 등)를 추적하는 메커니즘입니다.