[Vue] 부모 컴포넌트와 자식 컴포넌트의 데이터 바인딩 (v-model, :value @input)

어느 개발자·2021년 6월 3일
2

Vue

목록 보기
1/1
post-thumbnail

코드의 재사용을 위해 컴포넌트를 분리하면, 부모 컴포넌트와 자식 컴포넌트의 데이터 바인딩이 필요하다.

  • 부모 컴포넌트의 값을 자식 컴포넌트에게 내려준다.
  • 자식 컴포넌트에서 값이 변경되면, 그 사실을 부모 컴포넌트에도 알려야한다.

v-model

가장 간단한 방법은 바인딩하고 싶은 데이터를 v-model로 바인딩 해주는 방법이다.

부모 컴포넌트

<!-- App.vue -->
<template>
  <div>
    <div>parent-compo {{number}}</div>
    <child-compo v-model="number" />
  </div>
</template>

<script>
import ChildCompo from "@/components/ChildCompo";

export default {
  data () {
    return {
      number: 0
    }
  },
  components: {
    ChildCompo
  }
}
</script>

부모 컴포넌트에서 v-modelnumber라는 값을 내려주면, 자식 컴포넌트에서는 props로 데이터를 접근할 수 있다.

자식 컴포넌트

<!-- ChildCompo.vue -->
<template>
  <div>
    <div>child-compo {{value}}</div>
    <button @click="value += 1">Add 1</button>
  </div>
</template>

<script>
export default {
  props: {
    value: Number
  }
}
</script>


<script>
export default {
  props: {
    value: Number
  }
}
</script>

단, v-model로 바인딩 한 값은props에서 value로만 선언해야 한다.

v-model을 사용하면 발생하는 문제

v-model을 사용하다보면 자식 컴포넌트에서 값을 바꾸어도 부모 컴포넌트에는 적용되지 않는 이슈가 발생한다.
사실 위의 코드에서도 같은 문제가 발생한다.

아무리 클릭해도 자식 컴포넌트의 값 밖에 바뀌지 않는다.

props는 readonly인데, 자식 컴포넌트에서 값을 직접 바꿔서 발생하는 오류이다.
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"

:value, @input

사실 v-model:value@input의 축약 문법이다. v-model과 완전히 같은 코드이다.

부모 컴포넌트

  • :value를 사용하여 number 변수를 바인딩해주고
  • onChangeNumber를 사용하여, 자식 컴포넌트에서 값 변경을 감지한다.
    (대신 자식 컴포넌트에서 this.$emit('input', 변경할 값)으로 값이 변경되었음을 알려주어야 한다.
<!-- App.vue -->
<template>
  <div>
    <div>parent-compo {{number}}</div>
    <child-compo :value="number" @input="onChangeNumber" />
  </div>
</template>

<script>
import ChildCompo from "@/components/ChildCompo";

export default {
  data () {
    return {
      number: 0
    }
  },
  methods: {
    onChangeNumber (val) {
      this.number = val;
    }
  },
  components: {
    ChildCompo
  }
}
</script>

자식 컴포넌트

  • 자식 컴포넌트에서는 this.$emit('input', 변경할 값)로 값이 변경되었음을 부모 컴포넌트에게 알려야 한다.
    이 때, 'input'은 부모 컴포넌트에서 @input으로 선언한 키워드이다. (@뒤에 오는 키워드)
<!-- ChildCompo.vue -->
<template>
  <div>
    <div>child-compo {{value}}</div>
    <button @click="onClickButton">Add 1</button>
  </div>
</template>

<script>
export default {
  props: {
    value: Number
  },
  methods: {
    onClickButton() {
      this.$emit('input', this.value++) // 부모에게 값이 변경되었다는 것을 알림
    }
  }
}
</script>

props는 readonly

위 코드를 동작시키면, 아까와 같은 warning이 발생한다.

다시 한 번 강조하지만, props는 readonly이다.

<!-- ChildCompo.vue -->
<template>
  <div>
    <div>child-compo {{value}}</div>
    <button @click="onClickButton">Add 1</button>
  </div>
</template>

<script>
export default {
  props: {
    value: Number
  },
  data () {
    return {
      pvalue: this.value
    }
  },
  methods: {
    onClickButton() {
      this.$emit('input', this.pvalue++)
    }
  }
}
</script>

data에서 변수를 하나 선언하여 this.value를 복사하면 된다.

오류 없이 값이 함께 증가하는 것을 확인할 수 있다.

약간의 팁

:value와 @input의 네이밍은 마음대로

사실 :value@input이 아니어도 된다. 네이밍을 마음대로 해도 된다는 점을 강조하기 위해 극단적인 예시를 들어봤다.

<child-compo :hamster="number" @lovely="onChangeNumber" />

this.$emit('lovely', this.pvalue++)

@input만 따로 써도 된다

예를 들어 자식 컴포넌트에서 어떤 버튼을 클릭했을 때, 부모 컴포넌트의 글자를 변경해주는 경우와 같이 자식 컴포넌트에서 굳이 값을 내려주지 않아도 되는 경우가 있다. 그러면 @메서드이름만 사용해도 된다.

부모 컴포넌트

<!-- App.vue -->
<template>
  <div>
    <div>{{title}}</div>
    <child-compo @updateTitle="onChangeTitle" />
  </div>
</template>

<script>
import ChildCompo from "@/components/ChildCompo";

export default {
  data () {
    return {
      title: '클릭 전'
    }
  },
  methods: {
    onChangeTitle () {
      this.title = '클릭했다'
    }
  },
  components: {
    ChildCompo
  }
}
</script>

자식 컴포넌트

<!-- ChildCompo.vue -->
<template>
  <div>
    <button @click="onClickButton">Change Title</button>
  </div>
</template>

<script>
export default {
  methods: {
    onClickButton() {
      this.$emit('updateTitle')
    }
  }
}
</script>

0개의 댓글