이번 포스트에서는 자식 컴포넌트가 부모 컴포넌트에게 이벤트를 통해 정보를 전달하는 과정을 살펴보자.
자식 컴포넌트의 값을 상위 컴포넌트로 전달할 때
emit을 통한 이벤트 발생과 처리
부모 컴포넌트가 자식에게 데이터를 전달할 때는 props를 사용하고 반대로 자식 컴포넌트가 부모 컴포넌트에게 값을 전달하고자 할 때에는 사용자 정의 이벤트를 이용한다.
여기서 주의할 점은 통상 이벤트라고 하면 처리만을 생각하는데 컴포넌트의 통신에는 이벤트의 발생과 처리 두 가지가 모두 필요하다. 먼저 자식 컴포넌트는 전달할 데이터를 파라미터로 사용자 정의 이벤트를 발생시킨다. 이것을 emit이라고 한다. 다음으로 부모는 이 이벤트를 처리해야 하는데 이제까지 처럼 v-on이 사용된다.
자식 컴포넌트에서 emit을 통한 사용자 정의 이벤트 발생
기존의 QuizItem을 수정하여 문제를 맞추는데 걸린 시간을 자식 컴포넌트에서 부모 컴포넌트로 전달해 보자. 부모는 전달받은 이벤트 수신과 함께 전달받은 시간을 관리한다.
<script setup>의 형태에서 사용자 정의 이벤트를 정의할 때는defineEmits를 사용한다. defineEmits는 defineProps와 유사하게 문자열 배열 형태로 발생시킬 수 있는 이벤트의 목록을 정의할 수 있고 리턴으로 emit 함수를 반환하는데 이를 이용해서 이벤트를 발생시킬 수 있다.
간단한 이벤트를 발생 시킬 때에는 defineEmits를 사용하지 않고 바로 <template>에서 $emit 함수를 이용해서 inline 처리할 수도 있다.
자식 컴포넌트에서 예외를 발생시켰다면 부모 컴포넌트에서 그 예외를 처리하는 방법은 기존의 이벤트 처리와 동일하다. 단지 "click"과 같은 이벤트가 사용자 정의 이벤트로 바뀐 것뿐이다. 따라서 v-on의 형태로 해당 이벤트를 처리하면 된다.
이벤트 기타
event naming
사용자 정의 이벤트의 이름을 지을 때도 props와 마찬가지로 javascript 영역과 html 영역을 고려하는 것이 좋다.
이벤트를 발생하는 자식 컴포넌트에서는 JavaScript에서 관련 처리를 하기 때문에 camelCase를 사용한다.
이벤트를 수신하는 부모 컴포넌트에서는 html영역에서 처리하기 때문에 kebab-case의 사용이 권장된다. Vue에서는 자동으로 camelCase의 이벤트 이름이 kebab case로 변경된다.
// 자식 컴포넌트: camel case
const emit = defineEmits(['solvedQuestion’])
<!-- 부모 컴포넌트: kebab case-->
<QuizItem v-for="(quiz, index) in quizs" :key="index" . . . @solved-question="summary"/>
이벤트 호출에 대한 validation
props와 마찬가지로 defineEmits에서도 배열이 아닌 객체 형태로 이벤트를 선언할 수 있는데 주요 목적은 이벤트 호출에 대한 validation 기능이다.
각각의 이벤트에 대해 이름: validation 함수의 형태로 작성할 수 있는데 validation 함수를 null로 지정하면 validation을 진행하지 않는다. validation 함수는 이벤트 호출시 전달하는 파라미터를 파라미터로 선언할 수 있으며 boolean을 반환하는데 false이면 console에 문제 상황을 출력해서 잘못됨을 알릴 수 있다.
하지만 validation 함수에서 false가 반환된다고 이벤트가 전달되지 않는 것은 아니다. validation 함수의 목적은 개발 과정에서 이벤트를 잘못 호출하고 있음을 개발자에게 알려주는 역할이며 dev 버전이 아닌 운영 버전에서는 warn 자체가 발생하지 않는다.