simoong·blog

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

image

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