컴포넌트 심화: 슬롯(Slots)과 Provide/Inject
Props와 Emit만으로 모든 것을 해결하려다 보면 문제가 생깁니다. 부모가 자식의 내부 디자인(HTML)을 통째로 갈아 끼우고 싶거나, 할아버지 컴포넌트가 저 아래 까마득한 손자 컴포넌트에게 데이터를 주고 싶을 때는 어떻게 할까요? 이때 Slots와 Provide/Inject가 등장합니다.
<!-- ==========================================
// 📂 App.vue (Provide와 Slots 사용 예제)
// ========================================== -->
<script setup>
import { provide, ref } from 'vue';
import Modal from './Modal.vue';
import GrandChild from './GrandChild.vue';
// [1. Provide] 할아버지가 데이터를 제공합니다.
// 이 데이터는 자식, 손자, 증손자 등 하위 컴포넌트 어디서든 바로 꺼내 쓸 수 있습니다.
const themeColor = ref('dark');
provide('theme', themeColor);
</script>
<template>
<div class="p-8 bg-gray-50 max-w-lg mx-auto">
<!-- 손자 컴포넌트를 그냥 불러옵니다. Props를 안 넘겨줘도 됩니다! -->
<GrandChild />
<!-- [2. Slots] 자식 컴포넌트의 구멍(slot)에 HTML 끼워넣기 -->
<Modal>
<!-- 자식 컴포넌트의 <slot name="header"> 위치에 들어갈 HTML -->
<template #header>
<h1 class="text-2xl text-red-600 font-bold">경고: 결제 실패</h1>
</template>
<!-- 자식 컴포넌트의 기본 <slot> 위치에 들어갈 HTML -->
<p class="text-gray-700 my-4">잔액이 부족하여 결제가 취소되었습니다.</p>
<!-- 자식 컴포넌트의 <slot name="footer"> 위치에 들어갈 HTML -->
<template #footer>
<button class="bg-gray-800 text-white px-4 py-2 rounded">닫기</button>
</template>
</Modal>
</div>
</template>
<!-- ==========================================
// 📂 GrandChild.vue (Inject 예제)
// ========================================== -->
<script setup>
import { inject } from 'vue';
// 할아버지가 Provide로 뿌린 'theme' 이라는 데이터를 직통으로 꺼내옵니다.
// Props로 여러 번 전달받을 필요가 없습니다! (Prop Drilling 해결)
const theme = inject('theme');
</script>
<template>
<div :class="theme === 'dark' ? 'bg-black text-white' : 'bg-white text-black'">
현재 테마는 {{ theme }} 입니다!
</div>
</template>