[VUE3 신기능 시리즈#1] 새로운 데이터 바인딩 패턴: defineModel 활용하기

소개

Vue에서는 데이터 흐름 관리를 위한 다양한 방법을 제공하고 있습니다. 그 중에서도 propsemit을 이용한 방식이 널리 사용되어 왔습니다. 그러나 최근에 나온 Vue 3.3에서는 이러한 데이터 관리를 더욱 편리하게 해주는 새로운 기능인 defineModel이 등장했습니다. 이번 글에서는 이전에 주로 사용해온 propsemit 대신 defineModel을 활용하여 데이터 흐름을 관리하는 경험에 대해 소개하고자 합니다.

기존 데이터 흐름 관리 방식 : props와 emit

Vue에서는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 props를 사용하고, 그 반대의 경우에는 emit을 통해 이벤트를 발생시켜 부모 컴포넌트로 데이터를 전달합니다. 이러한 방식은 간단하고 직관적이지만, 데이터 흐름이 복잡해지거나 중첩된 컴포넌트 구조에서는 유지보수가 어려울 수 있습니다.

상위 컴포넌트에서는 props로 데이터를 전달하고 하위 컴포넌트에서는 emit으로 이벤트를 전달합니다.

새로운 데이터 흐름 관리 방식 : defineModel

Vue3에서는 defineModel을 활용하여 데이터 모델을 더 명확하게 정의하고 관리할 수 있습니다. 이를 통해 데이터의 구조와 유효성 검사를 강화할 수 있으며, 컴포넌트 간의 데이터 흐름을 더욱 효율적으로 관리할 수 있습니다.

defineModel을 사용하기 위한 준비

Vue 3.3에서 실험적인 기능으로 소개된 definemodel은 Vue 3.4 버전부터 안정적으로 지원되고 있습니다. Vue를 3.4 버전으로 업그레이드할 때, 아래의 종속성도 함께 업데이트해야 합니다.

  • Volar / vue-tsc@^1.8.27 (필수)
  • @vitejs/plugin-vue@^5.0.0 (Vite를 사용하는 경우)
  • nuxt@^3.9.0 (Nuxt를 사용하는 경우)
  • vue-loader@^17.4.0 (webpack 또는 vue-cli를 사용하는 경우)
3.3 버전으로 실행시 위와 같은 경고가 발생합니다.

defineModel 사용하기

defineModel 이전의 코드

<!-- InputBasic.vue -->
<script setup>
import { defineProps, defineEmits } from "vue";

const props = defineProps(["value"]);
const emit = defineEmits(["update:value"]);

function onInput(e) {
  emit("update:value", e.target.value);
}
</script>

<template>
  <div class="input-basic-comp">
    <input type="text" placeholder="이름" :value="value" @input="onInput" />
  </div>
</template>

컴포넌트의 양방향 바인딩을 지원하기 위해 propsemit을 사용하였습니다. 부모 컴포넌트로부터 데이터를 받아오기 위해 props를 사용하고, 데이터가 변경되었을 때 부모 컴포넌트에게 알리기 위해 emit을 사용하는 것을 보여줍니다.

defineModel 적용 후

<!-- InputBasic.vue -->
<script setup>
import { defineModel } from "vue";
const name = defineModel();
</script>
<template>
  <div class="input-basic-comp">
      <input type="text" placeholder="이름" v-model="name" />
  </div>
</template>

defineModel을 적용한 코드입니다.

부모 컴포넌트에서 v-model로 선언된 데이터(name)는 자식 컴포넌트에서 defineModel로 간편하게 연결할 수 있습니다. 이렇게 함으로써 자식 컴포넌트는 자동으로 해당 데이터를 props로 선언하고, 변경 사항을 부모 컴포넌트로 emit하는 ref를 반환받게 됩니다.

defineModel을 사용함으로써 명시적으로 데이터 모델을 정의할 수 있으며, 데이터의 변화를 감지하고 이에 반응하는 데 훨씬 효율적입니다. 이를 통해 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.

defineModel

defineModel arguments

// 필수로 설정
const model = defineModel({ required: true })

// 기본값 설정
const model = defineModel({ default: 0 })

// 타입 설정
const model = defineModel({ type: String })

// 로컬 변수로 설정하여 부모로부터 전달되지 않아도 사용할 수 있음.
const model = defineModel({ local: true })

// 첫 번째 매개변수에 이름, 두번째 매개변수에 설정값을 추가 
const model = defineModel('model', { required: true })

Multiple bindings

// 여러 개의 모델 정의
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')

프로젝트 적용

<!-- InputEmail.vue -->
<script setup>
import { ref, defineModel } from 'vue';
const email = defineModel('email', {
	default: '',
});
const emailChk = defineModel('emailChk', {
	default: true,
});

defineProps({
	placeholder: {
		type: String,
		default: '이메일 주소 입력',
	},
	// ...생략
});

// 이하 생략
</script>
<template>
	<div class="input-wrapper">
		<input
			type="text"
			class="input-email"
			:class="{ error: !emailChk && validChkBox }"
			:placeholder="placeholder"
			v-model="email"
		/>
	</div>
</template>

<!-- Parent.vue -->
<script setup>
import InputEmail from '@/component/InputEmail.vue'
const email = ref(''); // 이메일 값
const emailChk = ref(false); // 이메일 유효성 검사 결과
</script>

<template>
    <InputEmail
        placeholder="이메일주소"
        v-model:email="email"
        v-model:emailChk="emailChk"
    />
</template>

핀퐁 웹 페이지에서 이메일 입력창을 구현하기 위해 defineModel 을 활용했습니다.

부모 컴포넌트에서 지속적인 감시가 필요한 이메일 입력값인 email과 유효성 검사 값을 나타내는 emailChkdefineModel을 사용했습니다. 반면에 일회성으로 자식 컴포넌트에게 전달되는 placeholder는 이전의 방식대로 props로 처리하였습니다. 이렇게 함으로써 감시가 필요한 상태와 일회성 데이터 간에 명확한 구분이 이루어져 코드의 가독성이 향상되었습니다.

결론

defineModel을 활용하는 과정을 통해 데이터 흐름이 보다 명확하게 정의되고 관리되며, 코드가 더 간결해졌습니다.

새로운 기능을 활용하여 더 나은 개발 경험을 얻었으며, 앞으로도 VUE 공식 문서와 업데이트를 주시하여 최신 기능들을 적극적으로 활용할 것입니다.

참고

https://escuelavue.es/en/devtips/vue-3-modelvalue-definemodel-macrohttps://blog.vuejs.org/posts/vue-3-4#potential-actions-neededhttps://vuejs.org/api/sfc-script-setup.html#definemodelhttps://vuejs.org/guide/components/v-model.html

헥토데이터는 데이터 기반 다양한 서비스를 지원하는 기업입니다. 온라인에 분산된 데이터를 실시간으로 제공하기 위해 클라이언트 엔진과 웹 API를 활용합니다. 유수의 비대면 대출 핀테크 서비스가 선택한 헥토데이터의 CODEF API. 꼼꼼한 보안과 빠른 대응으로 기업이 자사 서비스에만 집중할 수 있도록 돕는 CODEF API에 대해 아래 배너를 눌러 확인해 보세요.

본 페이지 내의 모든 콘텐츠는 저작권법에 의해 보호받는 저작물로서, 모든 사용 권리는 ㈜헥토데이터에게 있습니다. 별도의 저작권 표시 없이 무단으로 사용하는 것을 금지하며, 자세한 저작권 정책은 해당 링크를 참고하시기 바랍니다. Copyright 2024.㈜헥토데이터 All rights reserved.