본문 바로가기
Be Smart/Vue

[Vue.js] 컴포넌트 통신 (Props, Event emit)

by 반월하 2021. 8. 9.
728x90

1. 컴포넌트 통신이란?

컴포넌트 간의 통신에는 규칙이 있습니다.

 

  1. 부모에서 자식으로 데이터를 전달하기 위해서는 props를 사용한다.
  2. 자식이 부모에게 데이터를 전달하기 위해서는 event를 발생시킨다.

이와 같은 규칙은 컴포넌트가 독립적으로 작동할 수 있도록 하기 위함이며, 데이터의 flow를 좀 더

규칙적으로 바라보기 위함입니다.

1-1. 컴포넌트 통신 규칙이 필요한 이유

Vue의 경우 컴포넌트로 화면을 구성하기 때문에 같은 웹 페이지라고 하더라도 데이터를 공유할 수 없습니다.

그 이유는 컴포넌트마다 자체적으로 고유한 유효 범위(Scope)를 가지기 때문입니다.

따라서, 직접적으로는 다른 컴포넌트의 값을 참조할 수 없기 때문에 뷰 프레임워크 자체에서 정의한 컴포넌트 데이터 전달 규칙을 따라야 합니다.

상위에서 하위로는 props 속성을 전달하여 데이터를 내려주고

하위에서 상위로는 이벤트를 발생시킵니다.

 

2. 컴포넌트 통신 코드

2-1. props란

상위컴포넌트 -> 하위 컴포넌트 데이터 전달

상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 사용하는 속성.

하위 컴포넌트 속성에 정의한다

<body>
    <div id="app">
        <!-- <app-header v-bind:프롭스 속성 이름="상위 컴포넌트 데이터 이름"></app-header> -->
        <app-header v-bind:propsdata="message"></app-header>                (3)
        <app-content v-bind:propsdata="num"></app-content>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var appHeader = {                                                   (1)
            template: '<h1>{{ propsdata }}</h1>',
            props: ['propsdata']
        };
        var appContent = {                                            
            template : '<div>{{propsdata}}</div>',
            props: ['propsdata']
        }
        new Vue({
            el: '#app',
            components: {
                'app-header': appHeader,                                    (2)
                'app-content': appContent
            },
            data: {
                message: 'hi',
                num:10
            }
        });
    </script>

(1) appHeader와 appContent라는 지역 컴포넌트로 사용될 변수를 만듭니다. 부모로부터 받을 데이터의 이름은 props라는 속성으로 정의합니다.

(2) appHeader와 appContent를 등록합니다. 이 두 컴포넌트는 부모 컴포넌트의 data 속성에는 접근할 수 없습니다.

(3) 부모의 데이터를 넘겨받을 때에는 v-bind:라는 데이터 바인딩 기법을 사용합니다.
데이터 바인딩이란?  두 변수를 유기적으로 연결하여, 값이 변경되면 즉시 반영되는 것이라 보면 됩니다.

해당 코드에서는 propsdata와 부모의 데이터인 message가 연결되는 모습입니다.

 

사용 문법은 다음과 같습니다.

<app-header v-bind:프롭스 속성 이름="상위 컴포넌트 데이터 이름"></app-header>

2-2 이벤트

하위컴포넌트 -> 상위 컴포넌트 이벤트 발생

- 이벤트 발생과 수신

하위컴포넌트에서 특정 이벤트가 발생하면(event emit) 상위 컴포넌트로 신호를 보낸다.

상위 컴포넌트에서는 하위 컴포넌트의 특정 이벤트가 발생되기를 기다리고 있다가 해당 이벤트를 수신하여

데이터를 처리한다.

 

- 이벤트 발생과 수신 형식

발생: $emit()

수신: v-on

 

<Event-emit>

<div id="app">
        <!-- <app-header v-on:" 하위컴포넌트에서 발생한 이벤트" = " 상위 컴포넌트의 메서드"></app-header> -->
        <app-header v-on:pass="logText"></app-header>                         (1)
        <app-content v-bind:num="num" v-on:add="addNum"></app-content>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var appHeader = {
            template: '<button v-on:click="passEvent">Click me </buttion>',   (2)
            methods:{
                passEvent:function(){                                          (3)
                    this.$emit('pass');
                }
        
            }
        }
        var appContent = {
            template : '<button v-on:click="addEvent">add {{num}} </button>',
            methods:{
                addEvent: function(){
                    this.$emit('add');
                }
            },
            props: ['num']

        }
        new Vue({
            el: '#app',
            components: {
                'app-header': appHeader,
                'app-content': appContent,
            },
            methods:{
                logText: function(){
                    console.log('hi');
                },
                addNum: function(){
                    this.num +=1;
                }
            },
            data:{
                num:10
            }
        });
    </script>

 

  1. 하위 컴포넌트에서 pass라는 event가 발생하면, 상위 컴포넌트에서 logText라는 메서드를 호출해라!라는 명령입니다.
  2. appHeader의 template를 보면 버튼이 하나 활성화되어 있고, click이라는 이벤트 발생 시 passEvent라는 메서드를 호출합니다.
  3. passEvent는 this.$emit('pass'); 라는 명령을 수행하는데, 이를 'pass'라는 이벤트를 발생시키는 코드입니다.

이를 이용하게 된다면 동일한 레벨에 있는 컴포넌트 간에도 통신이 가능합니다.

 

3. 같은 컴포넌트 레벨 간의 통신 방법

3-1. 이벤트 버스

위에서 아래로는 props, 아래에서 위로는 이벤트 발생을 통해 통신했다면,

같은 레벨의 컴포넌트는 통신이 되지 않으므로 부모 컴포넌트에게 이벤트를 올려주고

하위 컴포넌트에게 props로 데이터를 내려주는 방법으로 통신을 해야 합니다.

(이와 같은 통신방식을 '이벤트 버스'라고 정의한다)

 

여기서 같은 레벨의 컴포넌트가 통신이 되지 않는 이유도 컴포넌트의 고유의 유효 범위 때문입니다.

위에서 컴포넌트는 자체 scope를 가지고 있다고 했는데, 같은 이유로 컴포넌트는 다른 컴포넌트의 값을

직접 참조하지 못하므로 기본적인 통신 규칙에 따라 같은 레벨의 컴포넌트가 통신을 하기 위해서는

공통 상위 컴포넌트로 이벤트를 전달한 후 공통 상위 컴포넌트에서 하위로 props를 내려 보내는

방식으로 통신해야 한다.

3-2. 이벤트 버스의 장/단점

장점: 이벤트 버스를 활용하면 컴포넌트 간 데이터 전달이 가능하게 됨.

단점: 컴포넌트가 많아지면서 어디서 어디로 보냈는지 관리가 안 되는 문제가 발생.

<div id="app">
        {{ num }}
        <app-header v-bind:propsdata="num"></app-header>                                     (4)
        <app-content v-on:pass="deliverNum"></app-content>                                   (2)
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var appHeader = {
            template : '<h1>header {{propsdata }}</h1>',
            props : ['propsdata']                                                            
        }
        var appContent = {                                                                   (1)
            template : '<div>Content<button v-on:click="passNum">pass</button></div>',
            methods:{
                passNum:function(){
                    this.$emit('pass', 10); //발생시킬 이벤트와 넘겨줄 데이터
                }
            }
        }
        new Vue({
            el:'#app',
            components:{
                'app-header':appHeader,
                'app-content':appContent
            },
            data:{
                num:0
            },
            methods:{
                deliverNum: function(value){                                                 (3)
                    this.num = value;
                }
            }
        });
    </script>
  1. 버튼이 눌리면 passNum이라는 함수가 실행됩니다. 이 함수는 'pass'라는 이벤트를 발생시키며, 10이라는 데이터를 함께 넘겨줍니다.
  2. v-on이라는 directive에 의해 상위 컴포넌트는 deliverNum을 실행합니다.
  3. deliverNum은 앞서 넘겨준 10이라는 인자를 받아서 data 속성의 num값에 저장합니다.
  4. appHeader의 propsdata와 상위 컴포넌트의 num과 데이터 바인딩이 이루어졌으므로 propsdata에는 10이 전달됩니다.
728x90

'Be Smart > Vue' 카테고리의 다른 글

[VUE] SPA 웹 프론트앤드 개발을 위한 정리  (0) 2022.08.30
[Vue] Babel이란?  (0) 2021.08.10
[Vue] Webpack이란?  (0) 2021.08.10
[Vue.js] router (페이지 이동), axios (http 통신)  (0) 2021.08.09

댓글