plogger

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