기술 포스트

브라우저는 HTML을 어떻게 화면에 그릴까?


Article

퍼블리셔라면 HTML과 CSS를 매일 다룬다. 그런데 브라우저가 이 코드를 받아서 실제로 화면에 그리기까지 어떤 일이 벌어지는지 생각해본 적 있나?

이 과정을 이해하면 나중에 React의 가상 DOM이 왜 필요한지가 자연스럽게 이해된다. 그 출발점이 되는 이야기다.

HTML을 받으면 가장 먼저 하는 일

브라우저는 HTML 파일을 받으면 위에서 아래로 한 줄씩 읽어 내려간다. 그리고 이걸 DOM 트리라는 구조로 바꾼다.

DOM은 Document Object Model의 약자인데, 쉽게 말하면 HTML 태그들의 가계도다.

<html>
  <body>
    <h1>안녕하세요</h1>
    <p>반갑습니다</p>
  </body>
</html>

이 코드를 브라우저가 읽으면 이런 트리가 만들어진다.

html
 └── body
      ├── h1 → "안녕하세요"
      └── p  → "반갑습니다"

부모-자식 관계가 있는 나무 구조다. 퍼블리셔에게 익숙한 태그 중첩 구조 그 자체가 DOM 트리다.

CSS도 트리가 된다

HTML이 DOM 트리가 되는 것처럼, CSS도 CSSOM 트리로 변환된다.

CSSOM은 CSS Object Model의 약자다. 브라우저는 CSS 파일을 읽으면서 "이 선택자에 이 스타일이 적용된다"는 정보를 트리 구조로 정리한다.

예를 들어 body { font-size: 16px; } 아래에 h1 { color: red; }가 있으면, h1은 body의 font-size를 상속받으면서 자기만의 color도 갖게 된다. 이런 상속과 우선순위 계산이 CSSOM에서 일어난다.

두 트리를 합친다 — 렌더 트리

DOM 트리와 CSSOM 트리가 준비되면, 브라우저는 이 둘을 합쳐서 렌더 트리를 만든다.

렌더 트리는 실제로 화면에 보이는 것들만 포함한다. display: none이 적용된 요소는 여기서 빠진다. <head> 태그 안의 내용도 빠진다. 오직 눈에 보이는 요소와 그에 적용된 스타일만 남는다.

비유하자면, DOM 트리가 건물의 전체 설계도라면 렌더 트리는 실제로 지을 부분만 표시한 시공 도면이다.

레이아웃 — 어디에 얼마나 크게?

렌더 트리가 완성되면 브라우저는 레이아웃 단계에 들어간다. 이걸 리플로우(Reflow) 라고도 부른다.

이 단계에서 브라우저는 각 요소의 정확한 위치와 크기를 계산한다. "이 div는 왼쪽에서 100px, 위에서 200px 위치에 너비 300px, 높이 150px"처럼 구체적인 좌표를 잡는 거다.

width가 50%라면 부모 요소의 크기를 기준으로 실제 픽셀값을 계산한다. margin: auto도 이 단계에서 실제 수치로 바뀐다.

페인트 — 드디어 그린다

위치와 크기가 정해지면 마지막으로 페인트(Paint) 단계가 온다. 실제로 픽셀을 화면에 찍는 단계다.

배경색, 텍스트, 테두리, 그림자 같은 시각적 요소들이 이때 그려진다.

전체 흐름 정리

HTML → DOM 트리
CSS  → CSSOM 트리
         ↓
     렌더 트리
         ↓
   레이아웃(리플로우) — 위치/크기 계산
         ↓
   페인트(리페인트) — 화면에 그리기

이 순서를 Critical Rendering Path(핵심 렌더링 경로) 라고 부른다.

리플로우와 리페인트의 차이

이 두 가지는 헷갈리기 쉬운데, 핵심은 간단하다.

  • 리플로우: 요소의 위치나 크기가 바뀔 때 발생한다. 레이아웃을 다시 계산해야 하니까.
  • 리페인트: 색상이나 배경 같은 시각적 속성만 바뀔 때 발생한다. 위치는 그대로니까 레이아웃 계산은 건너뛴다.

중요한 건, 리플로우가 일어나면 리페인트도 반드시 따라온다는 점이다. 위치가 바뀌면 당연히 다시 그려야 하니까. 하지만 리페인트만 일어날 수도 있다.

그래서 width, height, margin, padding 같은 속성을 자주 바꾸면 성능에 영향을 줄 수 있다. 반면 color, background-color 같은 속성 변경은 상대적으로 가볍다.

퍼블리셔가 이걸 왜 알아야 할까?

지금 당장은 "그래서 뭐?" 싶을 수 있다. 하지만 이 기초가 나중에 큰 차이를 만든다.

React는 화면을 업데이트할 때 가상 DOM이라는 걸 쓴다. 진짜 DOM을 직접 건드리는 대신, 가상의 복사본에서 먼저 변경 사항을 계산하고 최소한의 변경만 실제 DOM에 반영한다.

왜 이렇게 할까? 바로 리플로우와 리페인트가 비싸기 때문이다. DOM을 직접 자주 건드리면 브라우저가 레이아웃 계산과 페인트를 반복해야 하고, 화면이 느려진다.

브라우저 렌더링 과정을 알면, 가상 DOM이 왜 등장했는지가 자연스럽게 이해된다. 다음 글에서는 자바스크립트 엔진이 코드를 어떻게 실행하는지 살펴본다.

브라우저는 HTML을 어떻게 화면에 그릴까? — 디코드랩(DCODELAB)