사전 원본 트리
개념

원본 트리

gabury1고친 사람 github-actions[bot]

원본 트리는 브라우저가 웹 문서를 화면에 그리려고 읽어 들이는 재료입니다. 문서를 이루는 조각들을 부모와 자식으로 이어 둔 트리입니다. 스타일을 입히고 화면에 그리는 단계는 이 트리를 고치지 않고 읽기만 합니다. 화면에 그릴 사각형 박스들은 이 트리를 보고 따로 만듭니다.

쉽고 빠른 이해

원본 트리는 화면에 그리기 전의 문서입니다. 목록 안에 항목이 셋 있으면 원본 트리에서도 목록 아래에 항목 셋이 매달립니다.

문서의 구조와 생김새를 따로 두려면 스타일을 입히는 동안 손대지 않는 원본이 하나 있어야 합니다. 그래야 같은 문서를 화면에서는 두 단으로, 인쇄할 때는 한 단으로 그릴 수 있습니다. 원본 트리를 고치는 것은 스크립트의 몫입니다.

  1. 브라우저가 받은 웹 문서 파일을 읽어 원본 트리를 세웁니다
  2. 스타일 규칙을 원본 트리의 모양에 맞춰 조각마다 붙입니다
  3. 화면에 놓을 박스들의 트리를 따로 만듭니다
  4. 박스마다 크기와 위치를 정하고 칠합니다

대가도 있습니다. 스크립트가 원본 트리를 고치면 브라우저는 스타일과 박스를 다시 계산합니다. 트리를 자주 고치는 페이지일수록 이 계산이 늘어납니다.

스타일이 생각대로 안 걸리면 원본 트리를, 배치가 어긋나면 박스 트리를 봅니다.

상세

출판사는 작가의 원고를 받아 책으로 짭니다. 원고에는 장과 절과 문단이 차례대로 들어 있습니다. 편집자는 원고에 손대지 않고 편집 지침을 따라 글자 크기와 쪽 나눔을 정합니다. 같은 원고로 종이책도 만들고 전자책도 만듭니다.

이 비유에서 원고가 원본 트리입니다. 편집 지침은 CSS(Cascading Style Sheets) 규칙이고, 짜인 지면은 화면입니다. CSS 는 웹 문서의 생김새를 적는 언어입니다.

이 절은 짧은 웹 문서 하나를 원본 트리로 옮겨 보는 데서 시작합니다. 그다음 CSS 가 이 트리를 어떻게 읽는지, 화면에 놓을 박스와 어떻게 갈리는지, HTML 파일에 적힌 내용과 어디서 어긋나는지를 차례로 봅니다.

요소와 글자의 트리

HTML(HyperText Markup Language)은 웹 문서의 내용과 구조를 적는 언어입니다. HTML 로 적은 파일의 내용을 HTML 소스라고 부릅니다. 브라우저가 서버에서 받아 오는 것이 이 소스입니다.

<li> 처럼 꺾쇠괄호로 이름을 적은 표시를 태그라고 합니다. 여는 태그와 닫는 태그로 감싼 조각 하나를 요소라고 부릅니다. 요소 안에는 다른 요소나 글자가 들어갑니다.

브라우저는 HTML 소스를 앞에서부터 읽으며 요소와 글자를 부모와 자식으로 잇습니다. 소스를 읽어 구조를 세우는 이 일을 파싱이라고 합니다. 요소가 요소를 감싸는 관계가 그대로 부모와 자식이 됩니다.

트리는 맨 위 하나에서 아래로 부모와 자식이 갈라져 내려가는 구조입니다. 그 안에 매달린 하나하나를 노드라고 부릅니다. 감싸고 감싸이는 관계를 담기에 알맞아서 문서를 이 모양으로 세웁니다. 파싱이 끝나면 요소와 글자가 저마다 노드가 되어 트리 하나를 이루고, 이 트리가 원본 트리입니다.

할 일 목록 하나를 적은 짧은 문서로 봅니다.

HTML
<ul>
  <li>주문</li>
  <li>결제</li>
  <li>배송</li>
</ul>

이 문서를 원본 트리로 옮기면 아래 모양이 됩니다.

flowchart TD
    U["ul 요소"] --> L1["li 요소"]
    U --> L2["li 요소"]
    U --> L3["li 요소"]
    L1 --> T1["글자 · 주문"]
    L2 --> T2["글자 · 결제"]
    L3 --> T3["글자 · 배송"]

ul 요소 아래에 li 요소 셋이 매달립니다. li 마다 그 아래에 글자 노드가 하나씩 붙습니다. 글자 노드는 텍스트 노드라고도 부릅니다. 글자 노드는 자식을 가질 수 없어서 언제나 트리의 끝에 섭니다. 태그 사이의 줄바꿈과 공백도 글자 노드로 들어가지만 그림에서는 뺐습니다.

웹 페이지에서는 DOM(Document Object Model, 문서 객체 모델)이 원본 트리입니다. DOM 은 트리의 노드 하나하나를 프로그램이 읽고 고칠 수 있는 객체로 다룹니다. 자바스크립트로 document.querySelector 를 불러 요소를 찾을 때 뒤지는 트리가 이것입니다.

원본 트리가 꼭 DOM 이어야 하는 것은 아닙니다. CSS 는 요소와 글자로 된 트리라면 무엇이든 읽습니다. XML(eXtensible Markup Language) 문서나 SVG(Scalable Vector Graphics) 그림에 CSS 를 입힐 수 있는 까닭입니다. 실무에서 만나는 원본 트리는 거의 언제나 DOM 입니다.

CSS 가 원본 트리를 읽는 방식

CSS 규칙은 원본 트리의 모양을 보고 어느 요소에 걸릴지 정합니다. 이 소절은 그 방식을 둘로 나눠 봅니다. 규칙이 걸릴 요소를 고르는 선택자와, 값이 부모에서 자식으로 내려가는 상속입니다.

선택자는 규칙이 걸릴 요소를 고르는 식입니다. 부모와 자식, 형제 사이의 순서 같은 관계를 써서 요소를 고릅니다. 이 관계는 모두 원본 트리에서 읽습니다. 앞의 목록에 규칙 두 줄을 걸어 봅니다. 줄마다 오른쪽 주석이 그 규칙이 고른 요소입니다.

CSS
ul > li { padding: 4px; }      /* li 셋 */
li:first-child { color: red; } /* 주문 */

첫 줄의 ul > li 는 ul 바로 아래 자식인 li 를 고릅니다. 그래서 셋 모두에 안쪽 여백이 붙습니다. 둘째 줄의 li:first-child 는 형제 가운데 첫째인 li 만 고릅니다. 두 규칙 모두 HTML 소스가 아니라 원본 트리의 모양을 보고 요소를 고릅니다.

상속도 원본 트리를 따라 흐릅니다. ul 에 글자 색을 주면 그 아래 li 와 글자들이 같은 색을 물려받습니다. 값이 내려가는 길은 원본 트리의 부모에서 자식으로 난 가지입니다. 글자 색처럼 물려받기로 정해진 속성만 이렇게 내려갑니다.

브라우저는 고른 규칙과 물려받은 값을 모아 요소마다 속성의 최종 값을 정합니다. 이 값의 묶음을 계산된 스타일이라고 부릅니다. 계산된 스타일은 원본 트리의 요소마다 하나씩 붙습니다.

원본 트리와 박스 트리

CSS 는 원본 트리를 읽기만 하고 고치지 않습니다. 화면에 놓을 것은 원본 트리 옆에 새 트리로 따로 세웁니다. 이 소절은 그 새 트리가 원본 트리와 어떻게 이어지고 어디서 갈리는지를 봅니다.

박스는 화면에서 사각형 하나를 차지하는 단위입니다. 개요에서 화면에 그릴 사각형이라 부른 것이 이 박스입니다. 박스를 부모와 자식으로 이은 트리가 박스 트리입니다.

브라우저는 박스마다 크기와 위치를 정합니다. 이 단계를 레이아웃이라고 하고, 우리말로 배치라고도 부릅니다. 배치가 끝나야 박스가 화면 어디에 놓일지 정해집니다.

마지막으로 브라우저는 위치가 정해진 박스를 화면에 칠합니다. 이 단계를 페인트라고 합니다.

flowchart TD
    A["HTML 소스"] -->|파싱| B["원본 트리"]
    B --> C["계산된 스타일"]
    B --> D["박스 트리"]
    C --> D
    D -->|레이아웃 · 페인트| E["화면"]

원본 트리는 이 흐름의 맨 앞에 섭니다. 박스 트리는 원본 트리와 계산된 스타일을 함께 읽어 만듭니다. 뒤의 단계들은 원본 트리를 읽기만 하고 바꾸지 않습니다. 스크립트가 원본 트리를 바꾸면 그 뒤 단계를 다시 거칩니다.

두 트리는 대개 모양이 비슷하지만 짝이 어긋나기도 합니다. display 속성을 none 으로 준 요소는 박스를 만들지 않습니다. 화면에서는 사라지지만 원본 트리에는 남아 있습니다. 그래서 자바스크립트는 숨긴 요소도 찾아서 고칠 수 있습니다.

반대로 원본 트리에 없는 박스가 생기기도 합니다. 가상 요소는 CSS 만으로 요소 앞뒤에 꾸밈 조각을 덧붙이는 문법입니다. ::before 에 content 속성으로 글자를 넣으면 요소 앞에 그 글자가 보입니다. 원본 트리에는 그 글자를 담은 노드가 없어서 자바스크립트로는 찾을 수 없습니다.

CSS 가 원본 트리를 고치지 않기 때문에 같은 트리를 여러 방식으로 그릴 수 있습니다. 화면에서는 두 단으로 놓고 인쇄할 때는 한 단으로 놓는 식입니다. 매체마다 다른 규칙을 거는 CSS 문법이 미디어 쿼리입니다. 원본 트리는 하나이고 박스 트리만 매체마다 달라집니다.

HTML 소스와 원본 트리의 차이

원본 트리는 HTML 소스를 그대로 옮긴 것이 아닙니다. 둘이 어긋나는 길은 두 가지입니다. 브라우저가 소스를 고쳐 읽는 경우와 스크립트가 나중에 트리를 고치는 경우입니다.

HTML 을 읽는 파서는 틀린 마크업을 만나도 멈추지 않고 고쳐서 읽습니다. 표가 대표 예입니다. <table> 안에 <tr> 을 바로 적으면 원본 트리에는 그 사이에 tbody 요소가 끼어듭니다.

HTML
<table>
  <tr><td>1</td></tr>
</table>

이 소스는 원본 트리에서 아래 모양이 됩니다. 소스에 없던 tbody 가 table 과 tr 사이에 섭니다.

flowchart TD
    A["table 요소"] --> B["tbody 요소 · 소스에 없었다"]
    B --> C["tr 요소"]
    C --> D["td 요소"]
    D --> E["글자 · 1"]

그래서 CSS 에 table > tr 이라고 쓰면 아무 요소에도 걸리지 않습니다. 원본 트리에서 tr 의 부모는 table 이 아니라 tbody 이기 때문입니다. HTML 소스만 보고 선택자를 짜면 이렇게 빗나갑니다.

두 번째 길은 스크립트입니다. 자바스크립트는 문서를 다 읽은 뒤에도 원본 트리에 노드를 넣고 뺍니다. 브라우저의 소스 보기 메뉴는 처음 받은 HTML 소스를 보여 줍니다. 지금의 원본 트리를 보려면 개발자 도구의 요소 패널을 엽니다.

원본 트리가 바뀌면 브라우저는 바뀐 부분의 스타일과 박스를 다시 계산합니다. 박스가 달라지면 배치도 다시 해야 합니다. 배치를 다시 계산하는 일을 리플로우라고 부릅니다. 트리를 자주 고치는 페이지일수록 이 계산이 늘어납니다.

원본 트리를 떠올릴 때

스타일이 생각대로 안 걸리면 먼저 원본 트리를 봅니다. 선택자와 상속은 원본 트리의 모양만 따르기 때문입니다. 앞의 table > tr 처럼 HTML 소스와 트리가 어긋난 곳에서 원인이 자주 나옵니다.

배치가 어긋날 때는 박스 트리를 봅니다. 숨긴 요소와 가상 요소처럼 두 트리가 갈리는 곳에서 원인이 나옵니다. 어느 트리를 봐야 하는지 먼저 가르면 찾는 범위가 좁아집니다.

이름이 겹치는 소스 트리

원본 트리는 영어 source document tree 를 옮긴 이름입니다. source 를 소스로 옮기면 개발자에게 익숙한 다른 낱말과 겹칩니다.

빌드 도구나 버전 관리에서 말하는 소스 트리는 소스 코드 파일이 담긴 폴더 구조입니다. 이 문서의 원본 트리와는 상관이 없습니다.

관련 항목

원본 트리를 이루는 구성 요소

노드 · HTML 요소 · 텍스트 노드 · 트리 · DOM · 섀도 DOM

원본 트리를 담는 문서 형식

HTML · XML · SVG · XHTML

원본 트리를 세우고 고치는 수단

파싱 · 파서 · JavaScript · 가상 DOM · 개발자 도구

원본 트리의 모양을 읽는 CSS 문법

CSS · 선택자 · 상속 · 캐스케이딩 · 가상 요소 · display · 미디어 쿼리

원본 트리를 재료로 삼는 구조

박스 트리 · 렌더 트리 · 계산된 스타일 · 접근성 트리 · CSSOM

원본 트리 뒤에 이어지는 처리 단계

렌더링 · 레이아웃 · 페인트 · 합성 · 리플로우 · 리페인트 · 캔버스 · 렌더링 엔진

다른 이름: source document tree · document tree · 원본 문서 트리 · 문서 트리