Vue 3의 컴파일러는 템플릿을 분석하여 런타임 diff 비용을 대폭 줄이는 최적화를 자동으로 적용합니다. 이것이 Vue 3가 React보다 가상 DOM 오버헤드가 적은 핵심 이유입니다.

면접에서 "Vue 3가 Vue 2보다 빠른 이유"를 물어보면, 컴파일러 수준의 최적화를 구체적으로 설명할 수 있으면 깊이 있어 보입니다.


1. 정적 호이스팅 (Static Hoisting)

템플릿에서 변하지 않는 노드를 렌더 함수 밖으로 끌어올려, 매 렌더마다 다시 생성하지 않습니다.

VUE
<template>
  <div>
    <h1 class="title">고정된 제목</h1>
    <p>{{ dynamicMessage }}</p>
    <span class="badge">v3</span>
  </div>
</template>
JAVASCRIPT
// 컴파일러 출력 (개념적)
// 정적 노드를 모듈 스코프로 호이스팅
const _hoisted_1 = h('h1', { class: 'title' }, '고정된 제목')
const _hoisted_2 = h('span', { class: 'badge' }, 'v3')

function render(ctx) {
  return h('div', null, [
    _hoisted_1,           // 재생성 안 함 — 동일 VNode 재사용
    h('p', null, ctx.dynamicMessage),  // 동적 — 매번 생성
    _hoisted_2            // 재생성 안 함
  ])
}

2. PatchFlags

어떤 부분이 동적인지를 비트 플래그로 표시하여, diff 시 변경 가능한 부분만 검사합니다.

JAVASCRIPT
// PatchFlags 종류
const PatchFlags = {
  TEXT: 1,        // 텍스트만 동적
  CLASS: 2,       // class 바인딩만 동적
  STYLE: 4,       // style 바인딩만 동적
  PROPS: 8,       // 특정 props만 동적
  FULL_PROPS: 16, // 동적 키의 props
  NEED_HYDRATION: 32,
  STABLE_FRAGMENT: 64,
  KEYED_FRAGMENT: 128,
  UNKEYED_FRAGMENT: 256,
  NEED_PATCH: 512,
  DYNAMIC_SLOTS: 1024,
  HOISTED: -1,
  BAIL: -2
}

// 컴파일러 출력 예시
h('p', { class: dynamicClass }, dynamicText, PatchFlags.TEXT | PatchFlags.CLASS)
// → diff 시 텍스트와 클래스만 비교하면 됨

3. Block Tree

동적 노드만 평탄화된 배열로 추적하여, 트리 전체를 순회하지 않습니다.

VUE
<template>
  <div>                          <!-- Block root -->
    <h1>정적 제목</h1>           <!-- 건너뜀 -->
    <div>                        <!-- 건너뜀 -->
      <p>{{ message }}</p>       <!-- 동적 — Block에 등록 -->
      <span>고정 텍스트</span>   <!-- 건너뜀 -->
    </div>
    <button @click="handler">    <!-- 동적 — Block에 등록 -->
      {{ buttonText }}
    </button>
  </div>
</template>
PLAINTEXT
Block의 dynamicChildren:
[
  p (PatchFlag: TEXT),
  button (PatchFlag: TEXT)
]

→ diff 시 이 2개만 비교 (나머지 정적 노드 무시)

4. 캐시된 이벤트 핸들러

인라인 핸들러를 캐싱하여 불필요한 자식 컴포넌트 업데이트를 방지합니다.

VUE
<template>
  <!-- 인라인 핸들러 — 매 렌더마다 새 함수 생성? -->
  <ChildComponent @click="() => handleClick(item.id)" />
</template>
JAVASCRIPT
// Vue 3 컴파일러는 핸들러를 캐시
function render(ctx) {
  return h(ChildComponent, {
    onClick: ctx._cache[0] || (ctx._cache[0] = () => ctx.handleClick(ctx.item.id))
  })
}
// → 같은 함수 참조를 유지하여 자식 컴포넌트가 불필요하게 리렌더링되지 않음

5. SSR 최적화

SSR에서는 가상 DOM을 거치지 않고 직접 문자열을 연결합니다.

JAVASCRIPT
// SSR 컴파일 결과 — 가상 DOM 대신 문자열 연결
function ssrRender(ctx) {
  return `<div class="container">` +
    `<h1>정적 제목</h1>` +
    `<p>${escapeHtml(ctx.message)}</p>` +
    `</div>`
}

Vue Template Explorer로 확인

Vue 공식 Template Explorer에서 컴파일러의 출력을 직접 확인할 수 있습니다.


면접 팁

  • React는 런타임에 모든 diff를 수행하지만, Vue는 컴파일 시점에 정적/동적을 분석 하여 diff 범위를 최소화합니다
  • 이것이 "컴파일러 기반 가상 DOM (Compiler-Informed Virtual DOM)"입니다
  • Svelte는 가상 DOM 없이 컴파일 시점에 직접 DOM 조작 코드를 생성하는데, Vue의 Vapor Mode는 이 방향에 가깝습니다

요약

Vue 3 컴파일러는 정적 호이스팅(VNode 재사용), PatchFlags(변경 가능 부분만 표시), Block Tree(동적 노드만 평탄 추적), 캐시된 핸들러(불필요한 업데이트 방지)로 런타임 성능을 최적화합니다. 이러한 "컴파일러 기반 가상 DOM"이 Vue 3의 성능 우위의 핵심입니다.

댓글 로딩 중...