Vue Components

케로로·2026년 8월 28일

Component

Component 개요

  • 기본 개념: 소프트웨어 공학에서 컴포넌트는 독립적인 기능을 수행하며 교체 가능한 '표준화된 소프트웨어 모듈(부품)'을 의미합니다. 독립성(Independency)과 교체 가능성(Replaceability)이 핵심입니다.

  • Vue 컴포넌트: 웹페이지를 구성하는 독립적이고 재사용 가능한 부품 단위입니다.

    • HTML, CSS, JavaScript를 하나의 .vue 파일 안에 모아 다루는 SFC(Single File Component) 형식을 갖습니다.
    • 어플리케이션은 이러한 여러 컴포넌트들이 모여 일정한 트리(Tree) 구조로 연결되면서 구성됩니다.

컴포넌트 계층 관계 (Hierarchy)

컴포넌트들은 상하/좌우 관계를 형성하며 트리 구조로 배치됩니다.

  • 부모-자식 (Parent-Child): 상위 블록(부모)이 하위 블록(자식)을 품는 구조입니다. 철저히 독립되어 있어 상대방의 내부 변수나 방을 직접 들여다볼 수 없습니다.
  • 형제 (Sibling): 동일한 부모 컴포넌트 아래 나란히 배치된 자식들 간의 관계입니다. 직접 통신할 수 있는 선이 없으므로 데이터 교환 시 반드시 부모를 거쳐서 올라갔다 내려와야 합니다.
  • 조상-후손 (Ancestors-Descendants): 다중 계층 구조로 연결되어 자식의 자식, 그 자식까지 내려가는 관계를 뜻합니다.

컴포넌트 등록 방식 비교 (Local vs Global)

지역(Local) 등록

<script setup> 내부에서 import하면 <template> 영역에서 내장 태그처럼 바로 쓸 수 있습니다.
표기법은 PascalCase(<BaseButton/>)와 kebab-case(<base-button></base-button>) 둘 다 지원합니다.

<script setup>
import BaseButton from './components/BaseButton.vue'
</script>

<template>
  <div class="box">
    <h3>컴포넌트 조립 테스트</h3>
    <hr />
    <!-- 파스칼케이스 표기 -->
    <BaseButton />
    <!-- 케밥케이스 표기 -->
    <base-button></base-button>
  </div>
</template>

전역(Global)

app.component()를 사용해 전역으로 등록하며, 체이닝(Chaining) 형태로 연결해서 등록할 수도 있습니다.

// main.js
import { createApp } from 'vue'
import App from './App.vue'

import BaseButton from './components/BaseButton.vue'
import BaseInput from './components/BaseInput.vue'

const app = createApp(App)

// app.component('태그이름', 컴포넌트변수)
app.component('BaseButton', BaseButton)
app.component('BaseInput', BaseInput)

// 체이닝 형태 작성 방식:
// app.component('BaseButton', BaseButton).component('BaseInput', BaseInput)

app.mount('#app')

비교 (Local vs Global)

구분지역 등록 (Local Registration)전역 등록 (Global Registration)
등록 위치해당 컴포넌트의 <script setup> 내부main.js 파일의 Vue 인스턴스 생성 시점
사용 범위import한 해당 파일의 <template> 내부애플리케이션 전체 어디서든 import 없이 사용 가능
주요 특징사용하지 않는 컴포넌트는 트리쉐이킹(최적화)됨자주 사용하는 공통 UI 컴포넌트(버튼, 인풋 등)에 적합

Vue 3 핵심 내장 함수 카테고리

  • Application: 앱 생성 및 전역 설정 함수 (createApp, app.config.*)
  • Reactive State: 반응형 상태 생성 및 제어 함수 (ref, reactive, readonly, toRefs 등)
  • Computed & Watchers: 데이터 계산 및 변경 감시 함수 (computed, watch, watchEffect)
  • Lifecycle Hooks: 생명주기 단계별 실행 콜백 함수 (setup, onMounted, onUpdated, onUnmounted 등)
  • Component Composition: 컴포넌트 구성 및 통신 함수 (defineProps, defineEmits, defineExpose 등)
  • Rendering: Virtual DOM 및 렌더링 제어 함수 (h, nextTick 등)
  • Dependency Injection: 하위 컴포넌트로 데이터 직통 전달 함수 (provide, inject)

컴포넌트 생명주기 4단계 (Component Lifecycle)

단계컴포넌트 상태주요 작업 및 상세 내용
1. 생성 (Creation)JS 메모리상에만 존재 (HTML 미부착)반응형 변수(ref, reactive), computed, watch 초기화
2. 부착 (Mounting)실제 DOM(HTML)에 화면이 붙은 상태DOM 요소 접근 가능, 백엔드 API 호출/초기 데이터 로딩
3. 갱신 (Updating)반응형 데이터 변경으로 화면 리렌더링리렌더링 직후 변경된 HTML 크기/스크롤 위치 계산
4. 소멸 (Unmounting)v-if="false" 등으로 화면에서 제거 및 파괴메모리 누수 방지를 위한 타이머(setInterval) 해제, 이벤트 리스너 제거

핵심 라이프사이클 훅 (Lifecycle Hooks)

  • setup(): 컴포넌트 생성 단계. 가장 먼저 실행되며 반응형 변수 및 함수를 초기화합니다.
  • onMounted(): DOM에 화면이 부착된 직후 호출됩니다. API 호출이나 DOM 직접 조작에 가장 적합한 시점입니다.
  • onUpdated(): 데이터가 변경되어 DOM 리렌더링이 완료된 직후 호출됩니다.
  • onUnmounted(): 컴포넌트가 DOM에서 완전 파괴된 후 호출됩니다. 타이머나 이벤트 리스너를 정리(Clean-up)하는 용도로 사용합니다.
<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from 'vue'

const count = ref(0)
let timerId = null

// 1. 생성 단계: <script setup> 본문 코드 실행 시점
console.log('1. [setup] 컴포넌트가 메모리에 생성되었습니다. (DOM 접근 불가)')

// 2. 부착 단계: 화면에 HTML 요소가 정상적으로 올라간 시점
onMounted(() => {
  console.log('2. [onMounted] 화면에 완벽히 부착되었습니다! (API 호출/DOM 조작 적기)')
  
  // 3초마다 count를 1씩 증가시키는 타이머 가동
  timerId = setInterval(() => {
    count.value++
  }, 3000)
})

// 3. 갱신 단계: count 변수 변경으로 화면이 리렌더링될 때마다 실행
onUpdated(() => {
  console.log(`3. [onUpdated] 데이터가 변경되어 화면을 새로 그렸습니다. (현재 count: ${count.value})`)
})

// 4. 소멸 단계: 컴포넌트가 화면에서 완전히 파괴되어 사라질 때 실행
onUnmounted(() => {
  // 백그라운드에서 타이머가 계속 도는 메모리 누수를 막기 위해 청소 작업 진행
  clearInterval(timerId)
  console.log('4. [onUnmounted] 컴포넌트가 소멸했습니다. 타이머 청소 완료!')
})
</script>

Props & Emits

Vue 3의 모든 컴포넌트 연동은 데이터는 위에서 아래로(Props), 이벤트는 아래에서 위로(Emits) 흐르는 단방향 데이터 흐름 구조를 따릅니다.

구분Props (하행선)Emits (상행선)
개념 정의부모가 자식에게 전달하는 반응형 데이터자식이 부모에게 보고하는 이벤트
흐름 방향부모 → 자식 (위에서 아래로)자식 → 부모 (아래에서 위로)
데이터 권한읽기 전용 (Read-only), 자식 측 수정 불가부모에게 상태 변경 요청 및 값 전달 가능
컴파일러 매크로defineProps({ ... })defineEmits([ ... ])
부모 측 바인딩자식 태그 속성에 콜론(:)으로 주입자식 태그 이벤트에 골뱅이(@)로 청취

Compiler Macro(컴파일러 매크로)란?

  • 런타임(Runtime) 실행 시점이 아닌 빌드(Build) 시점에 Vue 컴파일러가 코드를 변환해 주는 특수 예약어입니다.
  • defineProps(), defineEmits(), defineExpose() 같은 함수들이 이에 해당합니다.
  • 별도의 import 없이 <script setup> 내부에서만 즉시 사용할 수 있습니다.

defineProps()

  • 역할: 자식 컴포넌트 내부에서 부모가 전달할 데이터(속성)의 이름과 규격을 선언하는 Vue 3 내장 함수입니다.
  • 특징: 별도의 import 선언 없이 <script setup> 내에서 바로 사용할 수 있는 컴파일러 매크로(Compiler Macro)입니다.
  • 읽기 전용 (ReadOnly): 부모로부터 전달받은 Props 데이터는 자식 컴포넌트에서 직접 수정할 수 없으며 변경을 시도하면 에러가 발생합니다.

Props 정의 형식 비교

구분배열 형식 (['title', 'count'])객체 형식 ({ title: String, likes: Number })
장점코드가 간결하고 빠르게 선언 가능데이터 타입 검증, 필수 값 여부, 기본값 지정 가능
단점기본값 및 타입 검증 불가능배열 형식보다 코드가 다소 길어짐
추천간단한 프로토타입 개발 시실무 환경 및 견고한 컴포넌트 작성 시 (권장)
// 배열 형식
const props = defineProps(['title', 'count'])

// 객체 형식
defineProps({
  // 1. 타입만 간단히 지정하는 경우
  title: String,
  // 2. 필수 값과 기본값까지 꼼꼼하게 지정하는 경우
  likes: {
    type: Number,
    required: true // 부모가 이 값을 안 넘기면 에러발생.
  },
  status: {
    type: String,
    default: '대기 중' // 부모가 값을 안 주면 이 값이 기본으로 세팅.
  }
})

Props 사용하기

<template> 영역에서 Props 사용하기

  • 특징: Vue의 템플릿 엔진이 바인딩을 자동으로 처리하므로 props. 접두사 없이 선언한 속성명(변수명)을 그대로 사용할 수 있습니다.
  • 예시:
<template>
  <h1>{{ title }}</h1>
  <p>좋아요: {{ likes }}</p>
</template>

<script setup> 영역에서 Props 사용하기

  • 특징: JavaScript/TypeScript 로직 내부에서는 defineProps()가 반환하는 객체를 props라는 변수에 할당받은 후 점 표기법(props.속성명)을 통해 값에 접근해야 합니다.
  • 예시:
<script setup>
// 1. defineProps 결과를 'props' 변수에 할당
const props = defineProps({
  title: String,
  likes: Number
})

// 2. JS 함수 및 로직 내부에서 접근할 때는 'props.'을 붙여서 호출
const checkPopularity = () => {
  if (props.likes > 100) {
    console.log(`${props.title}은 인기 게시글입니다.`)
  }
}
</script>

핵심 요약

구분<template> 영역<script setup> 영역
접근 방식변수명 직접 사용props.변수명 형태 사용
작성 형태{{ title }}, {{ likes }}props.title, props.likes
접두사 필요 여부불필요필수 (props.)

부모 컴포넌트에서 자식 컴포넌트로 데이터 전달

자식 컴포넌트 (ChildComponent.vue)
부모 컴포넌트로부터 전달받은 message 데이터를 받아 화면에 출력합니다.

<script setup>
// 부모 컴포넌트가 전달할 Props의 타입(String)을 정의
const props = defineProps({
  message: String
});
</script>

<template>
  <!-- <template> 태그 내부에서는 props. 생략 후 변수명(message)으로 바로 접근 가능 -->
  <div>{{ message }}</div>
</template>

부모 컴포넌트 (ParentComponent.vue)
반응형 데이터(ref)를 생성하고 자식 컴포넌트를 불러와 데이터 속성을 바인딩(:message)하여 전달합니다.

<script setup>
import { ref } from 'vue';
// 자식 컴포넌트 불러오기
import ChildComponent from './ChildComponent.vue';

// 자식에게 전달할 반응형 문자열 데이터 선언
const parentMessage = ref('안녕하세요, 자식 컴포넌트!');
</script>

<template>
  <div>
    <!-- 
      :message - 자식 컴포넌트의 props 이름
      "parentMessage" - 부모가 전달할 반응형 데이터 변수명 
    -->
    <ChildComponent :message="parentMessage" />
  </div>
</template>

네이밍 컨벤션 (CamelCase vs kebab-case)

HTML 표준은 대소문자를 구분하지 못하기 때문에 Vue에서는 JavaScript와 HTML 환경에 맞춘 자동 매핑 규격을 따릅니다.

  • Script 및 Props 정의 (JS): camelCase 사용 (cityName, areaCode)

  • Template 속성 주입 (HTML): kebab-case 사용 (:city-name="selectCityName", :area-code="areaCode")

부모 컴포넌트 (ParentComponent.vue)

<script setup>
import { ref } from 'vue'
// 컴포넌트 파일 가져올 때는 파스칼 케이스(PascalCase)
import WeatherCard from './components/WeatherCard.vue'
// 내부 반응형 데이터는 카멜 케이스(camelCase)
const selectCityName = ref('수원시 영통구')
const areaCode = ref(103)
</script>
  
<template>
  <div>
    <WeatherCard
      :city-name="selectCityName"
      :area-code="areaCode"
    />
  </div>
</template>

자식 컴포넌트 (ChildComponent.vue)

<script setup>
// defineProps 내부 규격 선언은 카멜 케이스(camelCase)
defineProps({
  cityName: String,
  areaCode: Number
})
</script>

<template>
  <div class="card">
    <p>지역명: {{ cityName }}</p>
    <p>지역 코드: {{ areaCode }}</p>
  </div>
</template>

Prop Validation (유효성 검사) 옵션

부모가 잘못된 데이터를 넘겼을 때 콘솔 경고를 띄워 에러 추적을 용이하게 만듭니다.

  • 타입 단독 지정: cityName: String
  • 다중 타입 허용: areaId: [String, Number]
  • 필수 값 검사 (required): required: true 지정 시 부모가 해당 속성을 누락하면 경고 출력
  • 기본값 세팅 (default): 값이 넘어오지 않았을 때 사용할 기본값 설정
  • 커스텀 검사기 (validator): 함수 형태로 세부 조건 조건문 작성 (예: return value >= 0 && value <= 100)

지원 자료형(Type) 및 주의할 점

주의: 배열(Array)과 객체(Object)의 기본값(default)은 참조 타입 메모리 공유 문제를 방지하기 위해 반드시 화살표 함수 형태로 반환해야 합니다. (예: default: () => [])

유형defineProps 기입 법예시 데이터주요 주의사항
문자열 / 숫자 / 논리String, Number, Boolean"서울", 25, true기본 데이터 타입
배열 (Array)Array['서울', '수원']default 지정 시 반드시 팩토리 함수 사용 (() => [])
객체 (Object)Object{ name: '수원' }default 지정 시 반드시 팩토리 함수 사용 (() => ({}))
기타Function, Date 등콜백 함수, 날짜 객체 등함수 및 비동기 객체 검증 지원
defineProps({
  // 1. 문자열 (String)
  cityName: String,
  // 2. 숫자 (Number)
  temperature: Number,
  // 3. 논리형 (Boolean)
  isActive: {
    type: Boolean,
    default: false
  },
  // 4. 배열 (Array)
  weeklyForecast: {
    type: Array,
    // 중요: 배열의 기본값은 무조건'새 바구니를 구워내는 화살표 함수' 형태로!
    default: () => []
  },
  // 5. 객체 (Object)
  coordinates: {
    type: Object,
    // 중요: 객체의 기본값도 무조건 화살표 함수 형태로 반환!
    default: () => ({ lat: 37.5, lng: 126.9 })
  }
})

사용법 예제 코드

자식 컴포넌트 (ChildComponent.vue)
<template>에서는 정의된 변수명을 그대로 바로 조회하며 형태를 사용합니다.

<script setup>
// Props 정의 및 반환 객체를 props 변수에 할당
const props = defineProps({
  cityName: String,
  temperature: Number,
  isActive: {
    type: Boolean,
    default: false
  },
  // 배열/객체는 함수 형태로 default 반환
  weeklyForecast: {
    type: Array,
    default: () => []
  }
})

// Script 내에서 사용할 때는 'props.' 접두사 필수
const checkStatus = () => {
  console.log(`${props.cityName}의 현재 기온: ${props.temperature}도`)
}
</script>

<template>
  <!-- Template 내에서는 props. 생략하고 직접 참조 가능 -->
  <div>
    <h3>{{ cityName }}</h3>
    <p>기온: {{ temperature }}°C</p>
    <p>활성화 상태: {{ isActive }}</p>
  </div>
</template>

부모 컴포넌트 (ParentComponent.vue)

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const selectCityName = ref('수원시 영통구')
const currentTemp = ref(24)
</script>

<template>
  <div>
    <!-- camelCase 속성은 HTML 상에서 kebab-case로 작성 -->
    <ChildComponent 
      :city-name="selectCityName" 
      :temperature="currentTemp" 
    />
  </div>
</template>

defineEmits()

  • 역할: 자식 컴포넌트에서 부모 컴포넌트로 사용자 정의 이벤트(Custom Event)를 송신하기 위해 사용하는 Vue 3 내장 컴파일러 매크로 함수입니다.
  • 특징: defineProps()와 마찬가지로 별도의 import 없이 <script setup> 안에서 즉시 호출 가능합니다.
  • 작동 원리: 자식 컴포넌트 내부의 클릭, 키보드 등의 이벤트 또는 로직 변화를 트리거로 삼아 부모가 등록해둔 이벤트 리스너를 호출합니다.
  • 데이터 전달(Payload): emit('이벤트명', 데이터) 형태로 호출하며, 첫 번째 인자에는 이벤트 식별자(문자열), 두 번째 인자부터는 부모에게 전달할 데이터(Payload)를 실어 보냅니다.
  • 네이밍 규칙: 이벤트 타입명은 HTML 속성 표기 방식에 맞춰 kebab-case (예: select-city, update-request)로 작성하는 것이 관례입니다.

Props와 Emits 양방향 흐름 예제

Props로 받은 데이터를 자식에서 출력하고 자식의 클릭/요청에 의해 부모의 상태(State)가 변경되는 종합 코드 구조입니다.

자식 컴포넌트 (PropEmitsChild.vue)

<script setup>
// 1. 부모에게 물려받을 Props 정의 (타입 및 필수 검사)
const props = defineProps({
  parentData: {
    type: String,
    required: true
  }
})

// 2. 부모에게 송신할 커스텀 이벤트 식별자 등록 (관례상 변수명은 'emit'으로 지정)
const emit = defineEmits(['update-request'])

// 3. 버튼 클릭 시 페이로드를 실어 상위로 이벤트를 디스패치(발송)하는 함수
const sendNotification = () => {
  const payload = 'Child에서 가공한 새로운 데이터'
  emit('update-request', payload) // 첫 번째 인자: 이벤트명, 두 번째 인자: 데이터
}
</script>

<template>
  <div class="child-container">
    <h2>하위 컴포넌트 (Child)</h2>
    <p>수신된 Props 데이터: <strong>{{ parentData }}</strong></p>
    <br />
    <button @click="sendNotification">상위 컴포넌트로 갱신 요청 (Emit)</button>
  </div>
</template>

부모 컴포넌트 (PropEmitsParent.vue)

<script setup>
import { ref } from 'vue'
import PropEmitsChild from './PropEmitsChild.vue'

// 1. 부모의 로컬 반응형 상태 정의
const message = ref('Parent 초기 메시지')

// 2. 하위 컴포넌트의 커스텀 이벤트를 수신했을 때 실행될 핸들러 함수
// 하위 컴포넌트가 emit으로 보낸 payload가 인자(newValue)로 자동 주입됩니다.
const handleUpdateRequest = (newValue) => {
  message.value = newValue
}
</script>

<template>
  <div class="practice-section">
    <h2>Props & Emits</h2>
    <div class="parent-container">
      <h2>상위 컴포넌트 (Parent)</h2>
      <p>현재 로컬 데이터(State): <strong>{{ message }}</strong></p>
      <br />
      <!-- 
        :parent-data - Props 전달 (데이터 하행)
        @update-request - Emit 이벤트 수신 및 핸들러 연결 (이벤트 상행)
      -->
      <PropEmitsChild 
        :parent-data="message" 
        @update-request="handleUpdateRequest" 
      />
    </div>
  </div>
</template>

Provide & Inject

Provide / Inject 핵심 개념

  • Props Drilling 문제 해결: 상위 컴포넌트에서 까마득히 멀리 떨어진 최하위(자식) 컴포넌트로 데이터를 전달할 때 중간 단계 컴포넌트들이 데이터를 사용하지 않음에도 일일이 Props를 받아 넘겨줘야 하는 현상을 방지합니다.
  • 직접 주입 방식: 조상 컴포넌트가 provide로 데이터를 제공하면 하위 컴포넌트는 중간 컴포넌트들을 건너뛰고 inject 함수를 통해 데이터를 다이렉트로 전달받아 사용합니다.
  • 특징: 중첩 구조가 깊어도 중간 컴포넌트는 해당 데이터의 존재를 몰라도 됩니다. (단, 앱 규모가 커지면 전역 상태 관리 라이브러리인 Pinia를 더 주로 활용합니다.)

핵심 코드 사용법

조상 컴포넌트 (GrandParent.vue)
provide('키_이름', 전달할 값) 형식으로 데이터를 등록합니다.

<script setup>
import { ref, provide } from 'vue' // provide 함수 모듈 import

const themeColor = ref('dark-mode')

// 주입할 Key 이름과 실제 값(Value)을 등록
provide('globalTheme', themeColor)
</script>

최하위 컴포넌트 (GrandChild.vue)
inject('키_이름') 형식으로 데이터를 직접 인젝션(주입)받아 사용합니다.

<script setup>
import { inject } from 'vue' // inject 함수 모듈 import

// 조상이 provide로 등록했던 Key 이름을 지정하여 값 추출
const theme = inject('globalTheme')
</script>

provide() 사용 위치 및 방식 비교

provide()는 선언하는 위치와 목적에 따라 컴포넌트 내 주입 방식과 앱 전역 주입 방식으로 나뉩니다.

구분컴포넌트 내 주입 (provide)앱 전역 주입 (app.provide)
작성 위치특정 .vue 파일의 <script setup> 내부main.js (또는 main.ts) 파일
구문 예시provide('key', value)app.provide('key', value)
전달 범위해당 컴포넌트와 그 하위 자식 컴포넌트들앱 전체의 모든 컴포넌트
주요 용도특정 컴포넌트 트리 내 데이터 공유 (반응성 유지)앱 전역 설정, 전역 상태 및 인스턴스 공유

Component Slot

  • 개념: 자식 컴포넌트의 특정 구역을 <slot> 영역으로 비워두고 부모 컴포넌트가 자식을 사용할 때 그 내부에 넣어줄 HTML 마크업 및 템플릿 콘텐츠를 동적으로 주입하여 렌더링하는 기능입니다.

  • Props vs Slot 차이점: Props는 부모가 자식에게 데이터 값을 주입할 때 사용하고 Slot은 HTML 태그나 디자인 레이아웃 자체를 주입할 때 사용합니다.

Slot의 3가지 유형별 정리

① Default Slot (기본 / 이름 없는 슬롯)

별도의 속성 없이 단순하게 <slot> 태그만 사용하는 기본 형태입니다. 부모가 전달한 콘텐츠가 지정된 슬롯 위치에 대체되어 들어갑니다.

  • 폴백(Fallback) 콘텐츠: 부모가 아무런 템플릿 조각을 넘겨주지 않으면 내부에 정의해둔 디폴트 문구가 출력됩니다.
<!-- 자식: SlotDefaultChild.vue -->
<template>
  <div class="base-card">
    <slot>
      <p>기본 콘텐츠 영역입니다. (부모가 안 보낼 때 표시)</p>
    </slot>
  </div>
</template>

<!-- 부모: SlotDefaultParent.vue -->
<template>
  <SlotDefaultChild>
    <p>단순한 텍스트 문장을 주입합니다.</p>
  </SlotDefaultChild>
</template>

② Named Slot (이름 있는 슬롯)

컴포넌트 내에 여러 개의 Slot 영역이 필요한 경우 name 속성을 부여하여 각 슬롯의 위치를 다르게 지정합니다.

  • 자식 정의: <slot name="슬롯이름"></slot>
  • 부모 주입: <template v-slot:슬롯이름> 또는 약어인 <template #슬롯이름>을 사용하여 원하는 위치에 코드를 끼워 넣습니다.
<!-- 자식: SlotNamedChild.vue -->
<template>
  <div class="base-card">
    <header>
      <slot name="header"></slot> <!-- 'header' 이름 지정 -->
    </header>
    <main>
      <slot></slot> <!-- name이 없으면 Default Slot이 됨 -->
    </main>
  </div>
</template>

<!-- 부모: SlotNamedParent.vue -->
<template>
  <SlotNamedChild>
    <!-- #header (v-slot:header 축약형) 영역으로 주입 -->
    <template #header>
      <h3>Child 주입 제목</h3>
    </template>
    
    <!-- Default Slot으로 주입 -->
    <p>본문 내용이 이 자리에 배치됩니다.</p>
  </SlotNamedChild>
</template>

③ Scoped Slot (범위 지정 슬롯)

자식 컴포넌트 내부에서 다루는 로컬 데이터를 부모 컴포넌트의 슬롯 마크업 영역으로 전달받아 사용할 때 사용합니다.

  • 자식 바인딩: <slot :속성명="변수명"></slot> 형태로 부모에게 넘겨줄 데이터를 속성으로 바인딩합니다.
  • 부모 수신: 부모는 v-slot="변수주머니"를 지정해 데이터를 전달받은 뒤, 템플릿 내부에서 변수주머니.속성명 형태로 꺼내 사용합니다.
<!-- 자식: SlotScopedChild.vue -->
<script setup>
import { ref } from 'vue'
const message = ref('현재 서버 상태 정상')
const userCount = ref(150)
</script>

<template>
  <div class="base-card">
    <!-- 자식의 message와 userCount 데이터를 슬롯 속성으로 전달 -->
    <slot :text="message" :count="userCount">
      <p>부모가 마크업을 주입하지 않았을 때의 디폴트 화면</p>
    </slot>
  </div>
</template>

<!-- 부모: SlotScopedParent.vue -->
<template>
  <!-- v-slot="slotBag"을 통해 자식이 바인딩한 데이터 객체를 수신 -->
  <SlotScopedChild v-slot="slotBag">
    <div class="display-panel">
      <p>알림 메시지: {{ slotBag.text }}</p>
      <p>접속자 수: {{ slotBag.count }}명</p>
    </div>
  </SlotScopedChild>
</template>

0개의 댓글