Virtual DOM vs Real DOM — 진짜 성능 차이를 제대로 이해하기

Virtual DOM vs Real DOM — 진짜 성능 차이를 제대로 이해하기
최근에 렌더링 성능 관련해서 실험을 좀 했어요.
React로 컴포넌트를 만들다가 “Virtual DOM이 Real DOM보다 빠르다”는 말을 워낙 자주 들으니까,
정말 그런가? 싶어서 직접 테스트해봤죠. 결과는… 예상과 조금 달랐습니다.
💡 Virtual DOM, 정확히 뭐길래?
Virtual DOM은 말 그대로 **‘가상의 DOM’**이에요.
React나 Vue 같은 프레임워크에서 사용하는 구조로,
실제 브라우저 DOM을 바로 조작하는 대신 메모리 안에서 가짜 트리 구조를 만들어 관리합니다.
쉽게 말하면, “일단 메모리에서 계산하고, 바뀐 부분만 진짜 DOM에 반영하자”는 개념이에요.
// React 내부 개념 예시
const vdom1 = <div><p>Hello</p></div>
const vdom2 = <div><p>Hello, world!</p></div>
updateDOM(diff(vdom1, vdom2)) // 달라진 부분만 갱신이 방식을 쓰면 DOM 전체를 매번 갱신할 필요가 없기 때문에,
UI 변경이 잦을수록 효율적이에요.
🧩 Real DOM은 여전히 기본이죠
Real DOM은 우리가 평소에 다루는 브라우저의 실제 DOM이에요.
const el = document.createElement('div')
el.textContent = 'Hello DOM'
document.body.appendChild(el)단순하죠.
근데 문제는, 이렇게 직접 DOM을 건드릴 때마다 브라우저는 스타일 계산 → 레이아웃 → 페인트 과정을 다시 거친다는 겁니다.
이게 바로 우리가 흔히 말하는 “Reflow”나 “Repaint”의 원인이에요.
즉, DOM을 자주 바꾸면 성능이 급격히 떨어진다는 얘기죠.
🧪 직접 해본 성능 실험
처음엔 단순하게 “DOM을 1만 개 만들면 누가 더 빠를까?”를 비교해봤어요.
결과는 의외였습니다.
🧱 Real DOM 버전
<div id="root"></div>
<script>
const root = document.getElementById('root')
const start = performance.now()
for (let i = 0; i < 10000; i++) {
const el = document.createElement('div')
el.textContent = i
root.appendChild(el)
}
const end = performance.now()
console.log('Real DOM Render Time:', end - start, 'ms')
</script>결과는 빠릅니다.
보통 200~500ms 정도로 찍혔어요.
화면이 약간 버벅이긴 해도, React보다는 훨씬 빨랐습니다.
⚛️ Virtual DOM (React) 버전
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script>
const { createElement } = React
const App = () => {
const items = Array.from({ length: 10000 }, (_, i) =>
createElement('div', { key: i }, i)
)
return createElement('div', null, items)
}
const start = performance.now()
ReactDOM.render(createElement(App), document.getElementById('root'))
const end = performance.now()
console.log('Virtual DOM Render Time:', end - start, 'ms')
</script>결과는… 생각보다 느렸습니다. 😅
보통 400~900ms 정도 걸렸어요.
이건 당연한 게, React는 내부적으로 Virtual DOM 트리와 Fiber 구조를 만들고
diff 계산까지 준비한 뒤, 마지막에 한 번에 DOM을 반영하거든요.
즉, 초기 렌더링만 보면 Virtual DOM이 손해를 봐요.
🔁 그런데 업데이트가 반복되면 얘기가 달라집니다
이제 상태가 계속 바뀌는 상황을 생각해볼게요.
아래 예시는 1만 개의 데이터 중 하나를 랜덤으로 바꿔주는 React 코드입니다.
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script>
const { createElement, useState } = React
const App = () => {
const [data, setData] = useState(Array.from({ length: 10000 }, (_, i) => i))
const updateRandom = () => {
const copy = [...data]
const random = Math.floor(Math.random() * 10000)
copy[random] = Math.random()
setData(copy)
}
return createElement(
'div',
null,
createElement('button', { onClick: updateRandom }, 'Update Random Item'),
...data.map((n, i) => createElement('div', { key: i }, n))
)
}
ReactDOM.render(createElement(App), document.getElementById('root'))
</script>이제 버튼을 눌러보면, React는 달라진 한 줄만 업데이트합니다.
Real DOM처럼 전체를 다시 그리지 않아요.
그래서 반복적인 상태 변경에서는 React가 훨씬 부드럽게 반응합니다.
첫 렌더는 느려도, 이후에는 Virtual DOM이 압도적으로 효율적이에요.
⚙️ 상황별 비교 요약
| 상황 | Real DOM | Virtual DOM |
|---|---|---|
| 최초 렌더링 | 빠름 | 느림 |
| 반복 업데이트 | 느림 | 빠름 |
| 메모리 사용량 | 적음 | 많음 |
| 적합한 케이스 | 정적인 UI | 동적인 UI (React, Vue) |
🧠 실무에서 느낀 점
Virtual DOM은 “빠르다”기보단 **“덜 느려지게 도와주는 구조”**에 가깝습니다.
렌더링을 완전히 없애는 게 아니라, 꼭 필요한 부분만 갱신하도록 설계된 거죠.
저는 보통 이렇게 정리해요👇
- 랜딩 페이지나 홍보용 사이트 → Real DOM (Vanilla JS)로 충분
- 대화형 웹앱(SPA) → Virtual DOM (React/Vue)
- 그래픽/차트/Canvas 기반 UI → Real DOM 직접 제어
결국 중요한 건 “무엇을 얼마나 자주 바꾸느냐”예요.
💬 마무리하며
Virtual DOM은 “빠르다”가 아니라 “똑똑하다”는 표현이 더 어울려요.
처음엔 느리지만, 계속 쓰다 보면 효율이 드러나는 구조거든요.
실제로 실험해보면 이 차이가 확실히 느껴집니다.
UI를 한 번에 다 바꾸는 Real DOM은 짧은 시간엔 빠르지만,
React처럼 상태가 계속 변하는 UI에서는 금세 한계를 보여요.
결국엔 둘 다 필요합니다.
중요한 건 “언제, 어떤 기준으로 선택할지”죠.
#VirtualDOM #RealDOM #React #렌더링성능 #브라우저렌더링 #프론트엔드성능 #Reflow #SPA #웹성능 #JavaScript