우리는 한다, 개발을.

Vue3의 데이터 전달

⌛ 5 mins

Vue3 데이터 전달

Vue3의 상위 컴포넌트와 하위 컴포넌트 간 여러가지 데이터 전달 방법을 소개 하는 시간입니다.
기초적인 내용과 제가 검색하며 새로 알게 된 유용한 방법이 있어 공유 드리고자 주제를 선정하였습니다.

defineExpose()

vue3에서 defineExpose란 하위 컴포넌트에서 선언된 함수(API)를
상위 컴포넌트에서 직접적으로 호출(import)하여 사용할 수 있는 함수입니다.

하위 컴포넌트

<script setup>
import { ref, defineExpose } from 'vue'

const childNumber = ref(1);

defineExpose({ childNumber })
</script>

상위 컴포넌트

<template>
  <ChildComponent ref="$childRef"/>
  <button type="button" @click="submit">submit</button>
</template>
<script setup lang="ts">
import {ref} from "vue";

const $childRef = ref();

const submit = () => {
const {childNumber} = $childRef.value;
alert(`exposeChildNumber: ${childNumber}`);
}
</script>

defineProps() & defineEmits()

vue3에서 데이터 전달을 위해 가장 먼저 접하는 props와 emits를 입니다.

하위 컴포넌트

<template>
  <h1></h1>
  <button type="button" @click="increaseParentNumber">
    increaseParentNumber
  </button>
</template>
<script setup lang="ts">
import {defineProps, defineEmits} from 'vue';

const props = defineProps<{
parentNumber: number
}>();

const emits = defineEmits<{
(e: 'increaseParentNumber', value: number): void;
}>();

const increaseParentNumber = () => {
emits('increaseParentNumber', 1);
}
</script>

상위 컴포넌트

<template>
  <ChildComponet
      :parent-number="parentNumber"
      @increase-parent-number="increaseParentNumber"
  />
</template>
<script setup lang="ts">
import {ref} from "vue";

const parentNumber = ref(0);

const increaseParentNumber = (value: number) => {
parentNumber.value += value;
}
</script>

defineModel()

제가 새로 알게되었고 소개드리고 싶었던 방법입니다.
상위 컴포넌트에서 v-model을 통해 사용될 수 있는 양방향 바인딩 prop을 선언하는 데 사용될 수 있습니다.

하위 검포넌트

<template>
    <h1></h1>
    <h1>8</h1>
    <button type="button" @click="increaseNumber">increaseNumber</button>
    <button type="button" @click="increaseCount">increaseCount</button>
</template>
<script setup lang="ts">
import {defineModel} from "vue";

// "modelValue" prop 선언, 부모에 의해 v-model을 통해 사용됨
const number = defineModel<number>();
// "count" prop 선언, 부모에 의해 v-model:count를 통해 사용됨
const count = defineModel<number>('count', {required: true});

const increaseNumber = () => {
  if(number.value) number.value += 1;
}

const increaseCount = () => {
  count.value += 1;
}
</script>

상위 검포넌트

<template>
  <ChildComponet
      v-model="number"
      v-model:count="count"
  />
  <button type="button" @click="increaseNumber">increaseNumber</button>
  <button type="button" @click="increaseCount">increaseCount</button>
</template>
<script setup lang="ts">
import {ref} from "vue";

const number = ref(1);
const count = ref(1);

const increaseNumber = () => {
  number.value += 1;
}

const increaseCount = () => {
  count.value += 1;
}
</script>

마치며

한 화면에 작업을 맞친 후 컴포넌트로 분리할 때 조금 더 편리하게 v-model을 처리하고 싶어 찾아본 기능입니다.
상위 컴포넌트에서 v-model을 사용한 코드를 하위 컴포넌트으로 이동 후
props로 받아 v-model에 그대로 사용 시 하위 컴포넌트에서는 props를 직접 수정할 수 없기에 에러가 발생합니다.
이럴 경우에 props와 emits을 통해 해결하곤 했습니다.
defineModel을 사용하여 코드를 좀 더 간결하게 하고 상위, 하위의 데이터 전달을 편리하게 하게 할 수 있었습니다.
가끔이지만 상위에서 하위가 아닌 하위에서만 쓰이고 상위에서 읽기만 하고 싶은 데이터가 있을 경우엔
적절하게 defineExpose를 사용해보면 좋을 것 같습니다.

git repository

https://github.com/YoungminShimPMI/data_binding_test

더 많은 기능들…

https://ko.vuejs.org/api/sfc-script-setup

참고 자료