6부 · 브라우저는 어떻게 화면을 그리나

← 5부 | 목차 | 다음: 7부 · 전체 흐름과 다음 단계 →

지금 다루는 단계: ⑥ 렌더링 — 받은 HTML·CSS·JS가 어떻게 눈에 보이는 픽셀이 되는가.

3부에서 서버가 HTML을 보내줬고, 4·5부에서 그 HTML·CSS·JS를 작성해 봤습니다. 그런데 1부에서 한 약속 — "그림은 서버가 아니라 브라우저가 그린다" — 가 실제로 어떻게 일어나는지는 아직 안 봤죠. 이번 부의 주제입니다.

브라우저는 설명서를 받아 조립하는 공장

브라우저가 받는 것은 완성된 이미지가 아니라 텍스트(HTML·CSS·JS)라는 설명서입니다. 브라우저는 이 설명서를 여러 공정을 거쳐 화면으로 조립합니다. 마치 가구 조립 설명서를 보고 실제 가구를 세우는 것과 같죠.

flowchart LR
    HTML["HTML 텍스트"]:::doc --> DOM["DOM 만들기<br/>(요소 나무)"]:::render
    CSS["CSS 텍스트"]:::doc --> CSSOM["스타일 규칙<br/>정리"]:::render
    DOM --> RENDER["둘을 합쳐<br/>'무엇을 어디에 어떻게'<br/>계산"]:::render
    CSSOM --> RENDER
    RENDER --> LAYOUT["배치 계산<br/>(레이아웃)"]:::render
    LAYOUT --> PAINT["픽셀로 칠하기<br/>(페인트)"]:::good

    classDef doc fill:#e9d8fd,stroke:#6b46c1,color:#000
    classDef render fill:#cfe8ff,stroke:#2b6cb0,color:#000
    classDef good fill:#c6f6d5,stroke:#276749,color:#000

순서대로 풀어 보겠습니다.

1단계 · DOM: HTML을 '나무'로 바꾸기

브라우저는 HTML 텍스트를 위에서부터 읽으며, 4부에서 본 중첩 구조를 그대로 살린 나무(tree) 모양의 객체로 변환합니다. 이것이 DOM(Document Object Model, 문서 객체 모델)입니다.

예를 들어 이 HTML은:

HTML
<body>
  <h1>제목</h1>
  <p>문단 <strong>강조</strong></p>
</body>

이런 나무가 됩니다:

flowchart TD
    body["body"]:::doc
    h1["h1<br/>'제목'"]:::doc
    p["p"]:::doc
    text["'문단 '"]:::doc
    strong["strong<br/>'강조'"]:::doc

    body --> h1
    body --> p
    p --> text
    p --> strong

    classDef doc fill:#e9d8fd,stroke:#6b46c1,color:#000

왜 굳이 나무로 만들까요? 두 가지 이유가 있습니다.

  1. 부모-자식 관계가 그대로 표현되어, 배치와 스타일 상속을 계산하기 좋습니다.
  2. JavaScript가 이 나무를 만질 수 있습니다. 5부에서 document.getElementById(...)로 요소를 찾아 글자를 바꿨죠? 그 document가 바로 이 DOM 나무의 뿌리입니다. JS는 이 나무의 가지를 더하고, 빼고, 바꾸며 페이지를 살아 움직이게 합니다.
DOM은 'HTML의 살아있는 버전'입니다. HTML은 처음 배달된 설명서, DOM은 브라우저 안에서 그걸 조립해 만든 — 그리고 JS로 계속 고칠 수 있는 — 실물 구조입니다.

2단계 · 스타일 합치기

브라우저는 CSS도 읽어 "어떤 요소에 어떤 모양을 줄지" 규칙표로 정리합니다(이걸 CSSOM이라 부르지만 용어는 안 외워도 됩니다). 그런 다음 DOM 나무의 각 요소에 해당 스타일을 짝지어, "이 <h1>은 남색·가운데 정렬"처럼 '무엇을 어떻게 그릴지'가 정해진 최종 목록을 만듭니다.

3단계 · 레이아웃: 어디에 놓을지 계산

이제 각 요소가 화면의 어느 위치에, 얼마만 한 크기로 놓일지를 계산합니다. 이를 레이아웃(layout) 또는 '리플로(reflow)'라고 합니다.

화면 너비가 얼마인지, 어떤 요소가 어떤 요소 아래·옆에 오는지, 여백은 얼마인지를 따져 모든 요소의 좌표와 크기를 정합니다. 창 크기를 줄이면 글이 다시 줄바꿈되며 재배치되죠? 그게 레이아웃이 다시 계산되는 모습입니다.

4단계 · 페인트: 실제로 칠하기

위치와 크기가 정해지면, 마지막으로 브라우저가 화면에 실제 픽셀을 칠합니다(paint). 글자, 색, 테두리, 이미지가 이때 비로소 눈에 보이는 그림이 됩니다.

여기까지가 1부에서 약속한 ⑥ 렌더링의 전모입니다. 정리하면: HTML → DOM 나무 → 스타일 결합 → 위치 계산 → 픽셀 칠하기.

페이지 하나에 요청이 여럿인 이유

3부의 실습에서 "페이지 하나 여는 데 요청이 줄줄이 나간다"고 했죠. 이제 이유가 보입니다. 브라우저가 HTML을 읽다가 중간에 이런 줄을 만나면:

HTML
<img src="profile.jpg" alt="...">
<link rel="stylesheet" href="style.css">
<script src="app.js"></script>

각 파일(이미지·CSS·JS)을 받으려고 추가 HTTP 요청을 또 보냅니다. 즉 HTML은 "이런 부품들이 더 필요하다"는 주문 목록이기도 한 셈입니다. 부품이 다 도착해야 페이지가 완성됩니다. 이것이 앞 부들의 개요도에서 "추가 파일 요청" 점선이 의미하던 것입니다.

sequenceDiagram
    participant B as 브라우저
    participant S as 서버
    B->>S: GET /index.html
    S-->>B: HTML 본문
    Note over B: HTML 읽다가 img·css·js 발견
    B->>S: GET /style.css
    B->>S: GET /profile.jpg
    B->>S: GET /app.js
    S-->>B: 각각의 파일 응답
    Note over B: 모두 모아 DOM·스타일 완성 → 렌더링

왜 어떤 페이지는 늦게 뜨나: 렌더 차단

여기까지 오면 "왜 어떤 사이트는 글이 늦게 뜨고 깜빡일까?"를 이해할 단서가 생깁니다. 브라우저는 HTML을 위에서부터 읽다가 CSS·JS를 만나면, 경우에 따라 그 파일을 다 받을 때까지 그리기를 잠시 멈춥니다. 이를 '렌더 차단(render-blocking)'이라고 합니다.

  • CSS: 보통 먼저 다 받아야 합니다. 스타일을 모르고 그렸다가 다시 그리면 화면이 번쩍이니까요.
  • JavaScript: <script>를 만나면 기본적으로 거기서 멈추고 JS를 먼저 처리합니다. 그래서 무거운 JS가 위에 있으면 페이지가 늦게 뜹니다.

그래서 실무에서는 <script><body> 끝에 두거나(우리 실습이 그랬죠), defer(문서 다 읽은 뒤 실행)·async(따로 받아 준비되면 실행) 같은 표시를 붙여 차단을 줄입니다. 지금은 "JS 위치가 속도에 영향을 준다"는 감만 잡아도 충분합니다.

HTML
<!-- 문서를 다 읽은 뒤 실행 → 화면을 막지 않음 -->
<script src="app.js" defer></script>

한 번 그리면 끝이 아니다: 리플로와 리페인트

렌더링은 첫 화면에서 끝나지 않습니다. 5부에서 JS로 글자를 바꾸거나 요소를 추가했죠? 그렇게 DOM이나 스타일이 바뀌면 브라우저는 영향받은 부분을 다시 계산하고 다시 칠합니다.

  • 리플로(reflow): 위치·크기가 바뀌어 레이아웃을 다시 계산하는 것 (비용이 큼).
  • 리페인트(repaint): 위치는 그대로고 색 같은 겉모습만 다시 칠하는 것 (상대적으로 가벼움).

창 크기를 바꾸거나, 목록에 항목을 추가하거나, 애니메이션이 돌 때 이 과정이 반복됩니다. 지금 단계에서는 "화면이 바뀔 때마다 브라우저가 다시 일한다"는 사실만 기억하면 됩니다.

클라이언트와 서버, 일은 어디서 도나

여기서 초보가 자주 헷갈리는 점을 정리하겠습니다. 웹의 일 처리는 두 장소에서 나뉘어 일어납니다.

구분어디서 도나무엇을 하나
클라이언트 측<br/>(프런트엔드)내 브라우저DOM 조립, 렌더링, JS 동작버튼 클릭 반응, 화면 꾸미기
서버 측<br/>(백엔드)멀리 있는 서버데이터 저장·조회, 페이지 생성로그인 확인, 글 목록 불러오기

우리 실습 페이지는 전부 클라이언트 측에서 돕니다(파일을 더블클릭해 브라우저가 다 처리했죠). 반면 로그인·결제·검색처럼 데이터와 보안이 걸린 일은 서버 측에서 처리해야 합니다 — 그래야 비밀번호나 데이터를 안전하게 다룰 수 있으니까요. 이 둘을 합쳐 만드는 게 진짜 웹 서비스입니다.

이 부에서 기억할 것

  • 브라우저는 텍스트 설명서(HTML·CSS·JS)를 받아 DOM 나무 → 스타일 결합 → 레이아웃 → 페인트 순으로 화면을 조립한다.
  • DOM은 HTML의 '살아있는 나무 버전'이고, JavaScript는 이 나무를 만져 페이지를 바꾼다.
  • HTML 안의 이미지·CSS·JS는 각각 추가 요청으로 받아 온다(그래서 요청이 여럿).
  • CSS·JS는 렌더 차단을 일으킬 수 있어, JS는 보통 문서 끝이나 defer로 둔다.
  • 화면이 바뀌면 브라우저는 리플로(재배치)·리페인트(재칠)로 다시 그린다.
  • 일은 클라이언트 측(브라우저)서버 측(백엔드)으로 나뉜다. 보안·데이터가 걸리면 서버의 몫.

마지막 부에서는 1부부터 6부까지를 하나의 흐름으로 다시 꿰고, 우리 실습 페이지를 세상에 공개하는 법과 더 배울 방향을 안내합니다.

7부 · 전체 흐름과 다음 단계