본문 바로가기

FrontEnd

Vue3 컴포넌트 디자인 패턴 : Slots

반응형

Vue3에서 slot을 사용하여 유연한 구조, 유연한 합성이 가능한 컴포넌트를 개발하는 방법을 알아봅니다.

 

이전 시리즈

2022.12.21 - [Vue.js] - Vue3 컴포넌트 디자인 패턴 소개

2022.12.22 - [분류 전체보기] - Vue3 컴포넌트 디자인 패턴 : Slots

Props 중심 컴포넌트의 문제점

거대한 설정덩어리

props는 설정덩어리다.

  • props가 구조를 바꾸고
  • props 정의 순서와 사용 순서가 다르고
  • props이 거대해 진다면

컴포넌트의 이해와 확장이 어려워진다.

slot이 무엇인가?

슬롯은 콘텐츠 배포 outlet 역할을 하는 커스텀 Vue 엘리먼트다.
슬롯을 이용하면 정해진 공간을 마음대로 채울 수 있다.

 

슬롯은 표준 html 요소처럼 컨텐츠를 넘길 수 있도록 해주는 도구다

HTML 엘리먼트(예: div, span, p 등)로 작업할 때 우리는 자식으로 모든 종류의 입력을 허용한다.
<!-- Just passing a string -->
<div>
  This is just normal text
</div>

<!-- Composing text with other HTML elements -->
<div>
  This is text with <strong>other HTML elements</strong>.
</div>

즉, 텍스트를 전달하든 다른 엘리먼트를 추가하든 관계없이
컴포넌트는 화면에서 예상대로 렌더링된다.
Vue 컴포넌트의 슬롯을 사용하면 비슷한 효과를 얻을 수 있다.

슬롯을 사용하는 방법

슬롯을 사용하는 것은 다른 HTML 요소만큼 쉽다.
추가 설정은 필요없다.
컴포넌트의 템플릿에 슬롯 요소를 추가하기만 하면 된다.

 

📄App.vue : props 버전

<template>
  <main>
    <BaseButton text="Cancel" />
    <BaseButton text="Submit" right-icon="right-arrow" />
  </main>
</template>

📄App.vue : 슬롯 버전

<template>
  <main>
    <BaseButton>Cancel</BaseButton>
    <BaseButton>Submit <span class="icon right-arrow"></span></BaseButton>
  </main>
</template>

슬롯에 디폴트 컨텐츠 적용하기

그냥 슬롯을 채워두기만 하면 된다.

📄BaseButton.vue

<template>
  <button class="button">
    <slot>Submit</slot>
  </button>
</template>

슬롯에 값을 제공하면 디폴트 컨텐츠를 오버라이드(재정의)한다.

<!-- BaseButton with no custom content -->
<BaseButton></BaseButton>

<!-- Rendered BaseButton with no custom content -->
<button class="button">Submit</button>

<!-- BaseButton with custom content -->
<BaseButton>Cancel</BaseButton>

<!-- Rendered BaseButton with custom content -->
<button class="button">Cancel</button>

여러개의 슬롯 사용하기

레이아웃 : 슬롯

이를 위해선 named slottemplate block이 필요하다.

슬롯에 컨텐츠 꽃아넣기

named slot이 뭔가요?

named slot은 슬롯이 서로 고유하도록 설정하는 방법이다.
이를 위해 슬롯에는 사용자 지정 식별자로 지정할 수 있는 name prop이 있다.

 

📄CustomLayout.vue

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

그럼 특정 슬롯에 컴포넌트는 어떻게 전달할까?

template block이 뭔가요?

<template> 블록은 SFC의 HTML을 정의하는 방법과 유사하게
슬롯에 전달해야 하는 HTML의 특정 블록을 정의하는 데 사용할 수도 있다.
이를 위하여 v-slot 디렉티브를 적용해야 한다.
v-slot 디렉티브는 콘텐츠를 표시하길 원하는 슬롯 이름 인수를 사용할 수 있다.
v-slot 디렉티브는 콘텐츠를 표시하길 원하는 슬롯 name 인수를 사용할 수 있다.

v-slot 디렉티브는 탬플릿 요소에서만 사용할 수 있다.

slot name

동적 바인딩도 가능

📄App.vue

<template>
  <CustomLayout>
    <template v-slot:header>
      <p>Header content</p>
    </template>
    <template>
      <p>Main body content</p>
    </template>
    <template v-slot:footer>
      <p>Footer content</p>
    </template>
  </CustomLayout>
</template>
v-bind 및 v-on과 유사하게 v-slot에도 단축 구문이 있다. (#)
이전 예제를 다음과 같이 작성할 수 있다.
하지만 해당 방법은 CSS 셀렉터와 혼동을 줄 수 있고, slot에 익숙하지 않은 개발자들을 당황시킬 수 있으니 추천하지 않는다.

📄App.vue

<template>
  <CustomLayout>
    <template #header>
      <p>Header content</p>
    </template>
    <template>
      <p>Main body content</p>
    </template>
    <template #footer>
      <p>Footer content</p>
    </template>
  </CustomLayout>
</template>

정리

  • 슬롯은 컴포넌트에 다양한 유형의 컨텐츠를 제공할 수 있는 유연성을 허용하는 기술이다
  • 슬롯의 단점은 컨텐츠에 대한 제약사항(일관성)을 쉽게 제공할 수 없다는 것이다.
    • html 기반의 명세/문서화로 어느정도 극복할 수 있다.
    • 굳이 하자면 children을 traverse 하는 걸로 되긴 된다...
    • 슬롯 내부 작은 컴포넌트의 제약사항은 해당 컴포넌트에서 제공한다.

 

 

 

반응형