minstudio

내장 컴포넌트 마스터하기: 생명주기와 Teleport, Suspense

Vue 컴포넌트는 태어나고(Mount) 사라지는(Unmount) 생명주기(Lifecycle)를 가집니다. 이를 조작하여 컴포넌트가 뜰 때 백엔드 API를 호출할 수 있습니다. 추가로, Vue 3에서 제공하는 마법 같은 내장 컴포넌트인 <Teleport>를 사용하면, 복잡하게 중첩된 HTML 구조 속에서도 모달창이나 팝업을 화면 최상단(body)으로 순간이동 시켜 Z-Index 버그를 영구적으로 없앨 수 있습니다.

⏳ Lifecycle & 🚀 Teleport (순간이동) 주요 Lifecycle Hooks onMounted() 컴포넌트가 화면에 보일 때 발생. 이때 백엔드 API(fetch) 호출! onUpdated() 상태(ref)가 변해서 화면이 갱신될 때. onUnmounted() 컴포넌트가 화면에서 사라질 때. Teleport 순간이동 중첩된 뷰 (Z-index 버그위험) <Modal /> body 최상단 <Modal /> 안전한 렌더링!
<!-- ==========================================
// 📂 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>
내장 컴포넌트 마스터하기: 생명주기와 Teleport, Suspense | Minstudio