Vue 3 컴포넌트 통신 총정리 — props, emit, provide/inject
1. Props — 부모 → 자식
<!-- Parent.vue -->
<template>
<Child :title="pageTitle" :count="42" />
</template>
<script setup>
import Child from './Child.vue'
const pageTitle = '안녕하세요'
</script>
<!-- Child.vue -->
<script setup>
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 },
})
</script>
<template>
<h1>{{ props.title }} ({{ props.count }})</h1>
</template>
2. Emit — 자식 → 부모
<!-- Child.vue -->
<script setup>
const emit = defineEmits(['submit', 'cancel'])
function handleClick() {
emit('submit', { name: 'Hoon' })
}
</script>
<!-- Parent.vue -->
<template>
<Child @submit="onSubmit" />
</template>
<script setup>
function onSubmit(payload) {
console.log(payload.name) // 'Hoon'
}
</script>
3. provide / inject — 깊은 계층 전달
중간 컴포넌트를 거치지 않고 하위 어느 컴포넌트에나 데이터를 전달합니다.
<!-- 최상위 조상 컴포넌트 -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme) // key, value
</script>
<!-- 깊은 자식 컴포넌트 -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme', 'light') // 기본값 설정 가능
</script>
반응형 데이터(
ref,reactive)를 provide하면 자식에서도 반응성이 유지됩니다.
4. v-model — 양방향 바인딩
<!-- Parent.vue -->
<template>
<CustomInput v-model="searchText" />
</template>
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
5. 방법 선택 가이드
| 상황 | 방법 |
|---|---|
| 부모 → 자식 1단계 | props |
| 자식 → 부모 | emit |
| 여러 단계 깊이 | provide / inject |
| 여러 컴포넌트 공유 | Pinia |
| 양방향 바인딩 | v-model |