Geneuin
Geneuin
2 min read

Categories

컴포넌트간의 양방향바인딩

!! 이 포스트는 vue 버전 2.x 기준으로 작성되었습니다. 최신 vue 3.x 버전에서는 앞으로 소개할 v-model 기능에 많은 변화가 있으니 유의하시기 바랍니다.

컴포넌트간의 props를 “양방향 바인딩” 하는 방법 2가지.

  • v-model
  • .sync

v-model

v-model 속성은 v-bind와 v-on의 기능의 조합으로 동작합니다. 매번 사용자가 일일이 v-bind와 v-on 속성을 다 지정해 주지 않아도 좀 더 편하게 개발할 수 있게 고안된 문법입니다.

<input v-model="inputText"/>

new Vue({
  data: {
    inputText: ''
  }
})
		

위 아래 코드는 같은 역할을 하는 코드 입니다 ===

<input v-bind:value="inputText" v-on:input="updateInput"/>

new Vue({
  data: {
    inputText: ''
  },
  methods: {
    updateInput: function(event) {
      var updatedText = event.target.value;
      this.inputText = updatedText;
    }
  }
})
  • v-bind 속성은 뷰 인스턴스의 데이터 속성을 해당 HTML 요소에 연결할 때 사용한다.
  • v-on 속성은 해당 HTML 요소의 이벤트를 뷰 인스턴스의 로직과 연결할 때 사용한다.
  • 사용자 이벤트에 의해 실행된 뷰 메서드(methods) 함수의 첫 번째 인자에는 해당 이벤트(event)가 들어온다.
HTML 입력 요소의 종류에 따라 `v-model` 속성이 각각 다음과 같이 구성됩니다. 
(1) input 태그에는 `value / input` 
(2) checkbox 태그에는 `checked / change` 
(3) select 태그에는 `value / change`

기존 방법 ( v-model)

parent component

<div class="inputArea">
    <CTextarea  v-model="hostingContent" />
</div>

CTextarea.component

<template>
  <textarea :value="value" @input="inputChange"></textarea>
</template>
<script>
export default {
  name: "input",
  props: ["value"],
  data() {
    return {};
  },
  methods: {
    inputChange(event) {
      this.$emit("input", event.target.value);
    },
  },
};
</script>
  • 코드에서 알 수 있듯이 상위 컴포넌트에서 props을 기본값으로 ‘value’ 라는 명으로 받아옵니다.
  • 인풋 태그에서 값이 입력되면 인풋 태그에서 input 이벤트가 발생하고 inputChange 메서드가 실행됩니다.
  • updateInput 메서드에서 인풋 태그에 입력된 값을 상위 컴포넌트에 input 이벤트로 올려 보냅니다.

model 옵션

value 속성을 다른 용도로 사용하여야 하는 경우에는 model 옵션을 이용하여 문제가 생기는 것을 방지할 수 있습니다.

CTextarea.component

<template>
  <textarea :value="testValue" @input="inputChange"></textarea>
</template>
<script>
export default {
  name: "input",
  model: {
    prop: "testValue",
    event: "input",
  },
  props: ["testValue"],
  data() {
    return {};
  },
  methods: {
    inputChange(event) {
      this.$emit("input", event.target.value);
    },
  },
};
</script>

.sync

  • 2.3.0+ 에서 추가된 기능입니다.
  • v-model 과 .sync 의 명백한 차이점은 구문입니다. 컴포넌트가 어떻게 표시되는지 확인해봅시다.

parent

<div class="inputArea">
    <CTextarea  :hostingContent.sync="hostingContent" />
</div>

CTextarea.component

<template>
  <textarea
    :value="hostingContent"
    @input="$emit('update:hostingContent', $event.target.value)"
  ></textarea>
</template>
<script>
export default {
  name: "input",
  props: ["hostingContent"],
  data() {
    return {};
  },
  methods: {
    /*
        inputChange(event){
            this.$emit('input', event.target.value);
        }
			*/
  },
};
</script>

.sync 를 사용할때는 자식 컴포넌트는 value prop이 필요하지 않습니다. 대신 부모와 동일한 prop 이름을 사용하면 됩니다.

최종 예제 ( 팝업 )

parent

<transition name="pop">
    <ItemDeletePopShare v-if="deletePopFlag" :deletePopFlag.sync="deletePopFlag" @closeShare="requestChangeThumbNail" :getItemType="getItemType" :getItemProps="getItemProps" />
</transition>

child

<template>
  <div class="layerPop2" id="writingDelPop">
    <p class="popblackBg"></p>
    <div class="popBody">
      <div class="layCon">
        <div class="writingDelDiv">게시글 비공개를 하시겠습니까?</div>
        <a class="delete_btn" @click="$emit('closeShare')">확인</a>
        <a
          id="cancel_btn2"
          class="cancel_btn2"
          @click="$emit('update:deletePopFlag', false)"
          >취소</a
        >
      </div>
    </div>
  </div>
</template>

<script>
import myMixin from "@/mixin/mixin";

import { mapGetters } from "vuex";

export default {
  name: "ItemDeletePop",
  mixins: [myMixin],

  computed: {
    ...mapGetters(["memberInfo"]),
  },
  props: {
    getItemType: Function,
    getItemProps: Function,
    deletePopFlag: Boolean,
    requestChangeThumbNail: Function,
  },
  data() {
    return {};
  },
  methods: {},
};
</script>
  • 자식컴포넌트에게 v-if 를 통해 렌더 여부를 boolean 타입으로 설정
  • 자식컴포넌트에게 :deletePopFlag.sync=”deletePopFlag” 로 prop 전달
  • 자식컴포넌트는 전달받은 'deletePopFlag' 을 팝업을 해제하는 버튼에 양방향 컨트롤로 사용

    <a id="cancel_btn2" class="cancel_btn2" @click="$emit('update:deletePopFlag', false)">취소</a>

마치며

해당 글에서 소개하는 기능은 2.x 버전을 기준으로 작성하였습니다. 오늘날에는 vue 버전이 3.x 으로 업그레이드 되면서 많은 기능들이 추가되고 바뀌었습니다. 그 중 v-model 또한 많은 업데이트가 있었고 앞서 소개드린 .sync 기능도 삭제되었습니다. 이점 유의하시기 바랍니다.