티스토리 뷰

Vue.js 컴포넌트는 기본적으로 시작 태그종료 태그 사이에 오는 컨텐츠(다른 컴포넌트, 요소, 텍스트 노드)를 무시하고 렌더링된다.

그러나 컴포넌트가 사용되는 상황에 따라 외부로부터 컨텐츠를 전달 받는 편이 컴포넌트를 재사용하기에 유리한 경우가 있다. 이렇게 외부로부터 컨텐츠를 전달받는 수단을 슬롯(slot) 이라고 한다.

모달 윈도우 컴포넌트를 예로 들어보자. 모달 윈도우의 내용은 해당 모달 윈도우가 사용되는 상황에 따라 달라진다. 단순히 텍스트로 된 내용만 표시하는 것이라면 속성을 통해 텍스트를 전달받기만 하면 될 수도 있다.

<Modal :message="text"></Modal>

그러나 메시지에 강조 표시를 추가하고 싶다거나 메시지에 삽입된 URL을 링크로 만들어야 하는 경우 등이 있을 수 있다. 단순히 속성으로 값을 전달하는 것만으로는 이런 기능을 구현하기 어렵다. 이런 경우에 슬롯을 통해 외부로부터 컨텐츠를 전달받을 수 있다면 유연성도 높아지고 모달 윈도우로 노출할 컨텐츠가 무엇인지 템플릿만 봐도 한눈에 파악하기 쉽다.

<Modal>
  <p>
    <b>중요 공지</b>
    1월부터 <a href="./terms.html">이용 약관</a>이 변경됩니다.
  </p>
</Modal>

또 최근에는 브랜드 통일성을 위해 전사적으로 UI 프레임워크를 적용하는 사례가 늘고 있다. 이런 경우 버튼 등의 기본적인 요소부터 스타일을 통일할 수 있도록 컴포넌트를 정의한다. HTML 표준으로 제공되는 button 요소의 내용은 텍스트 노드로 지정할 수 있는데, 슬롯을 사용하면 컴포넌트에서도 HTML 표준 button 요소처럼 내용을 지정할 수 있다.

<!-- 버튼 텍스트를 속성을 통해 설정 -->
<MyButton text="전송"></MyButton>

<!-- 버튼 텍스트를 텍스트 노드를 통해 설정 -->
<MyButton>전송</MyButton>

이 외에도 다음과 같은 경우에 슬롯을 사용한다.

  • 페이지 전체 레이아웃을 나타내는 컴포넌트에 페이지 헤더, 바디, 푸터를 삽입
  • 액션 시트 컴포넌트에 선택 가능한 액션의 선택지를 삽입
  • 슬라이더 컴포넌트에 해당 아이템의 내용을 삽입

단일 슬롯

슬롯은 크게 단일 슬롯이름을 가지는 슬롯(Named Slot) 의 두 종류로 나뉜다.

앞서 본 MyButton 구현을 예로 들어 보자. 다음 HTML과 스크립트를 각각 파일에 저장한 다음 HTML 파일을 브라우저로 연다.

<!DOCTYPE html>
<title>Vue app</title>
<script src="https://unpkg.com/vue@2.5.17"></script>

<div id="app">
  <!-- 컨텐츠를 포함하는 컴포넌트 마운트 -->
  <my-button>전송</my-button>

  <!-- 컨텐츠 없이 컴포넌트 마운트 -->
  <my-button></my-button>
</div>

<script>
  var MyButton = {
      template = `
      <button>
          <!-- 부모 컴포넌트에서 받아온 컨텐츠로 갈아 끼움 -->
        <slot>OK</slot>
        </button>
    `
  }

  new Vue({
      el: '#app',
    components: {
      MyButton: MyButton
    }
  })
</script>

브라우저에서 파일을 열어보면 '전송'과 'OK' 버튼, 이렇게 2개의 버튼이 보인다. 표시된 화면의 DOM 트리를 참조하며 슬롯이 어떻게 동작하는지 확인해 보자.

컴포넌트 템플릿에 포함된 slot 요소가 바로 컨텐츠가 삽입되는 부분이다. 이 예제에서는 MyButtonOK를 감싸고 있는 <slot>..</slot> 부분에 해당한다.

HTML에 첫 번째 <my-button> 요소를 배치하면서 '전송'이라는 문자열을 컨텐츠로 전달했다. 그러면 자식 컴포넌트의 <slot>OK</slot> 요소를 배치하면서 '전송'이라는 문자열로 치환된다. 결국 최종 렌더링 결과는 <button>전송</button> 이 된다.

두 번째 <my-button> 요소는 컨텐츠를 지정하지 않았다. 이 경우는 자식 컴포넌트 slot 요소에 포함된 컨텐츠의 기본값이 사용된다. 그러므로 최종 결과는 <my-button>OK</my-button> 이 된다.

이름을 갖는 슬롯 (Named Slot)

slot 요소의 name 속성을 이용해 슬롯에 이름을 붙일 수 있다. 이 이름으로 특정한 슬롯을 지정해 컨텐츠를 삽입할 수 있다. 앞서 설명한 단일 슬롯은 고정된 위치 한 곳에만 컨텐츠를 전달할 수 있었다.

컴포넌트 중에도 여러 가지 컨텐츠로 구성되는 것들이 있다. 페이지 레이아웃이나 모달 윈도우 등은 헤더와 바디, 푸터 등의 부분으로 이루어진다. 이런 경우 각각의 부분을 개별 슬롯으로 다룰 수 있도록 이름을 붙인다.

이번에는 페이지 레이아웃을 예제로 살펴보자. 먼저 페이지 레이아웃을 다룰 컴포넌트를 표시한다. 이 컴포넌트는 slot 요소를 3개 포함한다. 이 중 헤더와 푸터 슬롯에는 이름이 붙어 있고, 바디는 이름 없는 슬롯, 그러니까 앞서 설명한 단일 슬롯이다. 이런 식으로 단일 슬롯과 이름을 갖는 슬롯을 함께 사용할 수 있다.

var MyPage = {
  template = `
    <div>
      <header>
        <!-- 헤더 슬롯 (이름을 갖는 슬롯) -->
        <slot name="header"></slot>
      </header>    
      <main>
          <!-- 바디 슬롯 -->
        <slot></slot>
      </main>
      <footer>
          <!-- 푸터 슬롯 (이름을 갖는 슬롯) -->
        <slot name="footer"></slot>
      </footer>
    </div>
  `
}

new Vue({
  el: '#app',
  components: {
    MyPage: MyPage
  }
})

그 다음 이 컴포넌트를 배치하고 슬롯에 삽입할 컨텐츠를 전달하는 부분을 살펴보자. <my-page> 요소 안을 보면 3가지 컨텐츠가 있다. 컨텐츠의 slot 속성에 이름을 지정해서 콘텐츠가 삽입될 위치를 지정한다.

slot 속성이 지정되지 않은 컨텐츠는 단일 슬롯의 컨텐츠로 간주한다.

<!DOCTYPE html>
<title>Vue app</title>
<script src="https://unpkg.com/vue@2.5.17"></script>

<div id="app">
  <my-page>
      <!-- name 속성값이 header의 <slot> 과 치환됨 -->
    <h1 slot="header">This is my page</h1>

    <!-- 단일 슬롯과 치환되는 컨텐츠 -->
    <p>
      Lorem ipsum dolor sit amet, ... et.
    </p>

    <!-- name 속성값이 footer의 <slot>과 치환됨 -->
    <p slot="footer">This is footer</p>
  </my-page>
</div>

브라우저로 확인해보면 각 슬롯에 지정된 컨텐츠가 삽입됐음을 알 수 있다.

푸터 부분을 노출하고 싶지 않다면 HTML 에서 <p slot="footer">This is footer</p> 부분을 삭제하면 된다.

'JavaScript > Vue.js' 카테고리의 다른 글

Vue Router :: Router 인스턴스와 Route 객체 비교  (0) 2021.08.08
VUE.JS의 EVENT 처리  (0) 2019.07.27
[Vue.js] 뷰 컴포넌트  (0) 2019.01.14
[Vue.js] 뷰 인스턴스  (0) 2019.01.14
[Vue.js] 뷰 템플릿  (0) 2019.01.13
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크