웹 애플리케이션의 사용자 경험(UX)을 극대화하기 위해 Vue가 제공하는 <Transition>과 <TransitionGroup> 컴포넌트의 활용법을 다룹니다. 진입/진출 트랜지션부터 리스트 애니메이션까지 시각적 피드백을 구현해 봅니다.
Vue Transition의 6가지 클래스 생명주기
Vue는 엘리먼트가 DOM에 추가(Enter)되거나 제거(Leave)될 때, 특정 시점마다 자동으로 CSS 클래스를 붙이고 떼어냅니다. 개발자는 이 클래스들에 CSS 애니메이션만 작성해주면 아주 부드러운 시각적 효과를 만들어낼 수 있습니다.
컴포넌트 이름
주요 특징 및 사용처
Mode 설정 옵션
<Transition>
단일 요소나 컴포넌트(v-if, v-show, 동적 컴포넌트)의 진입/진출을 관리.
mode="out-in" (이전 요소가 나간 후 새 요소 등장)
<TransitionGroup>
v-for로 렌더링되는 다중 아이템 목록 관리에 사용. 엘리먼트가 움직일 때 v-move 클래스가 자동으로 붙음.
tag="ul" (어떤 부모 태그로 렌더링할지 지정)
💡 꿀팁: Transition 이름 지정하기
단순히 <Transition>만 사용하면 기본 접두사는 v-가 됩니다 (예: v-enter-active). 하지만 <Transition name="fade">처럼 이름을 지정해주면, 클래스 접두사가 해당 이름으로 바뀝니다 (예: fade-enter-active). 이를 통해 한 페이지 안에서 팝업용, 슬라이드용, 페이드용 등 다양한 애니메이션 클래스를 독립적으로 설계할 수 있습니다!
<script setup>
import { ref } from 'vue';
// 1번 패널 상태 (모달 표시 여부)
const showModal = ref(true);
// 2번 패널 상태 (할일 목록 배열)
const items = ref([
{ id: 1, text: 'Vue 3 공부하기' },
{ id: 2, text: 'Transition 클래스 외우기' },
{ id: 3, text: 'Minstudio에서 실습하기' }
]);
let nextId = 4;
// 랜덤한 위치에 새 항목을 추가합니다. (기존 요소들이 부드럽게 밀려나는 것 확인)
const addItem = () => {
const randomIndex = Math.floor(Math.random() * (items.value.length + 1));
items.value.splice(randomIndex, 0, { id: nextId++, text: '새로운 항목 ' + nextId });
};
// 특정 항목 삭제
const removeItem = (id) => {
items.value = items.value.filter(item => item.id !== id);
};
</script>
<template>
<div class="parent-container">
<!-- 1. 단일 요소 트랜지션 (fade) -->
<div class="panel">
<h3>1. 단일 요소 Transition</h3>
<button @click="showModal = !showModal">토글 모달</button>
<!-- name="fade"를 주면 클래스가 fade-enter-active 등으로 만들어집니다. -->
<Transition name="fade">
<div v-if="showModal" class="modal-box">
부드럽게 나타나고 사라지는 페이드 모달입니다! ✨
</div>
</Transition>
</div>
<!-- 2. 다중 리스트 트랜지션 (list) -->
<div class="panel">
<h3>2. TransitionGroup (목록 애니메이션)</h3>
<button @click="addItem">항목 추가</button>
<!-- 배열(리스트)은 반드시 TransitionGroup을 써야 합니다. -->
<TransitionGroup name="list" tag="ul" class="todo-list">
<!-- key를 반드시 제공해야 올바른 애니메이션 위치를 추적할 수 있습니다. -->
<li v-for="item in items" :key="item.id" class="todo-item">
{{ item.text }}
<button @click="removeItem(item.id)">삭제</button>
</li>
</TransitionGroup>
</div>
</div>
</template>