38장 브라우저의 렌더링 과정

niyu·2021년 7월 19일
0
post-thumbnail

브라우저는 HTML, CSS, JS로 작성된 텍스트 문서를 어떻게 렌더링하는 걸까? 🧐

간략히 요약하자면 다음과 같다.

🧩 요청과 응답
HTML, CSS, JS, Image 등의 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받는다.

🧩 HTML/CSS 파싱과 렌더 트리 생성
응답받은 HTML과 CSS를 파싱해 DOMCSSOM을 생성하고 이들을 결합해 렌더 트리를 생성한다.

🧩 JS 파싱과 실행
응답받은 JS를 파싱해 AST를 생성하고 바이트 코드로 변환해 실행한다. 이때 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있다. 변경된 DOMCSSOM은 다시 렌더 트리로 결합된다.

🧩 리플로우(Reflow)와 리페인트(Repaint)
렌더 트리를 기반으로 HTML 요소의 위치와 크기를 계산하고 브라우저 화면에 HTML 요소를 페인팅한다.

브라우저 렌더링 과정

브라우저의 렌더링 과정 / 그림출처: poiemaweb

이제 각 과정에 대해 살펴보자.

요청과 응답

렌더링에 필요한 리소스는 모두 서버에 존재하기 때문에 필요한 리소스를 서버에 요청한다.

예를 들어, 브라우저 주소창에 https://velog.io 을 입력하고 엔터 키를 누르면 해당 velog 서버로 전송되고, 서버는 이 요청에 대해 암묵적으로 index.html 파일을 클라이언트로 응답한다.

또한 https://velog.io 를 로드하면 index.html 뿐만 아니라 css, js, image 등의 파일들도 응답된 것을 확인할 수 있는데, 렌더링 엔진은 index.html을 파싱하는 도중에 css, js, image 등의 외부 리소스를 로드하는 태그 등을 만나면 HTML 파싱을 일시 중단하고 해당 리소스 파일을 서버로 요청하기 때문이다.

HTML 파싱과 DOM 생성

서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트다. 이를 브라우저에 시각적인 픽셀로 렌더링하려면 HTML 문서를 브라우저가 이해할 수 있는 자료구조로 변환해 메모리에 저장해야 한다.

브라우저가 이해할 수 있는 자료구조로 변환되는 과정은 다음과 같다.

📓 Byte
브라우저는 서버가 응답한 HTML 문서를 바이트(2진수) 형태로 응답받는다.

ex) 101101001011 ...

📕 String
응답된 바이트 형태의 HTML 문서는 meta 태그의 charset 속성에 따라 지정된 인코딩 방식으로 문자열로 변환된다.

<html><head><meta charset="UTF-8"> ... </html>

📗 Token
문자열로 변환된 HTML 문서를 읽어 들여 문법적 의미를 갖는 최소 단위인 토큰들로 분해한다.

{
  startTag: 'html',
  contents: {
    startTag: 'head',
    contents: { ... },
    ...
  endTag: 'html'
}

📘 Node
각 토큰들을 객체로 변환해 노드들을 생성한다. 토큰의 내용에 따라 문서 노드, 요소 노드, 어트리뷰트 노드, 텍스트 노드가 생성된다.

📙 DOM
모든 노드들을 트리 자료 구조로 구성한다. 이 노드들로 구성된 트리 자료구조를 DOM(Document Object Model) 이라 부른다.

즉, DOM은 HTML 문서를 파싱한 결과물이다. DOM에 대해서는 39장에서 살펴보자.

<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <div>
      <h1>Cities</h1>
      <ul>
        <li id="one" class="red">Seoul</li>
        <li id="two" class="red">London</li>
        <li id="three" class="red">Newyork</li>
        <li id="four">Tokyo</li>
      </ul>
    </div>
  </body>
</html>

DOM tree

DOM tree / 그림출처: poiemaweb

브라우저 렌더링 엔진은 응답받은 HTML 문서를 파싱해서 브라우저가 이해할 수 있는 자료구조인 DOM(Document Object Model) 을 생성한다.

CSS 파싱과 CSSOM 생성

렌더링 엔진은 HTML을 처음부터 한 줄씩 순차적으로 파싱해 DOM을 생성해 나간다. DOM을 생성해 나가다가 CSS를 로드하는 link 태그나 style 태그를 만나면 DOM 생성을 일시 중단한다.

link 태그의 href 속성에 지정된 CSS 파일을 서버에 요청해 응답되면 렌더링 엔진은 HTML과 동일한 해석과정(바이트→문자→토큰→노드→CSSOM)을 거쳐 CSS를 파싱해 CSSOM(CSS Object Model)을 생성한다.

style 태그 내의 CSS 또한 파싱하여 CSSOM을 생성한다.

<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
...
<!-- style.css -->

body {
  font-size: 18px;
}

ul {
  list-style-type: none;
}

CSSOM 생성

CSS 파싱을 완료하면 HTML 파싱이 중단된 지점부터 다시 HTML을 파싱해 DOM 생성을 재개한다.

렌더 트리 생성

렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱해 각각 DOM과 CSSOM을 생성한다. 생성된 DOM과 CSSOM은 렌더링을 위해 렌더 트리(render tree) 로 결합된다.

단, meta 태그나 script 태그같은 브라우저 화면에 렌더링되지 않는 노드나, display: none 같은 CSS에 의해 표시되지 않는 노드들은 포함되지 않는다.

즉, 렌더트리는 브라우저 화면에 렌더링되는 노드만으로 구성된다.

렌더 트리 생성

렌더 트리 생성 / 그림출처: poiemaweb

완성된 렌더 트리는 각 HTML 요소의 레이아웃(위치와 크기)를 계산하는 데 사용되고, 브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력된다.

렌더 트리와 레이아웃/페인트

JS 파싱과 실행

CSS 파싱 과정과 마찬가지로 렌더링 엔진은 HTML을 파싱해 DOM을 생성해 나가다가, JS 파일을 로드하거나 JS 콘텐츠를 담은 script 태그를 만나면 DOM 생성을 일시 중단한다.

자바스크립트 파싱과 실행은 브라우저 렌더링 엔진이 아닌 자바스크립트 엔진이 처리한다. 자바스크립트 엔진은 JS를 해석해 AST(Abstract Syntax Tree)를 생성하고, AST를 기반으로 바이트 코드로 변환해 인터프리터에 의해 실행된다.

자바스크립트 파싱과 실행

리플로우와 리페인트

만약 JS 코드에 의해 DOM이나 CSSOM이 변경될 경우, 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합되고 변경된 렌더트리를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저의 화면에 다시 렌더링한다. 이를 리플로우(Reflow), 리페인트(Repaint)라 한다.

리플로우는 레이아웃에 영향을 주는 변경이 발생한 경우에 레이아웃에 대한 계산을 다시 하며, 리페인트는 재결합된 렌더 트리를 기반으로 다시 페인트한다.

리플로우와 리페인트

레이아웃 계산과 페인팅은 다음과 같은 경우에 반복해서 실행될 수 있다.

👉 JS에 의한 노드 추가나 삭제
👉 브라우저 창의 resizing에 의한 viewport 크기 변경
👉 HTML 요소의 위치나 크기에 변경을 발생시키는 width, height, margin, padding, border, display, position 등의 스타일 변경

만약 레이아웃에 영향이 없는 변경이라면 리플로우없이 리페인트만 실행된다.

레이아웃 계산과 페인팅을 다시 실행하는 리렌더링은 비용이 많이 들어 성능에 악영향을 주는 작업이기 때문에, 가급적 리렌더링이 빈번하게 발생하지 않도록 주의할 필요가 있다.

0개의 댓글