More than TIL/Vue

Vue prop / emit

코디번 KodeVvon 2022. 5. 12. 16:22

 

Vue의 Prop과 Emit

 

 

 

Vue가 가진 컴포넌트 통신 구조의 기본으로 prop과 emit이 있다.

다른 방법도 있긴 하지만 일단 기본 중의 기본(?)인 이 방법을 먼저 알아보자.

 

props는 상위 -> 하위로 무언가를 내려보내는 것,

emits는 하위 -> 상위로 무언가를 올려보내는 것

 

이라고 간단하게 생각할 수 있다

 

 

props로는 기본적으로 자식에게 data를 전달하고

emit으로는 특정 이벤트 발생 시 부모에게 신호를 보내는 방식이다

아래 그림을 참고하면 조금 더 이해가 쉽다 :)

 

 

 

 

 

 

Vue는 왜 굳이 자식에게는 props data만 주고, 부모한테는 events emit만 보내줄까?

그것은 Vue가 기본적으로 이전의 MVC(model–view–controller, MVC)패턴의 문제점을 개선하기 위한 MVVM패턴, 단방향 방식으로 데이터를 전달하는 패턴을 사용하기 때문이다.

 

 

MVC, MVP, MVVM

 

좌) MVC, 가운데) MVP, 우) MVVM

 

 

MVC, MVP, MVVM은 개발을 효율적으로 하고, 이후 유지보수도 손쉽게 하기 위해 설계된 설계방식, 디자인 패턴이다

이것에 대한 내용을 정리 후 props, emits를 다루기에는 그 내용이 너무 거대하다.

 


prop, emit 사용

 

어쨌든간에 단방향으로만 이뤄져야 하는 이 형태는, 이전 문제가 생겼던 방식을 개선하기 위해 마련한 일종의 규칙(?)이라고 생각하면 된다.

 

즉, props는 부모의 속성이 변경되면 자식 속성에게 전달되지만, 자식이 부모를 대상으로 전달하지는 않는다.

공식 가이드 문서에서는 이 규칙이 필요한 이유에 대해 '자식의 데이터가 부모에게 전달되는 것을 막는 것은 자식 요소가 의도치 않게 부모 요소의 상태를 변경함으로써 앱의 데이터 흐름을 이해하기 어렵게 만드는 일을 막기 위해서'라고 설명한다.

 

불편하게 느껴질 수 있지만, 그렇기 때문에 명시적인 것도 있다.

자식 -> 부모로 props라는 것은 불가능하다는 것을 분명히 하는 것이다.

따라서 props는 반드시 부모로부터 오는 것이다

 

공식 가이드에서 간단한 예제를 보여주고 설명한다

 

Vue.component('blog-post', {
  // JavaScript에서의 camelCase
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})

 

<!-- HTML에서의 kebab-case -->
<blog-post post-title="hello!"></blog-post>

 

뷰가 가진 슈퍼파워, 양방향 바인딩을 이용하면 props를 동적으로 전달할 수도 있다.

 

자식 컴포넌트가 부모에게 전달할 수 있는 것은 emit을 이용한 event다.

여기서 뷰2와 뷰3의 차이가 조금 나온다.

뷰2에서는 자식컴포넌트가 부모에게서 받는 props를 선언할 수 있었지만, 어떤 이벤트를 emit하는지는 선언할 수 없었다.

 

//Vue 2

<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text']
  }
</script>

 

이제 뷰3에서는 정의할 수 있다 :)

 

//Vue 3

<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text'],
    emits: ['accepted']
  }
</script>

 

 

이벤트를 받고 올려보내는 방식이 조금 더 깔끔해진 것이다.

 

// 부모 컴포넌트

<template> 
 <ChildComponent @click="log"></ChildComponent> 
</template>

<script>
import ChildComponent from './ChildComponent';

export default {
  components: {
    ChildComponent
  },
  methods: {
  	log(){
      console.log('Event from Child!')
    }
  }
}
</script>




//자식 컴포넌트

<template> 
 <div @click="$emit('click')">click to send Event!</div> 
</template>

<script>
export default {
  emits: [ 'click' ]
}
</script>

 

  1. 부모 컴포넌트로부터 이벤트 @click을 상속받음 // @click="log"
  2. 자식은 상속받은 'click'을 emits 옵션에 연결 // emits: [ 'click' ]
  3. 자식 템플릿 내 $emit()을 사용, 이벤트 발생 시 'click'을 부모에게 보내기 // @click="$emit('click')"
  4. 부모는 신호를 받으면 log()를 실행 // console.log('Event from Child!')

* 부모가 @click으로 전달하기 때문에 이벤트 이름을 사용해야한다고 느낄 수 있으나, 사실 원하는 대로 이름을 넣을 수 있다. @vvon="log" ->  emits: [ 'vvon' ]  ->  @click="emit('vvon')" 이 가능한 것이다.

 

 

부모에게 이벤트를 보낼 때 이벤트의 종류, 부모에게 전달할 신호도 설정할 수 있다 :)

//Vue 3 자식 컴포넌트

<template>
  <button v-on:click="$emit('click', $event)">OK</button>
</template>
<script>
export default {
  emits: [] // 선언된 event 없이
}
</script>

 

부모는 자식 컴포넌트가 보낸 이벤트를 보다 명시적으로 받고 추가 기능을 실행할 수 있다

//Vue3 부모 컴포넌트 html영역 

<my-button v-on:click="handleClick"></my-button>

 

 

 

 

 

 

 

 

 

[뷰 공식가이드, Props data 1] https://kr.vuejs.org/v2/guide/components.html#Props

[뷰 공식가이드, Props data 2] https://kr.vuejs.org/v2/guide/components-props.html

[뷰 공식가이드, Event emit 1] https://kr.vuejs.org/v2/guide/components-custom-events.html

[뷰 공식가이드, Event emit 2] https://v3.ko.vuejs.org/guide/migration/emits-option.html

 

[MVVM패턴이란?] https://docs.microsoft.com/ko-kr/windows/uwp/data-binding/data-binding-and-mvvm

[MVC, MVP, MVVM의 차이점] https://www.educba.com/mvc-vs-mvp-vs-mvvm/

'More than TIL > Vue' 카테고리의 다른 글

vue에서 파일 절대경로 설정하기  (0) 2022.05.03