내장 컴포넌트 마스터하기: 생명주기와 Teleport, Suspense
Vue 컴포넌트는 태어나고(Mount) 사라지는(Unmount) 생명주기(Lifecycle)를 가집니다. 이를 조작하여 컴포넌트가 뜰 때 백엔드 API를 호출할 수 있습니다. 추가로, Vue 3에서 제공하는 마법 같은 내장 컴포넌트인 <Teleport>를 사용하면, 복잡하게 중첩된 HTML 구조 속에서도 모달창이나 팝업을 화면 최상단(body)으로 순간이동 시켜 Z-Index 버그를 영구적으로 없앨 수 있습니다.
<!-- ==========================================
// 📂 Dashboard.vue (onMounted 와 Teleport 조합 예제)
// ========================================== -->
<script setup>
import { ref, onMounted } from 'vue';
const isModalOpen = ref(false);
const userData = ref(null);
const isLoading = ref(true);
// [1. 생명주기 훅: onMounted]
// 화면이 브라우저에 부착(Mount)되는 즉시 실행됩니다.
onMounted(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
userData.value = await response.json();
} catch (e) {
console.error("데이터 통신 에러!");
} finally {
isLoading.value = false;
}
});
</script>
<template>
<div class="p-8">
<h1 class="text-3xl font-bold mb-6">마이페이지 대시보드</h1>
<!-- 데이터 로딩 전 -->
<div v-if="isLoading" class="text-gray-500 animate-pulse">
데이터를 불러오는 중입니다...
</div>
<!-- 데이터 로딩 후 -->
<div v-else class="space-y-4">
<p class="text-xl">환영합니다, <span class="font-bold text-blue-600">{{ userData.name }}</span> 님!</p>
<button @click="isModalOpen = true" class="bg-red-500 text-white px-6 py-2 rounded shadow">
탈퇴 경고문 열기
</button>
</div>
<!--
[2. 텔레포트: <Teleport to="body">]
이 코드가 아무리 깊은 <div> 숲 속에 있어도,
브라우저가 그릴 때는 이 모달을 무조건 최상단 <body> 태그 바로 아래로 쏴버립니다.
덕분에 CSS z-index가 꼬여서 모달이 잘리는 현상이 절대 발생하지 않습니다.
-->
<Teleport to="body">
<!-- v-if 로 모달 렌더링 제어 -->
<div v-if="isModalOpen" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white p-8 rounded-xl shadow-2xl max-w-sm text-center transform transition-all">
<h2 class="text-2xl font-bold text-red-600 mb-4">정말 탈퇴하시겠습니까?</h2>
<p class="text-gray-600 mb-8">모든 정보가 삭제되며 복구할 수 없습니다.</p>
<div class="flex gap-4 justify-center">
<button @click="isModalOpen = false" class="px-4 py-2 bg-gray-200 rounded text-gray-800">취소</button>
<button class="px-4 py-2 bg-red-600 rounded text-white font-bold">탈퇴</button>
</div>
</div>
</div>
</Teleport>
</div>
</template>