Suspense
Suspense는 비동기 의존성을 가진 컴포넌트 트리의 로딩 상태를 선언적으로 관리하는 내장 컴포넌트입니다. 아직 실험적(experimental) 기능입니다.
면접에서 "비동기 데이터 로딩의 로딩 상태를 어떻게 관리하나요?"라는 질문에, isLoading 플래그 외에 Suspense라는 선언적 대안을 언급할 수 있으면 좋습니다.
기본 사용법
<!-- App.vue -->
<template>
<Suspense>
<!-- 기본 슬롯 — 비동기 컴포넌트 -->
<template #default>
<AsyncDashboard />
</template>
<!-- 폴백 슬롯 — 로딩 중에 표시 -->
<template #fallback>
<div class="loading">
<p>대시보드를 불러오는 중...</p>
</div>
</template>
</Suspense>
</template>
비동기 컴포넌트와 Suspense
Suspense가 감지하는 비동기 의존성은 두 가지입니다.
1. async setup() 컴포넌트
<!-- AsyncDashboard.vue -->
<script setup lang="ts">
// script setup에서 최상위 await를 사용하면 자동으로 async setup이 됨
const response = await fetch('/api/dashboard')
const dashboardData = await response.json()
// 이 컴포넌트는 Suspense 내부에서만 사용 가능
</script>
<template>
<div>
<h1>대시보드</h1>
<p>총 사용자: {{ dashboardData.totalUsers }}</p>
<p>오늘 방문: {{ dashboardData.todayVisits }}</p>
</div>
</template>
2. defineAsyncComponent
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent(() =>
import('./HeavyChart.vue')
)
</script>
<template>
<Suspense>
<HeavyChart />
<template #fallback>
<div>차트 로딩 중...</div>
</template>
</Suspense>
</template>
중첩 Suspense
<template>
<!-- 외부 Suspense — 전체 레이아웃 로딩 -->
<Suspense>
<template #default>
<AsyncLayout>
<!-- 내부 Suspense — 컨텐츠만 로딩 -->
<Suspense>
<template #default>
<AsyncContent />
</template>
<template #fallback>
<p>컨텐츠 로딩 중...</p>
</template>
</Suspense>
</AsyncLayout>
</template>
<template #fallback>
<p>전체 레이아웃 로딩 중...</p>
</template>
</Suspense>
</template>
에러 처리 — onErrorCaptured
<script setup lang="ts">
import { ref, onErrorCaptured } from 'vue'
const error = ref<Error | null>(null)
onErrorCaptured((err) => {
error.value = err as Error
return false // 에러 전파 중단
})
</script>
<template>
<div v-if="error" class="error-state">
<p>에러 발생: {{ error.message }}</p>
<button @click="error = null">다시 시도</button>
</div>
<Suspense v-else>
<AsyncComponent />
<template #fallback>
<p>로딩 중...</p>
</template>
</Suspense>
</template>
Suspense 이벤트
<template>
<Suspense
@pending="onPending"
@resolve="onResolve"
@fallback="onFallback"
>
<AsyncComponent />
<template #fallback>
<p>로딩 중...</p>
</template>
</Suspense>
</template>
<script setup lang="ts">
const onPending = () => console.log('비동기 의존성 감지 — 대기 시작')
const onResolve = () => console.log('모든 비동기 의존성 해결 완료')
const onFallback = () => console.log('폴백 컨텐츠 표시됨')
</script>
실전 주의사항
1. 실험적 기능 — API가 변경될 수 있음
2. async setup 컴포넌트는 반드시 Suspense 내부에서 사용
3. 에러 처리를 위해 onErrorCaptured와 함께 사용
4. SSR에서는 서버에서 비동기 의존성을 해결한 후 HTML을 전송
면접 팁
- Suspense는 선언적 로딩 상태 관리 의 핵심입니다 — isLoading 플래그를 수동으로 관리하는 것보다 깔끔합니다
- React의 Suspense와 비교할 수 있으면 좋습니다 — 개념은 비슷하지만 Vue는 아직 실험적 단계입니다
- SSR 환경에서의 Suspense 동작을 이해하고 있으면 심화 질문에 대응할 수 있습니다
요약
Suspense는 async setup 컴포넌트나 defineAsyncComponent의 로딩 상태를 #fallback 슬롯으로 선언적으로 처리합니다. onErrorCaptured로 에러를 처리하고, 중첩 Suspense로 세분화된 로딩 UX를 구현할 수 있습니다. 아직 실험적 기능이므로 프로덕션에서는 주의가 필요합니다.
댓글 로딩 중...