<slot> ๊ฐ๋
Vue.js ๊ณต์ ์ฌ์ดํธ slot
โผ๏ธ slot์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ๋ก HTML ์ฝํ
์ธ ๋ฅผ ์ ๋ฌํ ๋ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ด๋ค.
โผ๏ธ slot์ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๊ณ , ์ ์ฐํ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ์ ์๋ค.
โผ๏ธ ์์ ์ปดํฌ๋ํธ์์ <slot>์ ์ฌ์ฉํ๋ฉด, ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์ ๋ฌํ ๋ด์ฉ์ ์ํ๋ ์์น์ ๋ ๋๋งํ ์ ์๋ค.
์์ ์ปดํฌ๋ํธ (Child.vue)
<template>
<div class="box">
<slot></slot>
</div>
</template>
<style scoped>
.box {
padding: 10px;
border: 1px solid black;
}
</style>
๋ถ๋ชจ ์ปดํฌ๋ํธ (Parent.vue)
<template>
<Child>
<p>์ด ๋ด์ฉ์ด slot์ผ๋ก ์ ๋ฌ๋ฉ๋๋ค!</p>
</Child>
</template>
<script setup>
import Child from './Child.vue';
</script>
๐ ๊ฒฐ๊ณผ: <slot>์ด ์๋ ๋ถ๋ถ์ <p>์ด ๋ด์ฉ์ด slot์ผ๋ก ์ ๋ฌ๋ฉ๋๋ค!</p>๊ฐ ๋ ๋๋ง๋๋ค.
โผ๏ธ ์ฌ๋ฌ ๊ฐ์ ์ฌ๋กฏ์ ์ ์ํ ์ ์์ผ๋ฉฐ, name์ ์ง์ ํ์ฌ ํน์ ์ฌ๋กฏ์ ์ฝํ
์ธ ๋ฅผ ์ ๋ฌํ ์ ์๋ค.
์์ ์ปดํฌ๋ํธ (Child.vue)
<template>
<div class="box">
<header>
<slot name="header">๊ธฐ๋ณธ ํค๋</slot>
</header>
<main>
<slot>๊ธฐ๋ณธ ๋ด์ฉ</slot>
</main>
<footer>
<slot name="footer">๊ธฐ๋ณธ ํธํฐ</slot>
</footer>
</div>
</template>
๋ถ๋ชจ ์ปดํฌ๋ํธ (Parent.vue)
<template>
<Child>
<template #header>
<h1>์ปค์คํ
ํค๋</h1>
</template>
<p>๋ฉ์ธ ์ฝํ
์ธ </p>
<template #footer>
<p>์ปค์คํ
ํธํฐ</p>
</template>
</Child>
</template>
๐ ๊ฒฐ๊ณผ:
<header>
<h1>์ปค์คํ
ํค๋</h1>
</header>
<main>
<p>๋ฉ์ธ ์ฝํ
์ธ </p>
</main>
<footer>
<p>์ปค์คํ
ํธํฐ</p>
</footer>
๐ #header โ <slot name="header">
๐ #default โ <slot>
๐ #footer โ <slot name="footer">
โผ๏ธ ๋ถ๋ชจ๊ฐ ์ฌ๋กฏ ๋ด๋ถ์์ ์์ ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ์์ด๋ค.
์์ ์ปดํฌ๋ํธ (Child.vue)
<template>
<div>
<slot :text="message"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue';
const message = ref("์์ ์ปดํฌ๋ํธ์์ ์ ๋ฌ๋ ๋ฉ์์ง");
</script>
๋ถ๋ชจ ์ปดํฌ๋ํธ (Parent.vue)
<template>
<Child v-slot="{ text }">
<p>{{ text }}</p>
</Child>
</template>
<script setup>
import Child from './Child.vue';
</script>
๐ ๊ฒฐ๊ณผ:
<p>์์ ์ปดํฌ๋ํธ์์ ์ ๋ฌ๋ ๋ฉ์์ง</p>
๐ v-slot="{ text }"๋ฅผ ์ฌ์ฉํด slot์ผ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
โผ๏ธ Vue 3์์๋ v-slot์ #์ผ๋ก ์ค์ฌ์ ์ฌ์ฉํ ์ ์๋ค.
<Child #default="{ text }">
<p>{{ text }}</p>
</Child>
์ด๋ฆ ์๋ ์ฌ๋กฏ์๋ ์ ์ฉ ๊ฐ๋ฅ
<Child #header>
<h1>์ปค์คํ
ํค๋</h1>
</Child>
๐ฌ ๊ทธ๋ผ <Component /> ์ง์ ์ฌ์ฉ๊ณผ ์ฐจ์ด๊ฐ ๋ญ๊น?

๐ฅ ๊ฒฐ๋ก
โ
๊ธฐ๋ณธ slot โ ๋ถ๋ชจ๊ฐ ์ ๋ฌํ ๋ด์ฉ์ ์์ ์ปดํฌ๋ํธ์ ํน์ ์์น์ ์ฝ์
โ
์ด๋ฆ ์๋ ์ฌ๋กฏ โ name์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ๊ฐ์ ์ฌ๋กฏ ๊ด๋ฆฌ
โ
Scoped Slots โ ์์ ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ชจ์๊ฒ ์ ๋ฌ ๊ฐ๋ฅ
โ
v-slot ์ถ์ฝ ๋ฌธ๋ฒ โ #default, #header ๋ฑ ์ฌ์ฉ ๊ฐ๋ฅ
๐ก B2B ํ๋ก์ ํธ์์ ์์ฃผ ์ฌ์ฉํ๋ ์ฌ๋กฏ ๋ฐฉ์
โป๏ธ ์ด๋ฆ ์๋ ์ฌ๋กฏ (Named Slots)
โช๏ธ ์ด์ : B2B ์์คํ
์์๋ ๋ ์ด์์์ด ์ผ์ ํ์ง๋ง ์ผ๋ถ๋ง ๋ค๋ฅด๊ฒ ๋ณ๊ฒฝ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์. ์๋ฅผ ๋ค์ด, ํ
์ด๋ธ, ์นด๋, ๋ชจ๋ฌ ๊ฐ์ UI์์ ํค๋/๋ฐ๋/ํธํฐ๋ฅผ ๊ตฌ๋ถํด์ ๊ด๋ฆฌํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. #header, #footer ๊ฐ์ ๊ตฌํ๋ณ ์ฌ๋กฏ์ ์ฌ์ฉํ๋ฉด ์ ์ง๋ณด์๊ฐ ์ฌ์์ง.
โป๏ธ Scoped Slots
โช๏ธ ์ด์ : ํ
์ด๋ธ, ๋ฆฌ์คํธ, ๋๋กญ๋ค์ด ๊ฐ์ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๋์ ์ผ๋ก ํ์ํ ๋ ์ ์ฉํจ.
์๋ฅผ ๋ค์ด, ์กฐํ ํ๋ฉด์์ ๋ฐฑ์๋์์ ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ชจ์์ ๊ฐ๊ณตํ๊ณ ์์ ์ปดํฌ๋ํธ์์ ํ์ฉํ๋ ๊ฒฝ์ฐ. ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ชจ์์ ์ ๋ฌํ๊ณ , ์์์์ v-slot์ ์ฌ์ฉํด ํ์ฉํ๋ ๋ฐฉ์์ด ์ข์.
โ
B2B ํ๋ก์ ํธ์์์ ์ค์ ์์
1๏ธโฃ ์ด๋ฆ ์๋ ์ฌ๋กฏ (Named Slots) ๐ ์ฌ์ฉ ์์: ์กฐํ ํ๋ฉด์ ์นด๋ํ UI
โผ๏ธ ์กฐํ ํ๋ฉด์์ ๊ณ ๊ฐ/๊ณ์ฝ ์ ๋ณด๋ฅผ ์นด๋ ํํ๋ก ํ์ํ ๋ ์ฌ์ฉ.
CustomerCard.vue (์์ ์ปดํฌ๋ํธ)
<template>
<div class="card">
<header><slot name="header">๊ธฐ๋ณธ ์ ๋ชฉ</slot></header>
<section><slot>๊ธฐ๋ณธ ๋ด์ฉ</slot></section>
<footer><slot name="footer">๊ธฐ๋ณธ ํธํฐ</slot></footer>
</div>
</template>
CustomerList.vue
<template>
<CustomerCard>
<template #header>
<h2>VIP ๊ณ ๊ฐ</h2>
</template>
<p>๊ณ ๊ฐ๋ช
: ํ๊ธธ๋</p>
<p>๊ณ์ฝ ์ํ: ํ์ฑ</p>
<template #footer>
<button @click="viewDetail">์์ธ ๋ณด๊ธฐ</button>
</template>
</CustomerCard>
</template>
๐ ์ฅ์ :
โช๏ธ ๋ ์ด์์์ด ๊ณ ์ ์ ์ด๋ฉด์๋, ํค๋/๋ฐ๋/ํธํฐ๋ฅผ ์ ๋์ ์ผ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ.
โช๏ธ ์ฌ๋ฌ ๊ฐ์ ์นด๋์์ ๋ค๋ฅธ ์ฝํ
์ธ ๋ฅผ ์ฝ๊ฒ ์ ์ฉ ๊ฐ๋ฅ.
2๏ธโฃ Scoped Slots (๋ฒ์๊ฐ ์๋ ์ฌ๋กฏ) ๐ ์ฌ์ฉ ์์: ๊ณ ๊ฐ ๋ชฉ๋ก ํ ์ด๋ธ
โผ๏ธ v-for๋ก ๋ฐ๋ณต๋๋ ๋ฐ์ดํฐ๋ฅผ ๋์ ์ผ๋ก ํ์ํด์ผ ํ ๋ ๋ง์ด ์ฌ์ฉ๋จ.
CustomerCard.vue (์์ ์ปดํฌ๋ํธ)
<template>
<table>
<thead>
<tr>
<th>์ด๋ฆ</th>
<th>๊ณ์ฝ ์ํ</th>
<th>์์
</th>
</tr>
</thead>
<tbody>
<tr v-for="customer in customers" :key="customer.id">
<td>{{ customer.name }}</td>
<td>{{ customer.status }}</td>
<td>
<slot name="actions" :customer="customer"></slot>
</td>
</tr>
</tbody>
</table>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
customers: Array,
});
</script>
CustomerList.vue (๋ถ๋ชจ ์ปดํฌ๋ํธ)
<template>
<CustomerTable :customers="customerList">
<template #actions="{ customer }">
<button @click="editCustomer(customer)">์์ </button>
<button @click="deleteCustomer(customer)">์ญ์ </button>
</template>
</CustomerTable>
</template>
<script setup>
import CustomerTable from './CustomerTable.vue';
const customerList = [
{ id: 1, name: "ํ๊ธธ๋", status: "ํ์ฑ" },
{ id: 2, name: "๊น์ฒ ์", status: "๋นํ์ฑ" }
];
const editCustomer = (customer) => {
console.log("๊ณ ๊ฐ ์์ :", customer);
};
const deleteCustomer = (customer) => {
console.log("๊ณ ๊ฐ ์ญ์ :", customer);
};
</script>
๐ ์ฅ์ :
โช๏ธ ํ
์ด๋ธ์์ ์ ๋์ ์ธ ์์
๋ฒํผ์ ์ฝ๊ฒ ์ถ๊ฐ ๊ฐ๋ฅ.
โช๏ธ ๋ถ๋ชจ์์ ๋ฐ์ดํฐ ๊ฐ๊ณต ํ ๋๊ฒจ์ฃผ๊ณ , ์์์์ ํ์ฉ ๊ฐ๋ฅ.
โช๏ธ ๋ค์ํ ํํ์ ๋ฒํผ(์กฐํ, ์์ , ์ญ์ ๋ฑ)์ ์ํฉ์ ๋ง๊ฒ ๋ค๋ฅด๊ฒ ํ์ ๊ฐ๋ฅ.
โ
๊ฒฐ๋ก : B2B ํ๋ก์ ํธ์์ ๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ ์ฌ๋กฏ

๐ ์ด๋ฆ ์๋ ์ฌ๋กฏ โ ๋ ์ด์์์ด ์ผ์ ํ UI
๐ Scoped Slots โ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ๋ ๋์ ์ธ UI
B2B ํ๋ก์ ํธ์์๋ ๋ฐ์ดํฐ ์กฐํ์ ๊ด๋ฆฌ๊ฐ ๋ง๊ธฐ ๋๋ฌธ์, Scoped Slots์ด ํ์!
๋ํ, ๋ ์ด์์์ด ๋ฐ๋ณต๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์ Named Slots๋ ์์ฃผ ์ฌ์ฉ๋จ! ๐