Vue 3 – Slot을 활용한 컴포넌트 콘텐츠 전달

2025. 3. 30. 21:05Front-End/Vue.js

반응형

Vue에서 컴포넌트를 설계하다 보면, 특정 위치에 외부에서 콘텐츠를 끼워 넣고 싶을 때가 있다. 이런 경우 slot을 사용하면 컴포넌트의 내부 구조는 유지하면서 다양한 콘텐츠를 유연하게 전달할 수 있다.


기본 슬롯

가장 단순한 형태의 슬롯은 <slot></slot>이다. 부모 컴포넌트에서 전달한 콘텐츠가 이 자리에 삽입된다.

<!-- BaseButton.vue -->
<template>
  <button class="primary">
    <slot></slot>
  </button>
</template>
<!-- App.vue -->
<template>
  <BaseButton>
    확인
  </BaseButton>
</template>

 

결과

<button class="primary">
  확인
</button>

전달할 수 있는 콘텐츠는 텍스트뿐 아니라 HTML 요소, 다른 컴포넌트 등 어떤 것이든 가능하다.

<BaseButton>
  <strong>확인</strong> <span>✔️</span>
</BaseButton>

 


폴백 콘텐츠

슬롯에 콘텐츠가 전달되지 않은 경우를 대비해 기본값(폴백)을 지정할 수 있다.

<!-- BaseButton.vue -->
<template>
  <button>
    <slot>기본 버튼</slot>
  </button>
</template>

이렇게 하면 슬롯 콘텐츠가 없을 때 "기본 버튼"이 출력된다.


이름이 있는 슬롯 (Named Slots)

복잡한 컴포넌트에서 여러 위치에 서로 다른 콘텐츠를 전달하려면 이름 있는 슬롯을 사용한다.

<!-- CardLayout.vue -->
<template>
  <section class="card">
    <header>
      <slot name="header"></slot>
    </header>
    <div class="content">
      <slot></slot> <!-- default slot -->
    </div>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </section>
</template>

 

사용 예시:

<CardLayout>
  <template #header>
    <h1>공지사항</h1>
  </template>

  <p>내용을 입력해주세요.</p>

  <template #footer>
    <small>ⓒ 2025</small>
  </template>
</CardLayout>
  • #header, #footer는 v-slot:header, v-slot:footer의 축약형
  • 이름이 없는 슬롯은 default 슬롯이며, 암시적으로 전달된다

default 슬롯의 암시적 전달

아래 예시는 default 슬롯을 명시하지 않고도 콘텐츠를 전달하는 방식이다.

<CardLayout>
  <template #header>헤더</template>
  본문 내용입니다.
  <template #footer>푸터</template>
</CardLayout>

"본문 내용입니다."는 default 슬롯으로 자동 인식된다.


동적 슬롯 이름

슬롯 이름을 변수로 동적으로 설정할 수 있다.

<CardLayout>
  <template v-slot:[currentSlot]>
    동적 슬롯 콘텐츠
  </template>
</CardLayout>

변수 currentSlot의 값에 따라 다른 슬롯에 콘텐츠가 들어간다.


슬롯의 렌더링 범위

기본적으로 슬롯 콘텐츠는 부모 컴포넌트의 데이터에만 접근할 수 있고, 자식 컴포넌트의 데이터는 접근할 수 없다.

<CardLayout>
  {{ parentData }}         <!-- 가능 -->
  {{ childInternalValue }} <!-- ❌ 불가능 -->
</CardLayout>

 

 

Scoped Slots (자식 → 부모 데이터 전달)

자식 컴포넌트의 데이터를 부모 슬롯 콘텐츠로 전달하고 싶을 때는 Scoped Slot을 사용한다.

<!-- InfoBox.vue -->
<template>
  <div>
    <slot :message="infoMessage" :status="status"></slot>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const infoMessage = ref('데이터가 준비되었습니다');
    const status = ref('success');
    return { infoMessage, status };
  }
};
</script>
<!-- App.vue -->
<InfoBox v-slot="{ message, status }">
  <p>{{ message }} - 상태: {{ status }}</p>
</InfoBox>
  • 자식 컴포넌트는 <slot :속성="값">으로 데이터를 전달
  • 부모 컴포넌트는 v-slot="{ 속성명 }"으로 데이터를 받음

Named Scoped Slot

이름이 있는 슬롯에도 Scoped Slot 방식을 사용할 수 있다.

<!-- App.vue -->
<InfoBox>
  <template #header="{ message }">
    <h3>{{ message }}</h3>
  </template>

  <template #default="{ status }">
    <p>현재 상태: {{ status }}</p>
  </template>
</InfoBox>
  • 각 슬롯에 필요한 데이터를 선택적으로 전달받아 사용할 수 있다

정리

구분 설명

기본 슬롯 콘텐츠를 자식 컴포넌트 내부로 전달하는 가장 단순한 형태
폴백 콘텐츠 슬롯 콘텐츠가 없을 때 출력될 기본값
이름 있는 슬롯 여러 위치에 다양한 콘텐츠 전달 가능
Scoped Slot 자식 컴포넌트에서 데이터를 부모 슬롯 콘텐츠로 전달
Named Scoped Slot 이름 있는 슬롯에서도 scoped 방식 사용 가능
슬롯 범위 기본적으로 부모의 데이터에만 접근 가능, 자식 데이터는 scoped 방식으로만 가능

슬롯은 컴포넌트의 확장성과 유연성을 크게 높여주는 기능이다.

단순히 컴포넌트의 콘텐츠를 바꾼다는 수준을 넘어서, 자식 → 부모 방향 데이터 전달까지도 가능하게 해주기 때문에 컴포넌트를 좀 더 재사용 가능하게 만드는 핵심적인 기능 중 하나라고 볼 수 있다.

반응형