티스토리 뷰

Frontend

Vue3, Nuxt3 - Suspense

hjkang 2026. 2. 6. 14:55

Suspense

<Suspense> 는 비동기 컴포넌트가 로드되는 동안 fallback UI를 보여주는 Vue의 내장 컴포넌트

아직 실험적 기능이라 프로덕션 환경에서는 신중한 사용 필요

 

 

기존 방식

- 매번 loading, error 상태 직접 관리

- 중복된 코드

<template>
  <div>
    <div v-if="loading">로딩 중...</div>
    <div v-else-if="error">에러 발생: {{ error }}</div>
    <div v-else>
      <UserProfile :data="userData" />
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const loading = ref(true)
const error = ref(null)
const userData = ref(null)

onMounted(async () => {
  try {
    const response = await fetch('/api/user')
    userData.value = await response.json()
  } catch (e) {
    error.value = e.message
  } finally {
    loading.value = false
  }
})
</script>

 

Suspense를 사용한 방식

- 로딩 상태 자동 관리

<template>
  <Suspense>
    <template #default>
      <UserProfile />
    </template>
    <template #fallback>
      <div>로딩 중...</div>
    </template>
  </Suspense>
</template>

<script setup>
// 로딩 상태 관리 불필요
</script>

 

 

Vue3 에서의 사용

<!-- UserProfile.vue -->
<template>
  <div class="profile">
    <h2>{{ user.name }}</h2>
    <p>{{ user.email }}</p>
  </div>
</template>

<script setup>
// async를 사용하면 Suspense가 자동으로 대기
const response = await fetch('/api/user')
const user = await response.json()
</script>
<!-- App.vue -->
<template>
  <Suspense>
    <!-- 비동기 컴포넌트 -->
    <template #default>
      <UserProfile />
    </template>
    
    <!-- 로딩 중 표시할 UI -->
    <template #fallback>
      <div class="loading">
        <span class="spinner"></span>
        사용자 정보를 불러오는 중...
      </div>
    </template>
  </Suspense>
</template>

<script setup>
import UserProfile from './components/UserProfile.vue'
</script>

<style scoped>
.loading {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 20px;
}

.spinner {
  width: 20px;
  height: 20px;
  border: 2px solid #f3f3f3;
  border-top: 2px solid #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
</style>

 

여러 비동기 컴포넌트 처리 가능

<template>
  <Suspense>
    <template #default>
      <div class="dashboard">
        <!-- 모든 컴포넌트가 로드될 때까지 대기 -->
        <UserProfile />
        <Statistics />
        <RecentActivity />
      </div>
    </template>
    
    <template #fallback>
      <DashboardSkeleton />
    </template>
  </Suspense>
</template>

<script setup>
// 각 컴포넌트가 async setup을 가짐
import UserProfile from './UserProfile.vue'
import Statistics from './Statistics.vue'
import RecentActivity from './RecentActivity.vue'
import DashboardSkeleton from './DashboardSkeleton.vue'
</script>

 

 

Nuxt3에서의 사용

<!-- pages/users/[id].vue -->
<template>
  <div>
    <h1>사용자 프로필</h1>
    
    <Suspense>
      <template #default>
        <UserDetail :userId="route.params.id" />
      </template>
      
      <template #fallback>
        <UserDetailSkeleton />
      </template>
    </Suspense>
  </div>
</template>

<script setup>
const route = useRoute()
</script>
<!-- components/UserDetail.vue -->
<template>
  <div class="user-detail">
    <img :src="user.avatar" :alt="user.name" />
    <h2>{{ user.name }}</h2>
    <p>{{ user.bio }}</p>
  </div>
</template>

<script setup>
const props = defineProps<{ userId: string }>()

// Nuxt의 useFetch는 자동으로 Suspense를 지원
const { data: user } = await useFetch(`/api/users/${props.userId}`)
</script>

'Frontend' 카테고리의 다른 글

CSS Layout 정리  (0) 2026.02.23
React - useMemo & useCallback  (0) 2026.02.11
Vue3, Nuxt3 - Image Lazy Loading  (0) 2026.02.06
Vue3 - vue3-virtual-scroller  (0) 2026.02.03
Nuxt3 - Google Tag Manager(GTM) 연동  (0) 2026.01.20
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함