Vue 컴포넌트는 태어나고(Mount) 사라지는(Unmount) 생명주기(Lifecycle)를 가집니다. 이를 조작하여 컴포넌트가 뜰 때 백엔드 API를 호출할 수 있습니다. 추가로, Vue 3에서 제공하는 마법 같은 내장 컴포넌트인 <Teleport>를 사용하면, 복잡하게 중첩된 HTML 구조 속에서도 모달창이나 팝업을 화면 최상단(body)으로 순간이동 시켜 Z-Index 버그를 영구적으로 없앨 수 있습니다.
<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 text-slate-100">마이페이지 대시보드</h1>
<!-- 데이터 로딩 전 -->
<div v-if="isLoading" class="text-slate-400 animate-pulse">
데이터를 불러오는 중입니다...
</div>
<!-- 데이터 로딩 후 -->
<div v-else class="space-y-4">
<p class="text-xl text-slate-200">
환영합니다, <span class="font-bold text-blue-400">{{ userData.name }}</span> 님!
</p>
<button @click="isModalOpen = true" class="bg-red-500 text-white px-6 py-2 rounded shadow font-bold hover:bg-red-600">
탈퇴 경고문 열기
</button>
</div>
<!--
[2. 텔레포트: <Teleport to="body">]
이 코드가 아무리 깊은 <div> 숲 속에 있어도,
브라우저가 그릴 때는 이 모달을 무조건 최상단 <body> 태그 바로 아래로 쏴버립니다.
덕분에 부모의 overflow:hidden 이나 z-index 속성에 모달이 잘리는 현상이 절대 발생하지 않습니다!
-->
<Teleport to="body">
<!-- v-if 로 모달 렌더링 제어 -->
<div v-if="isModalOpen" class="fixed inset-0 bg-black/70 flex items-center justify-center z-50">
<div class="bg-slate-800 border border-slate-600 p-8 rounded-xl shadow-2xl max-w-sm text-center">
<h2 class="text-2xl font-bold text-red-500 mb-4">정말 탈퇴하시겠습니까?</h2>
<p class="text-slate-400 mb-8">모든 정보가 삭제되며 복구할 수 없습니다.</p>
<div class="flex gap-4 justify-center">
<button @click="isModalOpen = false" class="px-4 py-2 bg-slate-600 hover:bg-slate-500 rounded text-slate-100">취소</button>
<button class="px-4 py-2 bg-red-600 hover:bg-red-700 rounded text-white font-bold">탈퇴</button>
</div>
</div>
</div>
</Teleport>
</div>
</template>