[Vue.js] v-model을 활용한 컴포넌트 간 데이터 바인딩

Yoochan·2024년 3월 29일
0
post-thumbnail

vue에서 제공하는 v-model은 input, textarea, select 등의 구성요소에서 양방향 바인딩을 구현하는 데 사용될 수 있다.

v-model 속성은 v-bindv-on의 기능의 조합으로 동작한다.

  • v-bind : 뷰 인스턴스의 데이터 속성을 해당 HTML 요소에 연결할 때 사용
  • v-on : 속성은 해당 HTML 요소의 이벤트를 뷰 인스턴스의 로직과 연결할 때 사용
  • 사용자 이벤트에 의해 실행된 뷰 메서드(methods) 함수의 첫 번째 인자에는 해당 이벤트(event)가 들어온다

HTML 입력 요소의 종류에 따라 v-model 속성이 각각 다음과 같이 구성된다

  • input 태그에는 value / input
  • checkbox 태그에는 checked / change
  • select 태그에는 value / change

v-model 기본 사용방법

가장 흔하게 사용되는 Input의 value 값을 양방향으로 관리하는 예제로 살펴보자.
App.vue라는 부모 컴포넌트에서 Input 컴포넌트의 변경되는 value 값을 바인딩하고 싶은 상황이다.

// App.vue

<template>
  <Input v-model="input" />
  <div>model value: {{ input }}</div>
</template>

<script setup>
import { ref } from 'vue'
import Input from '@/components/Input.vue'

const input = ref('')
</script>

vue 3.4 이전 버전에서는 defineProps + defineEmits를 사용해서 양방향으로 데이터를 관리했다

// Input.vue
<template>
  <input :value="props.modelValue" @input="emit('update:modelValue', $event.target.value)" />
</template>

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

props의 기본 이름은 modelValue이다. v-model에 argument를 전달해주지 않고 위의 App.vue 코드처럼 v-model=”값" 으로만 보내게 되면 props를 “modelValue” 라는 이름으로만 값의 변경사항을 바인딩할 수 있다.

vue 3.4 이후에는 defineModel이라는 편의 메크로를 사용할 수 있다. 이는 위에서 소개한 defineProps + defineEmits의 기본 개념을 토대로 확장한 것이다.

// Input.vue
<template>
  <input type="text" v-model="model" />
</template>

<script setup>
const model = defineModel()
</script>

코드가 확실히 간결해진 것을 볼 수 있다.

그렇다면 defineModel만 사용할줄 알면 될까? 아니다.

form 바인딩 이외에 직접 구현한 컴포넌트 간의 데이터를 바인딩하려면 결국 defineProps + defineEmits를 사용할 줄 알아야한다.

또한 vue에서는 현재 시점에서는 IME 입력 한국어, 일본어, 중국어)에 대해서 아래와 같은 한계점이 있다.

화면에서 볼 수 있듯이, 한 글자에 대한 입력이 끝나야 텍스트값이 동기화되는 문제가 있다.

위와 같은 v-model의 한계점이 있어서, 공식 문서에서는 한국어 입력을 다룰 때는 v-bind:value와 v-on:input 을 직접 연결하여 사용하는 것을 권장하고 있다.

그래서 보통 v-bind + v-on or @input을 이용해 해결한다.

출처: https://vuejs.org/guide/essentials/forms.html#Basic-Usage

v-model arguments

v-model에 argument를 설정할 수 있다. 위에서 defineProps + defineEmits를 소개할 때, v-model에 arguments를 전달해줄 수 있다는 이야기를 살짝 언급했다.

아래의 코드처럼 원하는 변수의 이름을 설정해서 넘겨주고, 해당 변수의 이름을 받아서 사용할 수 있다.

// App.vue
<Input v-model="inputValue" />
// defineProps + defineEmits
<template>
  <input :value="props.inputValue" @input="emit('update:inputValue', $event.target.value)" />
</template>

<script setup>
const props = defineProps(['inputValue'])
const emit = defineEmits(['update:inputValue'])
</script>
// defineModel
<template>
  <input type="text" v-model="inputValue" />
</template>

<script setup>
const inputValue = defineModel('inputValue')
</script>

arguments를 정의하면 아래의 이점을 가질 수 있다.

  1. 변경되는 변수의 이름을 구별하기 쉽게 선언하고 사용할 수 있다.
  2. 다중 바인딩에 활용할 수 있다.

위의 예시는 단순하게 Input 컴포넌트 하나를 만들고 사용하기에 변수 하나로 처리가 되기 때문에 문제가 없다.
그런데 만약 성, 이름과 같이 2개 이상의 값을 v-model로 처리해야한다면, 이 arguments를 활용하여 아래와 같이 처리가 가능한 것이다.

// App.vue

<template>
  <Input v-model:first-name="first" v-model:last-name="last" />
  <div>성: {{ first }}</div>
  <div>이름: {{ last }}</div>
</template>

<script setup>
import { ref } from 'vue'
import Input from '@/components/Input.vue'

const first = ref('')
const last = ref('')
</script>
// defineModel

<template>
  <input type="text" v-model="firstName" />
  <input type="text" v-model="lastName" />
</template>

<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
// defineProps + defineEmits
<template>
  <input :value="props.firstName" @input="emit('update:firstName', $event.target.value)" />
  <input :value="props.lastName" @input="emit('update:lastName', $event.target.value)" />
</template>

<script setup>
const props = defineProps(['firstName', 'lastName'])
const emit = defineEmits(['update:firstName', 'update:lastName'])
</script>

참고 문서

https://joshua1988.github.io/web-development/vuejs/v-model-usage/#v-model%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%A0%EA%B9%8C

https://velog.io/@jay/uni-bidirectional

https://vuejs.org/guide/components/v-model.html

0개의 댓글