웹 애플리케이션의 사용자 경험(UX)을 극대화하기 위해 Vue가 제공하는 <Transition>과 <TransitionGroup> 컴포넌트의 활용법을 다룹니다. 진입/진출 트랜지션부터 리스트 애니메이션까지 시각적 피드백을 구현해 봅니다.
Vue는 엘리먼트가 DOM에 추가(Enter)되거나 제거(Leave)될 때, 특정 시점마다 자동으로 CSS 클래스를 붙이고 떼어냅니다. 개발자는 이 클래스들에 CSS 애니메이션만 작성해주면 아주 부드러운 시각적 효과를 만들어낼 수 있습니다.
| 컴포넌트 이름 | 주요 특징 및 사용처 | Mode 설정 옵션 |
|---|---|---|
| <Transition> | 단일 요소나 컴포넌트(v-if, v-show, 동적 컴포넌트)의 진입/진출을 관리. | mode="out-in" (이전 요소가 나간 후 새 요소 등장) |
| <TransitionGroup> | v-for로 렌더링되는 다중 아이템 목록 관리에 사용. 엘리먼트가 움직일 때 v-move 클래스가 자동으로 붙음. |
tag="ul" (어떤 부모 태그로 렌더링할지 지정) |
단순히 <Transition>만 사용하면 기본 접두사는 v-가 됩니다 (예: v-enter-active). 하지만 <Transition name="fade">처럼 이름을 지정해주면, 클래스 접두사가 해당 이름으로 바뀝니다 (예: fade-enter-active). 이를 통해 한 페이지 안에서 팝업용, 슬라이드용, 페이드용 등 다양한 애니메이션 클래스를 독립적으로 설계할 수 있습니다!
<!-- HIDDEN_TAB -->
<div id="app"></div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp, ref } = Vue;
const App = {
template: `
<div>
<h2 style="margin-top: 0;">Transition & TransitionGroup</h2>
<div class="container">
<!-- 1번 패널: 단일 Transition (Fade) -->
<div class="panel">
<h3 style="margin-top: 0; color: #38bdf8;">1. 단일 Transition</h3>
<button class="btn" @click="showModal = !showModal">
모달 {{ showModal ? '닫기' : '열기' }}
</button>
<Transition name="fade">
<div v-if="showModal" class="modal-box">
부드럽게 나타나고 사라지는<br/> 페이드 모달입니다! ✨
</div>
</Transition>
</div>
<!-- 2번 패널: 리스트 TransitionGroup (Slide) -->
<div class="panel">
<h3 style="margin-top: 0; color: #a78bfa;">2. TransitionGroup</h3>
<button class="btn" @click="addItem">항목 추가</button>
<TransitionGroup name="list" tag="ul" class="todo-list">
<li v-for="item in items" :key="item.id" class="todo-item">
{{ item.text }}
<button class="btn btn-danger" style="margin-bottom: 0; padding: 4px 8px; font-size: 0.8rem;" @click="removeItem(item.id)">삭제</button>
</li>
</TransitionGroup>
</div>
</div>
</div>
`,
setup() {
// 1번: 단일 Transition 상태
const showModal = ref(true);
// 2번: TransitionGroup 상태
const items = ref([
{ id: 1, text: 'Vue 3 공부하기' },
{ id: 2, text: 'Transition 클래스 외우기' },
{ id: 3, text: 'Minstudio에서 실습하기' }
]);
let nextId = 4;
const addItem = () => {
// 배열 임의 위치에 추가하여 부드럽게 밀리는(.list-move) 효과 확인
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);
};
return { showModal, items, addItem, removeItem };
}
};
createApp(App).mount('#app');
</script>