브라우저 동작원리 - DOM이 어떻게 그려지는가? (브라우저 렌더링 파이프)

ByEunwoo
javascript

🌐 브라우저 동작 원리 간단 정리

브라우저는 HTML, CSS, JavaScript를 받아 화면에 렌더링하는 복잡한 과정을 거친다.
이 과정을 잘 이해하면, 성능 최적화나 UI 디버깅 시 큰 도움이 된다.

브라우저 렌더링 파이프라인 (Rendering Pipeline)

브라우저 렌더링 파이프라인이란?

브라우저가 HTML, CSS, JS를 받아서
**사용자 눈에 보이는 화면(픽셀)**로 그리기까지의 **과정(파이프라인)**을 말한다.
즉, 우리가 index.html, style.css, app.js를 쓰면,
브라우저는 그걸 차례대로 "해석 → 변환 → 그리기" 하게 된다.

렌더링 파이프라인 단계 (6단계)

  1. HTML 파싱 → DOM(Document Object Model) 트리 생성
    브라우저는 HTML 코드를 위에서부터 읽으며, 각 요소를 트리 구조의 객체로 변환. (노드 기반의 트리 구조(DOM))
    DOM(Document Object Model)은 HTML 문서의 구조를 JS가 조작할 수 있게 만든 객체 모델이다.
    <script> 태그는 DOM 트리에 포함되지만, <style> 태그는 CSSOM을 생성하는 데 사용된다.
    DOM은 HTML 문서의 구조를 표현하기 때문에, <head>, <meta> 등 화면에 보이지 않는 요소도 포함된다.

  2. CSS 파싱 → CSSOM(CSS Object Model) 생성
    CSS 파일이나 <style> 태그의 내용을 분석해서 스타일 정보를 트리 형태로 만든다.
    CSSOM은 CSS 문서의 구조를 표현하는 객체 모델이다.

  3. DOM + CSSOM → 렌더 트리(Render Tree) 생성
    DOM의 구조와 CSSOM의 스타일을 결합하여, 실제 화면에 표시될 요소들만 포함하는 트리가 만들어진다.
    👉 display: none 요소는 이 과정에서 제외된다.

  4. 레이아웃 단계 (Reflow / Layout)
    렌더 트리를 바탕으로 각 요소의 위치와 크기를 계산한다.
    👉 부모의 크기에 따라 자식 크기가 바뀌는 경우, 전체 트리 재계산이 발생할 수 있다.

  5. 페인팅 단계 (Repaint / Paint)
    각 요소를 픽셀 단위로 시각화합니다. 배경색, 테두리, 그림자 등 스타일 요소가 이 단계에서 처리된다.

  6. 컴포지팅 단계 (Compositing)
    Paint 단계에서 처리된 레이어들을 조합해서 최종 화면에 표시한다.
    👉 GPU 가속이 활용될 수도 있다.

📌 DOM vs 렌더 트리: 개발자도구 기준으로 보기

DOM 트리 = 개발자도구의 요소 탭

  • HTML 전체 구조가 트리 형태로 표현됨
  • <head>, <meta>, <script> 등 화면에 안 보이는 요소도 포함
  • JavaScript로 동적으로 변경된 내용까지 반영됨

렌더 트리 = 화면에 실제 보이는 구조

  • DOM + CSSOM의 조합
  • display: none은 포함되지 않음
  • visibility: hidden이나 opacity: 0은 포함됨 (위치는 계산되지만, 시각적으로만 안 보임)

Reflow vs Repaint 정리

구분트리 영향발생 원인성능 비용
Reflow (Layout)DOM + CSSOM요소 크기, 위치 변경, DOM 삽입/삭제 등높음
Repaint (Paint)CSSOM색상, 그림자, 배경 등 스타일 변경중간
  • ❗ Reflow는 Repaint를 유발할 수 있지만, 그 반대는 아님
    예: width 변경 → Reflow + Repaint
    예: background-color 변경 → Repaint만 발생

💡 DOM 접근 최소화가 중요한 이유

DOM 조작 = 비용이 큰 연산

DOM은 브라우저 엔진에서 별도로 관리하는 복잡한 구조이기 때문에, JavaScript가 DOM을 조작하려면 브라우저와 "통신"해야 한다.

반복 DOM 조작은 특히 위험

다음과 같은 패턴은 성능 저하의 주범이 된다.

for (let i = 0; i < 1000; i++) {
  const item = document.createElement('li');
  item.textContent = `아이템 ${i}`;
  list.appendChild(item); // 매번 DOM에 접근해서 삽입
}

위 코드는 appendChild를 1000번 호출하면서, 매번 DOM이 갱신된다.
→ Reflow와 Repaint가 계속 발생해 브라우저의 렌더링 성능이 크게 떨어지고, 스크롤이나 애니메이션이 끊기는 현상이 생길 수 있다.

개선 방법 예시: DocumentFragment 활용

DOM 조작은 한 번에 모아서, 최소한으로 해야 브라우저 입장에서 더 효율적이다.
DocumentFragment는 실제 DOM에 붙기 전까진 브라우저에 영향을 주지 않는 가상의 DOM이다.

const fragment = document.createDocumentFragment();
 
for (let i = 0; i < 1000; i++) {
  const item = document.createElement('li');
  item.textContent = `아이템 ${i}`;
  fragment.appendChild(item); // 가상의 DOM에 먼저 저장
}
 
list.appendChild(fragment); // 단 한 번만 실제 DOM에 삽입

이 방식은 브라우저가 Reflow/Repaint를 단 한 번만 수행하게 도와주기 때문에
렌더링 최적화 측면에서 훨씬 더 효율적이다.

이 외에도 여러 방법이 있지만, 어떻게 하면 DOM 접근을 최소화할 수 있을지 고민하는 것이 중요하다.

Posted injavascript
Written byEunwoo