Slot 과 v-slot 기능 포스팅
Vue 개발 중 공부내용을 기록하는 포스트 입니다.
1. 개요
간단한 TodoList 를 Vue로 개발하면서 slot의 기능에 대해서 다시한번 정리해본다.
2. 구성
- todoProject
... 생략
- src
... 생략
- App.js
- components
... 생략
- TodoInput.vue
- common
- Modal.vue
2-1. App.js
우선 구성은 App.js는 Container Component의 역할을 한다.
모든 상태관리는 App.js에서 이뤄지고 components 폴더 내 ToDoList, TodoInput, TodoHeader ... 등등의 Presentational Component에서는 변경 해달라고 emit을 통해서 요청을 하는 형태이다.
2-2. TodoInput.vue
TodoInput.vue 컴포넌트에서 이번에 볼 부분은 slot을 사용한 부분이다.
<!-- Modal.vue -->
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header"> default header </slot>
</div>
<div class="modal-body">
<slot name="body"> default body </slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" @click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
<!-- TodoInput.vue -->
<!-- ... 생략 -->
<Modal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">경고!</h3>
</Modal>
<!-- ... 생략 -->
여기서 TodoInput.vue는 Modal.vue의 부모 컴포넌트가 된다.
Modal.vue에서 <slot name="header"> 를 통해 default header 로 slot을 먼저 정의해 놓았고 우리는 이러한 Modal.vue를 사용하면서 사용자의 재정의가 가능한 것이다.

위에 이미지에서 보이듯이 우리가 TodoInput.vue에서 재정의한 header 부분만 다르게 나타난다.
3. slot-scope
slot을 통해서 자식 컴포넌트에서 사용된 데이터 혹은 메서드를 사용할 수도 있다.
아래의 예제를 보자
<!-- 자식 컴포넌트 -->
<!-- ... 생략 -->
<div class="modal-body">
<slot name="body" :modalData="modalData" :consoleMessage="consoleMessage">
default body
</slot>
</div>
<script>
export default {
data() {
return {
modalData: ["modalData1", "modalData2"],
};
},
methods: {
consoleMessage() {
console.log("Console Message!");
},
},
};
</script>
<!-- ... 생략 -->
<!-- 부모 컴포넌트 -->
<!-- ... 생략 -->
<Modal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="body" slot-scope="slotScope">
<button v-on:click="slotScope.consoleMessage">Console Button</button>
</h3>
</Modal>
<!-- ... 생략 -->
위 소스에서 보면 Child Component에서 body Slot에서 modalData 와 consoleMessage 메서드를 정의하고 Parent Component에서 사용되는걸 볼 수 있다.

그리고 Console Button을 클릭하면 Child Component에서 정의한 ConsoleMessage 함수가 실행되어 console 메시지가 찍히는걸 볼 수 있다.

3-1. v-slot
Vue3 에서는 slot, slot-scope가 공식적으로 삭제 된다고 한다.
같은 기능을 제공하는 Vue 디렉티브 v-slot로 동일한 기능을 구현해보자.
child Component는 동일하고 Parent Component에 변화가 생긴다.
<!-- 부모 컴포넌트 -->
<!-- slot-scope 사용 -->
<Modal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="body" slot-scope="slotScope">
<button v-on:click="slotScope.consoleMessage">Console Button</button>
</h3>
</Modal>
<!-- v-slot 사용 -->
<Modal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<template v-slot:body="slotProps">
<h3>
<button v-on:click="slotProps.consoleMessage">Console Button</button>
</h3>
</template>
</Modal>

v-slot을 사용할 때는 v-slot:[name] 으로 사용하고 template 태그로 감싸고 그 안에서 사용해야 한다는 점이 차이가 있다.
slot의 기능은 실무에서도 자주 쓰이기 때문이 이번 Modal을 진행하면서 다시한번 리마인드 겸 정리하도록 하였다.
최종 적용예
하나의 예를 더 진행한다.
상위 컴포넌트 : UserView, ItemView
하위 컴포넌트 : UserProfile.vue
UserView, ItemView 두개의 컴포넌트에서 하나의 컴포넌트를 재사용 하는 경우이다.
하지만, 각각의 상위 컴포넌트에서 데이터를 props로 내려주는 방식으로 진행했을 때 각각의 경우마다 분기처리 해줘야되는 번거로움이 생긴다.
이럴경우 slot을 활용해서 상위 컴포넌트에서 하위 컴포넌트에 들어갈 내용을 정의하면 된다.
그렇게되면 상위 컴포넌트에서 가지고 있는 데이터로 재사용된 컴포넌트를 채울 수 있기 때문이다 아래 코드를 보자.
<!-- 상위 컴포넌트(UserView) -->
<template>
<div>
<user-profile :itemInfo="itemInfo">
<div slot="username"></div>
<template slot="time"></template>
</user-profile>
</div>
</template>
<!-- 상위 컴포넌트(ItemView) -->
<div>
<section>
<!-- 질문 상세 정보 -->
<user-profile :itemInfo="itemInfo">
<div slot="username"></div>
<template slot="time"></template>
</user-profile>
<section>
<h2></h2>
</section>
</section>
<!-- 하위 컴포넌트(UserProfile) -->
<template>
<div>
<user-profile :itemInfo="itemInfo">
<div slot="username"></div>
<template slot="time"></template>
</user-profile>
</div>
</template>
</div>
위와 같이 하위 컴포넌트에서 실제로 필요한 위치에 각각의 name을 갖는 slot 태그를 만들었다.
이후 상위 컴포넌트의 재사용하는 user-profile 태그에 각각의 용도에 맞춰 name 프로퍼티를 넣고 실제 들어가야할 tag를 정의한다.
작은거라도 하나하나 정리해가다보면 점점 내것이 되는것 같아 기분이 좋다.