이전 포스팅에서 props
와 emit
에 대해 다뤘다.
이번 포스팅에서는 그 중 emit
에 대해 좀 더 자세히 다뤄보겠다.
자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달 또는 트리거의 목적으로 이벤트를 내보낼 수 있다. 이벤트는 컴포넌트의 emit 메서드를 통하여 발생시킬 수 있다.
컴포넌트의 <template>
블록 안에서 내장 함수 $emit()을 사용하여 직접 커스텀한 이벤트를 내보낼 수 있다.
예시를 살펴보자.
<!-- PostCreate.vue 자식 컴포넌트 -->
<template>
<div>
<button @click="$emit('createPost'"></button> <!-- 자식에서 부모로 이벤트 'createPost'내보내기 -->
</div>
</template>
<!-- TheView.vue 부모 컴포넌트 -->
<template>
<div>
<PostCreate @create-post="callFunction" ></PostCreate> <!-- 부모에서 이벤트 수신 -->
</div>
</template>
<script>
const callFunction = () => {
console.log('이벤트 수신');
}
</scritp>
단계는 다음과 같다.
(1) 버튼 클릭 이벤트 발생 $emit('createPost')
실행
(2) 자식 -> 부모 'createPost' 이벤트 내보냄
(3) 부모에서 @create-post로 이벤트 수신하고 callFunction 함수 실행
(4) 콘솔창에 '이벤트 수신' 출력됨.
여기서 잠깐, 이상한 점을 발견할 수 있다.
자식에서 보낸 이벤트는 'createPost'로 카멜케이스였지만, 부모에서는 'create-post' 케밥케이스로 수신했다.
이게 가능한 것일까?
가능하다. 심지어 추천되는 방법이다.
자식에서 카멜케이스 형식으로 이벤트를 발신했지만, 부모에서 케밥케이스 표기로 리스너를 사용하여 이를 수신할 수 있다.
@
는 v-on
이다. v-on
이벤트리스너는 항상 자동으로 소문자 변환을 하기때문에 'createPost'가 아닌 'createpost'로 변환된다. 이러한 변환 과정으로 인해 카멜케이스는 호출에 있어 문제를 발생시킬 수 있다.
따라서, 리스너 이벤트 이름은 케밥케이스로 작성하는 것이 좋다.
이벤트와 함께 특정 값을 내보낼 수 있다.
$emit
함수 이벤트명에 추가로 파라미터를 넘길 수 있다.
<!-- PostCreate.vue 자식 컴포넌트 -->
<template>
<div>
<button @click="$emit('createPost', '토끼는', '개발개발'"></button> <!-- 자식에서 부모로 이벤트 'createPost'내보내기 -->
</div>
</template>
<!-- TheView.vue 부모 컴포넌트 -->
<template>
<div>
<PostCreate @create-post="callFunction" ></PostCreate> <!-- 부모에서 이벤트 수신 -->
</div>
</template>
<script>
const callFunction = (word1, word2) => {
console.log(word1, word2); // 토끼는 개발개발 출력
}
</scritp>
예제2
처럼 컴포넌트 내장메서드 $emit
을 사용할 수도 있고,
setup
함수의 context.emit
을 사용하여 스크립트에서 처리할 수도 있다.
<!-- PostCreate.vue 자식 컴포넌트 -->
<template>
<div>
<button @click="createPost"></button>
</div>
</template>
<script>
export default{
setup(props, {emit}){ //context.emit 구조분해할당
const createPost = () => {
emit('createPost', '토끼는', '개발개발');
}
return { createPost};
}
}
</scritp>
vue3에서는
emits
옵션을 사용하여 이벤트를 선언할 수 있다.
이벤트 선언 방법에는 (1) 문자열 배열 선언, (2) 객체 문법 선언 두 가지 형식이 있다.
emits
에 이벤트를 문자열 배열로 선언하였다.
<script>
export default{
emits: ['createPost']
setup(props, {emit}){ //context.emit 구조분해할당
emit('createPost', '토끼는', '개발개발');
}
}
</scritp>
객체문법으로 선언할 경우 validation 로직을 추가할 수 있다. 만약 validation이 없다면 null로 설정하면 된다.
<script>
export default{
emits: {
// 유효성 검사가 없는 이벤트 선언
createPost: null,
// 유효성 검사가 있는 이벤트 선언
createPost: newTitle => {
console.log(newTitle);
if(!newTitle){
return false;
}
return true;
}
}
},
setup(props, {emit}) {
context.emit('createPost', '토끼는', '개발개발')
}
}
</scritp>
이벤트 선언 방법 두 가지에 대해 알아봤다.
자, 그럼 이제 의문점이 하나 생겼을 것이다.
선언하지 않아도 잘 돌아가는데 왜 emits
로 이벤트를 선언해야 하는지에 대한 의문 말이다.
선택사항이겠지만, 다음과 같은 이유가 있다.
① 컴포넌트가 작동하는 방식을 더 잘 문서화 하기 위해
② 또한, vue가 non-prop 속성에 알려진 리스너(Fallthrough)를 제외할 수 있다.
vue공식문서
vue3 완벽정리(기본편) - 짐코딩
이벤트 선언 부분에서, 이벤트를 선언하지 않아도 잘 돌아가는데 '왜 이벤트를 선언해야하는가?'에 대한 답으로 컴포넌트의 문서화를 답했다.
컴포넌트의 문서화, 다시 말해 읽기 편하고 유지보수가 쉬운 코드.
내가 실무에서 nexacro -> vue 전환 프로젝트에서<개발 표준>에 대해 고민하며 굉장히 중요하다고 생각했던 부분이다.
전환 프로젝트뿐만 아니라 모든 업무에 있어서도 중요하다고 생각하는 부분이다.
개발을 혼자 공부하던 취준생 시절 '가독성이 좋은 코드', '유지보수가 쉬운 코드'에 대해 대충 감으로만 느꼈었는데 이게 얼마나 중요한지 개발 업무를 하며 깨달았다.
단순히, 짧고 간결한 코드가 좋은 코드가 아니다.
타개발자가 내 코드를 봤을 때 얼마나 직관적으로 빠르고 명료하게 그 컴포넌트의 기능과 구현을 읽어낼 수 있느냐가 포인트다.
특정 기능을 수정해야 할 때 그 기능들이 어디에 어떻게 구현되어 있으며 선언된 변수들과 함수들은 어떤 역할을 하고있는지 명확하게 파악할 수 있는 것이 진짜 좋은 코드라고 생각한다.
아 이거 정말 중요하다.
나만 볼 수 있는 구조와 명명으로 나만 편하게 작성해놓으면
내 코드를 수정하러 온 동료는 매우 빡이 칠 것이다.
내 동료를 화나게 하지말자..!