Virtual DOM
가상 돔
DOM의 메모리 내 표현. 변경 사항만 실제 DOM에 적용. React 핵심.
가상 돔
DOM의 메모리 내 표현. 변경 사항만 실제 DOM에 적용. React 핵심.
Virtual DOM은 실제 DOM의 가벼운 JavaScript 객체 표현입니다. UI 변경이 필요할 때 먼저 Virtual DOM을 업데이트하고, 이전 Virtual DOM과 비교(diffing)하여 변경된 부분만 실제 DOM에 적용(reconciliation)합니다.
실제 DOM 조작은 비용이 큽니다. DOM을 변경하면 브라우저는 레이아웃 재계산, 리페인트, 리플로우를 수행하는데, 잦은 DOM 조작은 성능 저하를 야기합니다. Virtual DOM은 변경 사항을 배치 처리하여 DOM 조작 횟수를 최소화합니다.
React가 2013년 Virtual DOM 개념을 대중화했습니다. 컴포넌트가 렌더링되면 React는 Virtual DOM 트리를 생성하고, 이전 트리와 비교하여 차이점만 실제 DOM에 반영합니다. 이 과정을 Reconciliation이라 하며, React 18부터는 Fiber 아키텍처로 더 효율적으로 처리합니다.
Virtual DOM이 항상 빠른 것은 아닙니다. 단순한 변경에는 오히려 오버헤드가 될 수 있어요. Svelte는 Virtual DOM 없이 컴파일 타임에 최적화된 코드를 생성하고, Solid.js는 세밀한 반응성으로 Virtual DOM 없이 정확한 DOM 업데이트를 수행합니다. 프레임워크 선택 시 이런 차이를 이해하면 좋습니다.
// Virtual DOM 개념 이해를 위한 간단한 구현
// 1. Virtual DOM 노드 생성 함수
function createElement(type, props, ...children) {
return {
type,
props: props || {},
children: children.flat()
};
}
// 2. Virtual DOM → 실제 DOM 변환
function render(vNode) {
if (typeof vNode === 'string' || typeof vNode === 'number') {
return document.createTextNode(vNode);
}
const element = document.createElement(vNode.type);
// props 적용
Object.entries(vNode.props).forEach(([key, value]) => {
if (key.startsWith('on')) {
element.addEventListener(key.slice(2).toLowerCase(), value);
} else {
element.setAttribute(key, value);
}
});
// 자식 렌더링
vNode.children.forEach(child => {
element.appendChild(render(child));
});
return element;
}
// 3. 사용 예시 (JSX 없이)
const vdom = createElement('div', { class: 'container' },
createElement('h1', null, 'Hello Virtual DOM'),
createElement('button', { onClick: () => alert('클릭!') }, '클릭하세요')
);
// 4. 실제 DOM에 마운트
document.body.appendChild(render(vdom));
// React에서의 실제 동작 방식
// JSX → createElement 호출 → Virtual DOM 생성 → Diffing → DOM 업데이트
// <div>Hello</div> 는 React.createElement('div', null, 'Hello')로 변환됨
"Virtual DOM은 메모리상의 DOM 표현으로, 상태 변경 시 새 Virtual DOM을 만들어 이전과 비교합니다. 변경된 부분만 실제 DOM에 적용해서 불필요한 리렌더링을 줄입니다. React의 경우 key prop으로 리스트 아이템을 식별해서 효율적인 diffing을 돕습니다."
"Virtual DOM 자체가 성능 보장은 아니에요. 불필요한 리렌더링을 줄이려면 React.memo, useMemo, useCallback을 적절히 사용해야 합니다. 리스트에는 반드시 안정적인 key를 제공하고, 인덱스를 key로 쓰면 성능 저하와 버그가 생길 수 있어요."
"Svelte는 Virtual DOM 없이 컴파일 타임에 DOM 업데이트 코드를 생성해요. 런타임 오버헤드가 없어서 번들 크기와 실행 속도에서 유리합니다. 대신 React의 거대한 생태계와 비교하면 선택지가 제한적이죠."
Virtual DOM은 대규모 업데이트를 배치 처리하는 데 유리하지만, 단순한 변경에는 오히려 오버헤드입니다. Svelte, Solid.js는 이를 피하기 위해 다른 접근법을 사용합니다.
인덱스를 key로 쓰면 리스트 아이템 순서 변경 시 React가 잘못 매칭해서 버그와 성능 저하가 발생합니다. 고유한 id를 key로 사용하세요.
컴포넌트 분리로 리렌더링 범위를 줄이고, React DevTools Profiler로 병목을 찾은 후 메모이제이션을 적용하세요. 측정 없는 최적화는 낭비입니다.