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 디렉티브)을 도입했습니다.

  1. 이름이 있는 슬롯
  2. 범위를 가지는 슬롯

새 디렉티브가 slotslot-scope 속성을 대체하고 두 속성은 이제 사라질 예정입니다.
아직 삭제된건 아니고 문서는 여기에서 볼 수 있습니다.
새 문법이 도입된 이유는 이 RFC에서 찾아볼 수 있습니다.

역자 주: 간략히 설명드리자면 기존 문법이 혼란스러운 부분이 있기 때문입니다.
이름이 있는 슬롯범위를 가지는 슬롯을 동시에 쓸 때 좀 더 간결해지긴 하는데 슬롯 자체가 이해가 쉽지는 않습니다.

슬롯에 들어가는 내용 (slot content)

Vue에 있는 컨텐트 배포 API는 slot 요소를 컨텐트 배포 통로로 사용하는 Web Components spec draft에서 영향을 받았습니다.

역자 주: 웹 컴포넌트는 2011년에 최초로 제안된 개념으로 구글에서 주로 밀고 있습니다.
표준안만 나오면 가장 가볍고 보편적인 웹 프레임워크가 되었겠지만 브라우저 제작사들이 아직 표준안을 도출하지 못하고 있습니다.
대신 논의 과정에서 나온 여러 개념들이 리액트, 앵귤러, 뷰 등의 웹 프레임워크에 도입되어서 사용되고 있습니다.

그래서 아래와 같은 컴포넌트를 만들 수 있습니다.



<navigation-link url="/profile">
  Your Profile
</navigation-link>


그리고 &lt;navigation-link&gt; 템플릿을 아래와 같이 만들 수 있습니다.



<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라고 썼는데 이 이름은 당연히 사용자가 원하는 대로 바꿀 수 있습니다.

Note

사실 범위가 있는 슬롯은 잘 이해가가지 않는다…
활용성 측면에서도 그렇고.. 사용법도 그렇고..
어떻게 사용해야되고 어떻게 활용해야되는거지..?

단독 디폴트 슬롯을 위한 축약 문법(Abbreviated Syntax for Lone Default Slots)