레슨 10 / 11·5개 토픽
트랜지션과 비동기 컴포넌트
Transition 컴포넌트
Vue의 컴포넌트는 요소가 DOM에 추가되거나 제거될 때 CSS 전환 또는 애니메이션을 자동으로 적용합니다. v-if, v-show, 동적 컴포넌트 전환에 사용됩니다.
vue
<template>
<button @click="show = !show">토글</button>
<!-- name 속성으로 클래스 접두사 지정 -->
<Transition name="fade">
<div v-if="show" class="box">
안녕하세요!
</div>
</Transition>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const show = ref(true)
</script>
<style scoped>
/* 진입 시작 상태 */
.fade-enter-from {
opacity: 0;
transform: translateY(-20px);
}
/* 진입 활성 상태 (트랜지션 정의) */
.fade-enter-active {
transition: all 0.3s ease-out;
}
/* 진입 완료 상태 (생략 가능 — 기본값은 요소의 기본 스타일) */
.fade-enter-to {
opacity: 1;
transform: translateY(0);
}
/* 퇴장 시작 상태 */
.fade-leave-from {
opacity: 1;
transform: translateY(0);
}
/* 퇴장 활성 상태 */
.fade-leave-active {
transition: all 0.3s ease-in;
}
/* 퇴장 완료 상태 */
.fade-leave-to {
opacity: 0;
transform: translateY(20px);
}
</style>CSS 트랜지션 클래스 정리
- •
v-enter-from— 진입 시작 상태 (요소 추가 직전) - •
v-enter-active— 진입 트랜지션 활성 (transition/animation 정의) - •
v-enter-to— 진입 완료 상태 (트랜지션 끝) - •
v-leave-from— 퇴장 시작 상태 (요소 제거 직전) - •
v-leave-active— 퇴장 트랜지션 활성 - •
v-leave-to— 퇴장 완료 상태 (트랜지션 끝, 요소 제거) - •
name="fade"지정 시 접두사가fade-로 변경 (예:fade-enter-from)
TransitionGroup
은 v-for로 렌더링된 리스트의 항목이 추가, 제거, 이동될 때 트랜지션을 적용합니다. 각 항목에 반드시 고유한 key가 필요합니다.
vue
<template>
<div>
<input v-model="newItem" @keyup.enter="addItem" placeholder="항목 추가" />
<TransitionGroup name="list" tag="ul" class="item-list">
<li v-for="item in items" :key="item.id" class="item">
{{ item.text }}
<button @click="removeItem(item.id)">삭제</button>
</li>
</TransitionGroup>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Item { id: number; text: string }
const newItem = ref('')
const items = ref<Item[]>([
{ id: 1, text: '첫 번째 항목' },
{ id: 2, text: '두 번째 항목' },
])
let nextId = 3
const addItem = () => {
if (!newItem.value.trim()) return
items.value.push({ id: nextId++, text: newItem.value })
newItem.value = ''
}
const removeItem = (id: number) => {
items.value = items.value.filter(i => i.id !== id)
}
</script>
<style scoped>
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.list-enter-active,
.list-leave-active {
transition: all 0.4s ease;
}
/* FLIP 애니메이션: 이동하는 항목에 적용 */
.list-move {
transition: transform 0.4s ease;
}
/* 퇴장 항목이 레이아웃에서 빠지도록 */
.list-leave-active {
position: absolute;
}
</style>defineAsyncComponent
defineAsyncComponent를 사용하면 컴포넌트를 비동기적으로 로드하여 초기 번들 크기를 줄일 수 있습니다. 로딩/에러 상태를 세밀하게 제어할 수 있습니다.
vue
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
// 기본 사용법
const HeavyChart = defineAsyncComponent(
() => import('./components/HeavyChart.vue')
)
// 고급 옵션: 로딩/에러 컴포넌트 지정
const AdminPanel = defineAsyncComponent({
loader: () => import('./components/AdminPanel.vue'),
loadingComponent: () => import('./components/LoadingSpinner.vue'),
errorComponent: () => import('./components/ErrorMessage.vue'),
delay: 200, // 로딩 컴포넌트 표시 전 대기 시간 (ms)
timeout: 10000, // 타임아웃 시 에러 컴포넌트 표시 (ms)
})
</script>
<template>
<div>
<!-- 필요할 때만 로드 -->
<HeavyChart v-if="showChart" />
<!-- 로딩/에러 상태 자동 처리 -->
<AdminPanel />
</div>
</template>Suspense와 async setup()
Vue 3의 는 비동기 의존성을 가진 컴포넌트 트리의 로딩 상태를 처리합니다. async setup() 또는 최상위 await를 사용하는 컴포넌트를 감쌀 수 있습니다.
vue
<!-- AsyncDashboard.vue — async setup() 사용 -->
<script setup lang="ts">
// 최상위 await — Suspense 필요
const response = await fetch('/api/dashboard')
const data = await response.json()
</script>
<template>
<div class="dashboard">
<h2>대시보드</h2>
<div class="stats">
<div v-for="stat in data.stats" :key="stat.label">
<p class="text-2xl font-bold">{{ stat.value }}</p>
<p class="text-gray-500">{{ stat.label }}</p>
</div>
</div>
</div>
</template>
<!-- App.vue — Suspense로 감싸기 -->
<template>
<Suspense>
<!-- 기본 슬롯: 비동기 컴포넌트 -->
<template #default>
<AsyncDashboard />
</template>
<!-- fallback 슬롯: 로딩 중 표시 -->
<template #fallback>
<div class="flex items-center justify-center p-8">
<div class="animate-spin h-8 w-8 border-4 border-green-500 border-t-transparent rounded-full"></div>
<span class="ml-3">데이터를 불러오는 중...</span>
</div>
</template>
</Suspense>
</template>💡
Suspense는 아직 실험적(experimental) 기능입니다. 프로덕션에서는 defineAsyncComponent의 loadingComponent/errorComponent 옵션이 더 안정적입니다. 트랜지션은 mode="out-in" 속성으로 퇴장 완료 후 진입하도록 순서를 제어할 수 있습니다.