42 slot이 있는 콘텐츠 배포
source: categories/study/vue-experiance/vue-experiance_9-32.md
42. slot이 있는 콘텐츠 배포
1. slot이 있는 콘텐츠 배포
HTML 요소와 마찬가지로 다음과 같이 구성 요소에 콘텐츠를 전달할 수 있으면 종종 유용합니다.
<alert-box>
Something bad happened.
</alert-box>
slot 활용 방법은 아주 쉽습니다.
slot
엘리먼트를 사용하기만하면 됩니다.
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
위에서 볼 수 있듯이 원하는 slot
을 추가하기만하면 됩니다.
지금은 이것이 slot
에 대해 알아야할 전부입니다.
하지만 이 페이지를 읽고 내용에 익숙해지면 나중에 다시 돌아와 slot
에 대한 전체 가이드를 읽는 것이 좋습니다.
slot(슬롯)
vue 2.6.0 버전에서 이름이 있는 슬롯과 범위를 가지는 슬롯을 위한 새로운 통합 문법(v-slot 디렉티브)을 도입했습니다.
- 이름이 있는 슬롯
- 범위를 가지는 슬롯
새 디렉티브가 slot
과 slot-scope
속성을 대체하고 두 속성은 이제 사라질 예정입니다.
아직 삭제된건 아니고 문서는 여기에서 볼 수 있습니다.
새 문법이 도입된 이유는 이 RFC에서 찾아볼 수 있습니다.
역자 주: 간략히 설명드리자면 기존 문법이 혼란스러운 부분이 있기 때문입니다.
이름이 있는 슬롯과 범위를 가지는 슬롯을 동시에 쓸 때 좀 더 간결해지긴 하는데 슬롯 자체가 이해가 쉽지는 않습니다.
슬롯에 들어가는 내용 (slot content)
Vue에 있는 컨텐트 배포 API는 slot
요소를 컨텐트 배포 통로로 사용하는 Web Components spec draft에서 영향을 받았습니다.
역자 주: 웹 컴포넌트는 2011년에 최초로 제안된 개념으로 구글에서 주로 밀고 있습니다.
표준안만 나오면 가장 가볍고 보편적인 웹 프레임워크가 되었겠지만 브라우저 제작사들이 아직 표준안을 도출하지 못하고 있습니다.
대신 논의 과정에서 나온 여러 개념들이 리액트, 앵귤러, 뷰 등의 웹 프레임워크에 도입되어서 사용되고 있습니다.
그래서 아래와 같은 컴포넌트를 만들 수 있습니다.
<navigation-link url="/profile">
Your Profile
</navigation-link>
그리고 <navigation-link>
템플릿을 아래와 같이 만들 수 있습니다.
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
컴포넌트를 렌더링할 때 <slot></slot>
이 <span class="fa fa-user"></span> Your Profile
로 교체됩니다.
슬롯에는 HTML 같은 템플릿 코드를 포함시킬 수 있기 때문입니다.
<navigation-link url="/profile">
<!-- Font Awesome 아이콘을 추가합시다 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
다른 컴포넌트(역자 주: <font-awesome-icon>
)도 가능합니다.
<navigation-link url="/profile">
<!-- 컴포넌트로 아이콘을 추가해봅시다 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
만약 <navigation-link>
템플릿이 <slot>
요소를 가지고 있지 않다면 그 자리에 들어갔어야 할 모든 내용이 무시될 것입니다.
컴파일될 때의 범위(Compilation Scope)
슬롯 안에 데이터 옵션을 사용하고 싶을 수 있습니다. 아래의 예를 봅시다.
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
여기서 slot
은 같은 템플릿의 나머지와 똑같은 인스턴스 속성(즉 같은 ‘범위')에 연결되어 있습니다.
slot
이 <navigation-link>
의 범위에 연결된 것이 아닌거죠.
예를 들어 url
에 접근하려고하면 작동하지 않을 것입니다.
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
url은 undefined로 나올 겁니다.
이 Clicking here will send you to: {{ url }} 이라는 데이터는
<navigation-link>로 넘어가지만
<navigation-link> 컴포넌트 안에 정의되어 있지는 않으니까요.
-->
</navigation-link>
이 규칙을 기억하세요.
기본값 지정(Fallback Content)
아무 컨텐트도 전달되지 않았을 때 slot
에 렌더링시킬 대비책(즉 기본값)을 지정해놓는 것이 유용한 경우가 있을 수 있습니다.
<submit-button>
컴포넌트의 예를 살펴봅시다.
<button type="submit">
<slot></slot>
</button>
보통은 <button>
안에 "Submit" 텍스트가 들어가기를 원할 때가 많습니다.
"Submit"을 기본값으로 만들기 위해서는 <slot>
태그 사이에 넣어야 합니다.
<button type="submit">
<slot>Submit</slot>
</button>
이제 부모 컴포넌트의 <submit-button>
를 사용할 때 아래와 같이 슬롯에 해당하는 아무 내용을 전달하지 않으면
<submit-button></submit-button>
기본값인 "Submit"이 렌더링될 것입니다.
<button type="submit">
Submit
</button>
하지만 슬롯에 어떤 내용을 전달하면
<submit-button>
Save
</submit-button>
전달된 내용이 렌더링되겠죠.
<button type="submit">
Save
</button>
이름이 있는 슬롯(Named Slots)
여러 개의 슬롯을 쓰면 더 유용할 때가 있습니다.
예를 들어 <base-layout>
컴포넌트의 아래 템플릿을 봅시다.
<div class="container">
<header>
<!-- 헤더는 여기에 넣을 겁니다 -->
</header>
<main>
<!-- 본문은 여기에 넣을 겁니다 -->
</main>
<footer>
<!-- 푸터는 여기에 넣을 겁니다 -->
</footer>
</div>
이런 경우를 위해서 <slot>
요소는 서로 다른 슬롯들을 정의할 때 쓸 수 있는 name
이라는 특별한 속성을 가지고 있습니다.
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
name
이 지정되지 않은 <slot>
에는 암묵적으로 "default" 값이 사용됩니다.
이름이 있는 슬롯에 내용을 전달하려면 <template>
에 v-slot
디렉티브를 쓰고 그 속성에 앞에서 지정한 name
을 넣으면 됩니다.
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
이제 <template>
요소들의 모든 내용물이 적절한 슬롯으로 전달되었습니다.
v-slot
을 쓴 <template>
으로 싸여있지 않은 내용물들은 디폴트 슬롯
에 해당되는 것으로 간주합니다.
하지만 원한다면 명시적으로 <template>
에 default
를 표시하고 그 안에 내용을 넣을 수도 있습니다.
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
양쪽 모두 렌더링된 HTML은 아래와 같게 됩니다.
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
삭제될 예정인 slot
속성과 다르게 v-slot
만 <template>
태그에 추가할 수 있다는 점을 유의하시기 바랍니다.(예외가 하나 있기는 합니다)
범위가 있는 슬롯(scoped slots)
어떨 때는 자식 컴포넌트에서만 접근할 수 있는 데이터에서 슬롯에 필요한 내용을 가져와야 할 수 있습니다.
아래 템플릿의 <current-user>
컴포넌트의 예를 살펴봅시다.
<span>
<slot>{{ user.lastName }}</slot>
</span>
아래와 같이 기본값을 사용자의 성말고 이름으로 바꾸고 싶을 수도 있습니다.
<current-user>
{{ user.firstName }}
</current-user>
하지만 이 파일은 작동하지 않습니다.
왜냐하면 <current-user>
컴포넌트만 user
에 접근할 수 있는데 슬롯에 제공되는 내용들은 부모 컴포넌트에서 렌더링되기 때문입니다.
부모 컴포넌트의 슬롯에서 user
를 쓸려면 user
를 <slot>
요소에 속성으로 연결해야 합니다.
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
<slot>
요소에 연결된 속성을 슬롯 속성(slot props)라고 합니다.
이제 부모 컴포넌트의 범위(scope)에서 v-slot
에 연결한 ‘슬롯 속성(slotProps)'를 쓸 수 있습니다.
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
이번 사례에서 모든 슬롯 속성
의 이름을 slotProps
라고 썼는데 이 이름은 당연히 사용자가 원하는 대로 바꿀 수 있습니다.
사실 범위가 있는 슬롯은 잘 이해가가지 않는다…
활용성 측면에서도 그렇고.. 사용법도 그렇고..
어떻게 사용해야되고 어떻게 활용해야되는거지..?