2025. 3. 30. 16:49ㆍFront-End/Vue.js
Vue에서 자식 컴포넌트가 부모에게 어떤 동작을 요청하거나 데이터를 전달해야 할 때 사용하는 방법이 있다.
바로 이벤트 발생(emit)이다.
이 글에서는 Vue 3 기준으로 emit을 이용한 컴포넌트 간 커뮤니케이션 방식과, v-model 커스텀 바인딩까지 정리한다.
기본 개념 – $emit
자식 컴포넌트에서 부모 컴포넌트로 이벤트를 전달하려면 $emit()을 사용한다.
<!-- ChildButton.vue -->
<template>
<button @click="$emit('click-button')">클릭</button>
</template>
<!-- App.vue -->
<template>
<ChildButton @click-button="handleClick" />
</template>
<script>
export default {
methods: {
handleClick() {
console.log('버튼 클릭됨');
},
},
};
</script>
자식 컴포넌트에서 $emit('click-button')을 호출하면, 부모는 @click-button을 통해 이를 받을 수 있다.
이벤트에 데이터 담기
이벤트에는 원하는 값을 함께 보낼 수 있다.
<!-- MessageBox.vue -->
<template>
<button @click="$emit('send-message', 'Hello', 'Vue')">메시지 전송</button>
</template>
<!-- App.vue -->
<template>
<MessageBox @send-message="showMessage" />
</template>
<script>
export default {
methods: {
showMessage(a, b) {
console.log(a, b); // "Hello", "Vue"
},
},
};
</script>
참고!
자식은 "이벤트 이름 + 전달할 값"만 정해서 보내고, 그걸 부모가 "받아서 처리하는 함수만 정의"하는 구조임
“나는 'send-message'라는 이름으로 'Hello'와 'Vue'를 보낼게. 이 이벤트가 발생하면 누군가(부모)가 처리하겠지!”
emits 옵션 사용
Vue 3에서는 어떤 이벤트가 발생할 수 있는지를 명시적으로 선언할 수 있다.
배열로 선언
export default {
emits: ['send-message'],
setup(_, { emit }) { // props는 안쓰니깐 생략한다는 의미
emit('send-message', '내용');
},
};
객체로 선언 + 유효성 검사
export default {
emits: {
send: (payload) => {
if (!payload) {
console.warn('payload 누락됨');
return false;
}
return true;
},
},
setup(_, { emit }) {
emit('send', '데이터');
},
};
v-model 커스텀 컴포넌트 만들기
Vue 3에서는 컴포넌트에도 v-model을 사용할 수 있다. 일반적으로는 다음과 같은 형태로 작성한다.
<!-- App.vue -->
<template>
<CustomInput v-model="username" />
</template>
<script>
export default {
data() {
return { username: '' };
},
};
</script>
CustomInput.vue
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
};
</script>
computed로 양방향 바인딩 처리
<template>
<input v-model="value" />
</template>
<script>
import { computed } from 'vue';
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
setup(props, { emit }) {
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
});
return { value };
},
};
</script>
v-model에 전달인자 사용하기
<!-- App.vue -->
<BookEditor v-model:title="title" />
<!-- BookEditor.vue -->
<template>
<input :value="title" @input="$emit('update:title', $event.target.value)" />
</template>
<script>
export default {
props: ['title'],
emits: ['update:title'],
};
</script>
여러 개의 v-model 바인딩
<!-- App.vue -->
<BookEditor v-model:title="bookTitle" v-model:author="bookAuthor" />
<!-- BookEditor.vue -->
<template>
<input :value="title" @input="$emit('update:title', $event.target.value)" />
<input :value="author" @input="$emit('update:author', $event.target.value)" />
</template>
<script>
export default {
props: ['title', 'author'],
emits: ['update:title', 'update:author'],
};
</script>
v-model 수식어 처리
Vue는 v-model.capitalize="value"처럼 수식어를 붙일 수 있다. 이 경우, modelModifiers prop을 통해 수식어를 확인할 수 있다.
<template>
<input :value="modelValue" @input="emitValue" />
</template>
<script>
export default {
props: {
modelValue: String,
modelModifiers: { default: () => ({}) },
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const emitValue = (e) => {
let val = e.target.value;
if (props.modelModifiers.capitalize) {
val = val.charAt(0).toUpperCase() + val.slice(1);
}
emit('update:modelValue', val);
};
return { emitValue };
},
};
</script>
실전 예시 – 자식에서 부모로 "글자 키우기" 요청하기
<!-- App.vue -->
<template>
<div :style="{ fontSize: fontSize + 'em' }">
<ArticleCard @enlarge="fontSize += 0.2" />
</div>
</template>
<script>
export default {
data() {
return { fontSize: 1 };
},
};
</script>
<!-- ArticleCard.vue -->
<template>
<article>
<p>내용</p>
<button @click="$emit('enlarge')">글자 키우기</button>
</article>
</template>
<script>
export default {
emits: ['enlarge'],
};
</script>
마무리
- 자식 → 부모로 이벤트를 보낼 때는 $emit() 사용
- Vue 3에서는 emits 옵션으로 발생 가능한 이벤트 정의
- v-model은 modelValue와 update:modelValue로 구성
- v-model:propName 형식으로 다중 바인딩 가능
- modelModifiers를 통해 수식어 처리 가능
컴포넌트를 좀 더 유연하게 설계하려면 emit과 v-model 구조를 제대로 이해해두는 것이 중요하다. 프로젝트 규모가 커질수록 이런 기본 구조가 전체 흐름을 좌우한다.
'Front-End > Vue.js' 카테고리의 다른 글
Vue 3 – Slot을 활용한 컴포넌트 콘텐츠 전달 (0) | 2025.03.30 |
---|---|
Vue 3 - Non-Prop 속성 제대로 이해하기 (0) | 2025.03.30 |
Vue 3 – Props로 컴포넌트에 데이터 전달하기 (0) | 2025.03.30 |
<Vue.js> v-directives (0) | 2024.08.19 |
<Vue.js> 컴포넌트 (0) | 2024.08.19 |