성능 최적화
성능 최적화는 "측정 → 병목 식별 → 최적화 → 재측정"의 순서를 따라야 합니다. 추측 기반 최적화는 시간 낭비입니다.
면접에서 "Vue 앱의 성능을 어떻게 최적화하나요?"라고 물으면, 구체적인 기법과 함께 "먼저 측정한다" 를 강조하면 좋은 인상을 줍니다.
렌더링 최적화
v-once — 한 번만 렌더링
<template>
<!-- 변하지 않는 대량의 컨텐츠 -->
<div v-once>
<h1>{{ staticTitle }}</h1>
<p>{{ staticDescription }}</p>
<!-- 이 블록은 초기 렌더링 후 diff에서 완전히 제외 -->
</div>
</template>
v-memo — 조건부 캐싱
<template>
<!-- deps 배열의 값이 변하지 않으면 캐시된 VNode 재사용 -->
<div v-for="item in items" :key="item.id" v-memo="[item.selected]">
<p>{{ item.name }}</p>
<span>{{ item.selected ? '선택됨' : '미선택' }}</span>
<!-- item.selected가 변할 때만 리렌더링 -->
</div>
</template>
shallowRef / shallowReactive
import { shallowRef } from 'vue'
// 대량 데이터에서 깊은 반응형이 불필요할 때
const bigList = shallowRef<Array<{ id: number; value: string }>>([])
// 데이터 교체로 업데이트 트리거
bigList.value = [...newData]
컴포넌트 분할
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
// 무거운 컴포넌트는 비동기 로딩
const HeavyChart = defineAsyncComponent(() =>
import('./HeavyChart.vue')
)
const HeavyEditor = defineAsyncComponent(() =>
import('./HeavyEditor.vue')
)
</script>
<template>
<!-- 필요할 때만 로드 -->
<HeavyChart v-if="showChart" :data="chartData" />
<HeavyEditor v-if="editMode" />
</template>
가상 스크롤
수천 개의 아이템을 렌더링할 때는 화면에 보이는 것만 렌더링합니다.
<script setup lang="ts">
// vue-virtual-scroller 사용 예시
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
const items = ref(Array.from({ length: 10000 }, (_, i) => ({
id: i,
text: `아이템 ${i}`
})))
</script>
<template>
<RecycleScroller
:items="items"
:item-size="50"
key-field="id"
v-slot="{ item }"
>
<div class="item">{{ item.text }}</div>
</RecycleScroller>
</template>
computed 캐싱 활용
// 나쁜 예: 매 렌더마다 필터링
// <div v-for="item in items.filter(i => i.active)" />
// 좋은 예: computed로 캐싱
const activeItems = computed(() =>
items.value.filter(i => i.active)
)
Props 안정성
불필요한 자식 컴포넌트 업데이트를 방지합니다.
<script setup lang="ts">
import { computed } from 'vue'
const items = ref([1, 2, 3, 4, 5])
// 나쁜 예: 매 렌더마다 새 배열 생성
// <ChildComponent :items="items.filter(i => i > 2)" />
// 좋은 예: computed로 안정적인 참조 유지
const filteredItems = computed(() => items.value.filter(i => i > 2))
</script>
<template>
<ChildComponent :items="filteredItems" />
</template>
이미지 최적화
<template>
<!-- 지연 로딩 -->
<img loading="lazy" src="/image.jpg" alt="설명" />
<!-- 뷰포트 내 이미지만 로드 -->
<img v-for="img in images" :key="img.id"
:src="isVisible(img.id) ? img.url : placeholder"
loading="lazy"
/>
</template>
성능 측정 도구
// Vue DevTools — 컴포넌트 렌더링 시간 확인
// app.config.performance — 브라우저 Performance API에 마커 추가
const app = createApp(App)
app.config.performance = true // 개발 모드에서만
// Chrome Performance 탭에서 Vue 컴포넌트별 렌더링 시간 확인 가능
면접 팁
- "먼저 측정한다" — 병목이 어디인지 모르고 최적화하는 것은 시간 낭비입니다
v-memo는 Vue 3.2에서 추가된 기능으로, 대량 리스트에서 매우 효과적입니다- 가상 스크롤을 "구현해본 적 있다"고 말할 수 있으면 실무 역량을 보여줄 수 있습니다
요약
Vue 성능 최적화는 v-once/v-memo로 불필요한 렌더링을 줄이고, shallowRef로 깊은 반응형 비용을 절약하고, 비동기 컴포넌트로 코드를 분할하고, 가상 스크롤로 대량 리스트를 처리합니다. 반드시 측정 먼저, 최적화 후, 재측정의 순서를 따르세요.
댓글 로딩 중...