티스토리 뷰

페이지를 개발할 때 여러 종류의 input태그가 필요할 수 있다. 예를 들어 이름이나 주소같은 단순한 텍스트를 입력하는 <input type="text>, 전화번호를 입력받으면서 3-4-4자리로 끊어서 자동으로 '-'를 붙여주는 <input type="tel">, 숫자를 입력받는 <input type="number">등이 있을 수 있다. 그리고 이러한 기능을 가진 input 태그들 안에 여러가지 클래스나 함수가 포함되어 있는 경우가 대부분이다. 프로젝트에서 이러한 input 태그들이 여러 군데에서 사용된다면 매번 코딩하는 것보다 차라리 컴포넌트로 분리하는게 나을 수도 있다. 즉 input태그를 커스텀 컴포넌트화 시킴으로써, 각 input태그별로 특정 기능을 특화시킬 수 있다. 그렇다면 input태그를 커스텀 컴포넌트화 시키려면 어떻게 해야하는지 살펴보자.
우선 vue에는 input태그에 v-model이라는 양방향 데이터 바인딩이라는 강력한 기능을 제공한다. 하지만 컴포넌트화 시켜서 부모-자식간의 관계가 형성되면 부모 컴포넌트에서 자식 컴포넌트로 v-model를 별도로 연결시켜주어야만 양방향 데이터 바인딩 기능을 유지할 수 있다.   
방법은 어렵지 않다. 사실 부모 컴포넌트 입장에서는 컴포넌트화하기 이전 코드와 동일하게 v-model="data"를 걸어주기만 하면 된다. 이렇게만 하면 자식 컴포넌트의 props에서 자동으로 modelValue라는 이름으로 받을 수 있게 된다.
별도로 처리가 필요한건 자식 컴포넌트이다. 자식 컴포넌트에서도 v-model="modelValue"로 처리할 수 있으면 좋으련만, 아쉽게도 불가능하다. 왜냐하면 자식 컴포넌트에서 props의 값은 직접 변형이 불가능하기 때문이다. v-model은 input태그의 value@input이벤트를 동시에 처리해주는 디렉티브인데, @input="modelValue = $event.target.value"props의 값을 직접 변형하는 코드이기 때문이다.   

이를 해결하기 위해서는 자식 컴포넌트 입장에서는 부모 컴포넌트에게 emit()으로 '값이 $event.target.value'로 변했다는 정보만 전달하고, 실질적인 값의 변형은 부모 컴포넌트에서 진행되야 한다.

// MainPage.vue (parent)
<template>
  <div>
    <InputNumber v-model="number" />
    <p>number : {{ number }}</p>
  </div>
  <div>
    <InputText v-model="text" />
    <p>text:{{ text }}</p>
  </div>
</template>

<script>
import InputNumber from "@/components/InputNumber.vue";
import InputText from "@/components/InputText.vue";
import { ref } from "vue";

export default {
  components: { InputNumber, InputText },
  setup() {
    let number = ref(0);
    let text = ref("");

    return {
      number,
      text,
    };
  },
};
</script>
// InputNumber.vue (child)
<template>
  <input
    type="number"
    placeholder="number"
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>
<script>
export default {
  props: {
    // 부모 컴포넌트에서 내려준 v-model 값
    modelValue: Number,
  },
};
</script>
// InputText.vue (child)
<template>
  <input
    type="text"
    placeholder="text"
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>
<script>
export default {
  props: {
    // 부모 컴포넌트에서 내려준 v-model 값
    modelValue: String,
  },
};
</script>
댓글