'통신'에 해당되는 글 1건

  1. 2019.12.30 Vue 컴포넌트 간의 통신

Introduction

Icon

1장과 3장에서 Vue의 특징중 컴포넌트 기반 프레임워크라는 내용과 인스턴스의 유효 범위에 대해서 설명하였습니다. 결국 인스턴스를 생성하여 컴포넌트로 사용하기 때문에 결국 컴포넌트가 유효 범위가 있다고 할 수 있습니다.

따라서 Vue의 컴포넌트는 유효 범위가 있기 때문에 서로 다른 컴포넌트 끼리 값을 직접적으로 참조할 수 없습니다. 그렇다면 서로 다른 컴포넌트 끼리 데이터 공유가 필요할 경우 어떻게 해야할까요?

 

이번장에서는 서로 다른 컴포넌트간의 통신에 대해서 설명하겠습니다.

 

 

1. 컴포넌트 간 통신과 유효 범위

 

컴포넌트는 각각 자체적으로 고유한 유효 범위(Scope)를 갖고있습니다. 각 컴포넌트의 유효 범위는 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수 없습니다.

컴포넌트 유효 범위 테스트

1

2

3

4

<div id="app">

    <my-component1></my-component1>

    <my-component2></my-component2>

</div>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

// 첫 번째 컴포넌트 내용

var cmp1 = {

    template: '<div>첫 번째 지역 컴포넌트 : {{ cmp1Data }}</div>',

    data: function() {

        return {

        cmp1Data : 100

        }

    }

};

// 두 번째 컴포넌트 내용

var cmp2 = {

    template: '<div>두 번째 지역 컴포넌트 : {{ cmp2Data }}</div>',

    data: function() {

        return {

        cmp2Data : cmp1.data.cmp1Data

        }

    }

};

new Vue({

    el: '#app',

    // 지역 컴포넌트 등록

    components: {

        'my-component1': cmp1,

        'my-component2': cmp2

    }

});

 

2개의 지역 컴포넌트를 등록하고, 한 컴포넌트에서 다른 컴포넌트의 값을 참조하는 코드입니다.

my-component2 컴포넌트 내용에서 {{ cmp2Data }} my-component1 컴포넌트의 data.cmp1Data를 참조하고있습니다. 일반 Javascript 를 생각해보면 해당 컴포넌트 영역에 "100"이 출력 되야 합니다.

하지만 코드를 실행해보면 데이터가 출력되지 않습니다.

 

위 처럼 출력되는 이유는 같은 레벨에 있는 컴포넌트끼리는 값을 참조할 수 없기 때문입니다

 

 

 

2. 상위 하위 컴포넌트 관계

 

위 내용처럼 Vue에서는 자체적으로 정의한 컴포넌트 데이터 전달 방법을 따라야 합니다. 가장 기본적인 방법은 상위(부모) -> 하위(자식) 컴포넌트 간의 데이터 전달 방법입니다.

3장에서 설명한 전역 컴포넌트와 지역 컴포넌트 내용처럼 인스턴스에 컴포넌트를 등록하면 인스턴스는 상위 컴포넌트, components 속성에 등록한 컴포넌트는 하위 컴포넌트가 됩니다.

상위에서 하위로는 props라는 속성을 사용하여 데이터를 전달할 수 있고, 하위에서 상위로는 $emit을 사용하여 이벤트를 발생시킬 수 있습니다.

Vue에서는 이렇게 미리 정의해 놓은 데이터 전달 방식에 따라 일관된 구조로 애플리케이션을 작성하게 됩니다. 그러므로 개발자 개개인의 스타일대로 구성되지 않고, 애플리케이션이 모두 동일한 데이터 흐름을 갖습니다.

 

 

3. 상위에서 하위 컴포넌트로 데이터 전달하기

 

상위에 있는 데이터를 하위에서 사용하기 위해서 props라는 속성을 이용한다 설명했습니다. props 속성은 하위 컴포넌트에서 사용합니다.

*props의 속성이름은 꼭 소문자로 입력해야합니다.

1

2

3

Vue.component('child-component', {

    props: ['속성이름']

});

 

하위 컴포넌트에서 등록한 props 속성을 Vue component Tag에서 v-bind라는 속성으로 상위 컴포넌트의 data 속성명을 입력합니다.

1

<child-component v-bind:속성이름="상위컴포넌트 data 속성명"></child-component>

 

앞서 보았던 같은 레벨의 컴포넌트 끼리 data 속성을 공유할 수 없었던 예제로 하위 컴포넌트로 등록후 props를 속성을 사용해보겠습니다.

1

2

3

4

<div id="app">

    <my-component1 v-bind:propsdata="parentData"></my-component1>

    <my-component2></my-component2>

</div>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

var cmp1 = {

    template: '<div>첫 번째 지역 컴포넌트 : {{ cmp1Data }}, 부모 데이터 : {{ propsdata }}</div>',

    data: function() {

        return {

        cmp1Data : 'cmp1'

        }

    },

    props: ['propsdata']

};

     

var cmp2 = {

    template: '<div>두 번째 지역 컴포넌트 : {{ cmp2Data }}, 부모 데이터 : {{ parentData }}</div>',

    data: function() {

        return {

        cmp2Data : 'cmp2'

        }

    }

};

new Vue({

    el: '#app',

    components: {

        'my-component1': cmp1,

        'my-component2': cmp2

    },

    data: {

        parentData: 'new Vue()'

    }

});

 

이렇게 상위 인스턴스의 parentData를 하위 컴포넌트에서 props 속성으로 사용할 수 있습니다.

 

 

4. 하위에서 상위 컴포넌트로 이벤트 전달하기

 

앞서 설명한 내용처럼 하위 컴포넌트는 상위 컴포넌트로 이벤트 발생하여 상위 컴포넌트의 method를 호출할 수 있습니다.

이벤트 발생과 수신은 $emit v-on 속성을 사용합니다.

1

this.$emit('이벤트명');

하위 컴포넌트 method에서 $emit을 사용하여 이벤트를 발생시킵니다.

 

1

<child-component v-on:이벤트명="상위컴포넌트 method"></child-component>

하위 컴포넌트에서 발생시킨 이벤트명으로 상위 컴포넌트 method를 실행합니다.

 

1

2

3

<div id="app">

    <child-component v-on:show-log="printText"></child-component>

</div>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Vue.component('child-component', {

    template: '<button v-on:click="showLog">show</button>',

    methods: {

        showLog: function() {

        this.$emit('show-log');

        }

    }

});

new Vue({

    el: '#app',

    data: {

        message: 'Hello Vue! passed from Parent Component'

    },

    methods: {

        printText: function() {

        console.log("message : " + this.message);

        }

    }

});

child-component에서 template에 있는 v-on속성과 click이벤트를 연결하였습니다. click 이벤트는 child-component showLog라는 method를 실행합니다.

Vue Tag 속성에는 v-on에 연결된 show-log 이벤트를 부모 인스턴스의 printText 메소드를 호출하게 됩니다.

show 버튼을 클릭하면 Vue 인스턴스에 정의된 methods 속성에 printText function 을 실행하게되고 message를 출력하게 됩니다.

 

 

5. 관계 없는 컴포넌트 간 통신 - 이벤트 버스

 

앞서 설명된 내용을 종합해보면 컴포넌트 간의 통신은 상하위 관계가 없으면 통신이 불가능 한 구조로 설명되었습니다.

하지만 서로 다른 컴포넌트 간의 통신이 불가능한 것은 아닙니다

하위 컴포넌트 D에서 A로의 통신을 하기 위해선 위 그림처럼 [상위 컴포넌트 B > 최상위 컴포넌트 > 상위 컴포넌트 A ] 와 같은 경로로 할 수는 있습니다.

하지만 특정 컴포넌트끼리의 관계가 없지만 서로 데이터를 주고받거나 서로의 기능을 호출하기 위해선 위 구조로는 필요없는 상위 컴포넌트가 필요합니다

이런 불편함을 해소할 수 있는 방법이 이벤트 버스 입니다. 위 그림처럼 특정 컴포넌트끼리 통신이 가능하게 해주는 이벤트 버스에 대해서 알아보겠습니다.

1

var eventBus = new Vue();

이벤트 버스를 위한 Vue 인스턴스를 생성해줍니다. 이 인스턴스를 이용해서 컴포넌트간 통신 매개체로쓸 수 있습니다.

 

1

2

3

4

5

methods: {

    메소드명function() {

        eventBus.$emit('이벤트명', 데이터, ...);

    }

}

하나의 컴포넌트에서 eventBus 인스턴스를 이용해 $emit을 사용합니다. 작성된 이벤트명에 대한 이벤트를 발생하고 데이터를 전달할 수도 있습니다

 

1

2

3

4

5

6

7

methods: {

    메소드명2: function() {

        eventBus.$on('이벤트명', function(value, ...){

            ...

        });

    }

}

eventBus.$on을 이용하여 이벤트명에 대한 이벤트 발생을 감지합니다. 전달된 데이터를 function에서 사용할 수 있습니다.

 

1

2

3

4

<div id="app">

    <child-component1></child-component1>

    <child-component2></child-component2>

</div>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

var eventBus = new Vue();

 

Vue.component('child-component1', {

    template: '<div>하위 컴포넌트1 영역입니다.<button v-on:click="showLog">show</button></div>',

    methods: {

        showLog: function() {

        eventBus.$emit('triggerEventBus', 100);

        }

    }

});

 

Vue.component('child-component2', {

    template: '<div>하위 컴포넌트2 영역입니다. 전달받은 값 : {{ number }}</div>',

    created: function() {

        var self = this;

        eventBus.$on('triggerEventBus', function(value){

        self.number += value;

        });

    },

    data: function() {

        return {

        number : 0

        }

    }

});

 

var app = new Vue({

    el: '#app'

});

 

 

하위 컴포넌트 1에서 하위 컴포넌트 2 created 에 작성된 function을 통해 data.str의 값을 조작할 수 있습니다.

이렇게 서로 상하관계가 아닌 컴포넌트끼리 통신을 가능하게 해주는 것을 Vue에서는 이벤트 버스라 부릅니다. 이런 방식은 프로그래밍 패러다임 Event-driven Programming이나 Event-driven Architecture에서 보았을 수 있습니다. 해당내용에 대해서는 이번장 설명 상단에 링크를 참조하시길 바랍니다.

 

 

 

Conclusions

 

Vue의 컴포넌트 간의 통신에 대해 알아보았습니다. Vue에서 지향하고있는 단방향 데이터 흐름이 적합한곳에서는 상하관계로 완전히 분리된 컴포넌트간의 통신은 이벤트 버스를 적절히 사용하면 Vue의 컴포넌트를 완벽하게 사용할 수 있겠습니다.

다음장에서는 Front-end Framework 의 발전에 원동력인 SPA를 가능하게 해주는 Vue 라우터와 HTTP 통신에 대해 설명하도록 하겠습니다.

 

 

References

Icon

https://kr.vuejs.org/v2/guide/components.html

Related Information

Icon

Event-driven Architectrue - http://www.2e.co.kr/hp/pages/share/ShareView.php?modsrl=5913&docsrl=181888&datatype=sms

Event-driven Programming - https://en.wikipedia.org/wiki/Event-driven_programming

 

출처: <http://wiki.sys4u.co.kr/pages/viewpage.action?pageId=8553499>

 

Posted by 철냄비짱
,