Document Object Model(문서 객체 모델) 이란 HTML 문서를 객체 형태로 표현한 트리 구조를 말한다. HTML은 단순한 문서형 마크업 언어이기 떄문에 스스로 데이터를 조작하거나 동작을 처리할 수 없다.
따라서 브라우저는 HTML을 DOM으로 변환하여, JavaScript가 웹 문서를 동적으로 제어할 수 있도록 한다.
DOM은 단순히 HTML의 일부가 아니라, 브라우저가 웹페이지를 이해하고 조작하는 방식 그 자체이다.
<!DOCTYPE html>
<html>
<head>
<title>My title</title>
</head>
<body>
<h1>A heading</h1>
<a href="https://example.com">Link text</a>
</body>
</html>
예시로, 위와같은 HTML코드를 브라우저는 이를 아래과 같은 트리 구조(Tree Structure)로 해석한다.
즉, DOM은 개발자가 브라우저 안의 문서를 프로그래밍적으로 다루게 해주는 “인터페이스” 역할을 한다.


브라우저 랜더링 PIPE-LINE은 JS → Style → Layout → Paint → Composite 순으로 진행된다.
작은 변화라도 DOM 내부를 수정했다면 위와같은 전체과정을 거치게 되는데 , 해당 부분만 재랜더링하는 것이 아닌 트리 전체를 다시 랜더링하기에 많은 리플로우/리페인팅 비용이 발생한다 .
리플로우 (Reflow) = 레이아웃 재계산
요소의 크기나 위치가 바뀌면, 브라우저가 화면 전체(또는 일부)의 배치를 다시 계산하는 과정
리페인트 (Repaint) = 화면 다시 그리기
요소의 시각적 스타일(색상, 배경, 테두리 등) 이 바뀌었을 때, 브라우저가 픽셀을 다시 칠하는 과정
DOM은 전체 문서 단위로 갱신되기떄문에 DOM 상태와 실제 UI 상태의 일관성 유지가 어렵다.
복잡한 UI일수록 “어디가 언제 업데이트되었는지” 관리가 어려울 수 밖에 없다.
위와 같은 DOM의 한계를 해결하기 위해 REACT, VUE.JS 등은 Virtual DOM을 도입하였다.
Virtual DOM은 JS 객체 형태이며 실제 DOM의 가벼운 사본을 메모리 상에서 관리하는 구조이다.
React나 Vue.js 같은 라이브러리는 렌더링 시 매번 실제 DOM을 직접 수정하지 않고,
먼저 Virtual DOM을 갱신한 뒤 변화된 부분만 실제 DOM에 반영(diff & patch) 한다.
--- 랜더링 트리 생성 -> Diffing(비교) -> Reconciliation(조정) ---
총 3가지 과정을 통해 동작하게 되며 변경사항을 기반으로 새로운 가상 돔 트리를 메모리에 생성하고 Diff 알고리즘을 이용해 이전 Virtual DOM과 새 Virtual DOM을 비교하여 어떤 부분이 바뀌었는지를 효율적으로 찾아낸다.
각 노드를 “key” 속성으로 추적하여 최소한의 연산으로 차이를 계산한다.
차이점이 발견되면 React는 해당 변경 사항만 실제 DOM에 반영하게 되는데, 위와같은 과정으로 Reflow/Repaint 비용을 최소화할 수 있다.
기존 DOM 방식
const element = document.getElementById('count');
let count = 0;
function increase() {
count++;
element.textContent = count; // 실제 DOM 직접 수정
}
이 경우 버튼 클릭마다 실제 DOM 노드가 수정되고 매번 Reflow와 Repaint가 발생할 수 있다.
React + Virtual DOM 방식
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
React는 상태(count)가 변할 때마다 Virtual DOM을 새로 그리지만,
실제 DOM에는 변경된
태그의 텍스트 노드만 반영한다. 이로 인해 전체 렌더링 파이프라인이 다시 돌지 않고, 필요한 최소 작업만 수행된다.
구분 기존 DOM Virtual DOM 변경 처리 방식 직접 DOM 수정 메모리 상에서 변경 후 최소 패치 렌더링 비용 매번 전체 렌더링 변경된 부분만 업데이트 성능 효율 Reflow/Repaint 잦음 불필요한 렌더링 최소화 코드 유지보수 명령형(Imperative) 선언형(Declarative) 복잡도 DOM 상태 직접 관리 상태 기반 UI로 간결화
Virtual DOM이 항상 빠른 것은 아니다.
단순한 구조에서는 diffing 계산이 오히려 오버헤드가 될 수 있다.
하지만 일반적으로 대규모 SPA나 상태 변화가 잦은 애플리케이션에서는
Virtual DOM의 장점이 훨씬 더 크게 작용한다.
Virtual DOM의 핵심은 이전 Virtual DOM과 새로운 Virtual DOM을 비교(diff) 하는 것이다.
이 비교 과정은 아무리 최적화되어 있어도 연산 자체가 추가 비용으로 존재한다.
React의 diff 알고리즘은 O(n) 복잡도를 가진다.
하지만 컴포넌트가 많아지거나 트리가 깊어지면, 이 “O(n)”도 무시할 수 없을 만큼 커질 수 있다.
즉, Virtual DOM은 "실제 DOM을 건드리지 않음으로써 절약한 비용"을 메모리 상의 비교 연산으로 치환한 구조다.
따라서 DOM 조작이 많지않은 경우에는 (흔치않겠지만) diff 계산이 불필요한 오버헤드가 될 수 있다.
React의 diff 알고리즘은 완벽하게 “최소 변경”을 계산하지 않는다.
그 이유는 완전한 최적화 탐색은 O(n³) 수준으로 너무 비싸기 때문이다.
그래서 React는 heuristic기반의 근사 알고리즘을 사용한다.
즉, 빠른 대신 완벽하지 않다.
때문에 특정 상황에서는 예상보다 더 많은 실제 DOM 업데이트가 발생할 수 있다.
<ul>
<li key="1">A</li>
<li key="2">B</li>
<li key="3">C</li>
</ul>
예를 들어 이 리스트에서 첫 번째 요소 A를 삭제하면,
React는 key를 기준으로 효율적으로 변경을 감지하지만
key를 쓰지 않거나 순서가 복잡하게 바뀌면 전체 리스트를 재생성할 수도 있다.
결국 DOM과 Virtual DOM의 차이는 단순히 “빠르냐 느리냐”의 문제가 아니라,
“어떻게 효율적으로 UI 상태를 관리하고 반영할 것인가”의 문제다.
DOM은 브라우저의 근본적인 구조이자 웹의 기본 단위이지만, 직접 조작 시 성능 비용이 크다.
Virtual DOM은 이러한 구조적 한계를 완화하기 위한 추상화 계층으로, 대부분의 경우 훨씬 효율적이다.
그러나 Diff 연산 비용과 완벽하지 않은 최적화 한계를 가진다는 점에서,
언제나 절대적인 해답은 아니며 상황에 따라 적절히 선택하는 것이 중요하다.