ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 뷰 컴포넌트 통신
    JavaScript/Vue.js 2019. 2. 27. 12:08

    뷰 컴포넌트 통신


    뷰의 경우 컴포넌트 단위로 화면을 구성하기 때문에 같은 웹 페이지라 하더라도 데이터를 서로 공유할 수가 없다는 특징이 있다.

    그 이유는 컴포넌트마다 자체적으로 유효 범위를 갖기 때문인데, 각 컴포넌트마다 유효 범위가 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수 없다.






    상 - 하위 컴포넌트 관계


    컴포넌트간 데이터 전달을 하기 위해서는 뷰 프레임워크 자체에서 정의한 컴포넌트 데이터 전달 방법을 따라야 한다.


    상위 컴포넌트 - 하위 컴포넌트를 등록한 인스턴스 - new Vue() (부모 컴포넌트)

    하위 컴포넌트 - 지역 또는 전역으로 등록한 컴포넌트 - Vue.component() or new Vue({components}) (자식 컴포넌트)


    뷰 컴포넌트 통신 방식

    상 - 하위 컴포넌트 간 통신 방식

    (Do it! Vue.js 입문 책의 저자 캡틴 판교님의 설명 참고)





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


    • props 속성

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


    props 선언 방식


    하위 컴포넌트에서 props 속성을 정의했다면 상위 컴포넌트의 HTML 코드에 등록된 child-component 태그에 v-bind 속성을 추가하여 데이터를 바인딩할 수 있다.





    실제 사용 예제


    위 코드의 작업 순서는 다음과 같다.

    1. new Vue()로 인스턴스 하나를 생성한다. - 21 라인
    2. Vue.component()를 이용해 하위 컴포넌트인 child-component를 등록한다. - 17라인
    3. child-component의 내용에 props 속성으로 myprops를 정의한다. - 18라인
    4. HTML에 컴포넌트 태그를 추가한다.  v-bind: propsdata = 'message'는 상위 컴포넌트인 message 속성 값 'Hello World!'를 하위 컴포넌트 myprops로 전달한다. - 12라인
    5. child-component의 template 속성에 정의된 <p>{{ myprops }}</p>는 'Hello World!'가 된다. - 19라인



    위의 예제에서 child-component를 전역으로 등록만 하고 상위 컴포넌트를 지정해주지 않았음에도 뷰 인스턴스 안에 상위 컴포넌트가 존재하는 것 처럼 하위 컴포넌트로 props를 전달했다.

    그 이유는 컴포넌트를 등록함과 동시에 뷰 인스턴스 자체가 상위 컴포넌트가 되기 때문이다.


    (뷰 인스턴스에 child-component를 등록한 모습)



    이렇듯 인스턴스에 새로운 컴포넌트를 등록하게 되면 기존에 있는 컴포넌트는 상위 컴포넌트가 되고, 새로 등록된 컴포넌트는 하위 컴포넌트가 된다.

    새 컴포넌트를 등록한 인스턴스를 최상위 컴포넌트(Root Component)라고 부른다.


    (Vue DevTools에선 Vue Instance를 Root로 표현한다.)




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


    • 이벤트 발생과 수신

    하위 컴포넌트에서 상위 컴포넌트로 통신을 하기 위해서는 이벤트를 발생시키면 된다.

    상위 컴포넌트에서 하위 컴포넌트의 특정 이벤트가 발생하기를 기다리고 있다가 하위 컴포넌트에서 특정 이벤트가 발생하면 상위 컴포넌트에서 해당 이벤트를 수신하여 상위 컴포넌트의 메소드를 호출한다.


    Q. 하위 컴포넌트에서 상위 컴포넌트로 데이터 전달은 불가능한가?


    A. 뷰는 (공식 사이트에서)는 하위 컴포넌트에서 상위 컴포넌트로 데이터를 전달하는 방법을 소개하지 않고 있다.

    뷰는 단방향 데이터 흐름 구조를 따르기 때문에 질문과 같은 방식은 양방향 데이터 흐름 구조이므로 이를 어기게 된다.

    만약 그러한 방법을 구현하고 싶다면 뷰의 Event Bus를 찾아보라.



    • 이벤트 발생과 수신 형식

    이벤트 발생과 수신은 "$emit()"과 "v-on:" 속성을 사용하여 구현한다.

    1
    2
    // 이벤트를 발생시킴
    this.$emit('이벤트 명');
    cs

    ($emit()을 이용한 이벤트 발생 시키기)


    $emit()을 호출하면 괄호 안에 정의된 이벤트가 발생한다.

    $emit()을 호출하는 위치는 '하위 컴포넌트'의 특정 메소드 부분이다.

    여기서 this는 하위 컴포넌트를 가리키게 된다.



    1
    2
    3
    // 하위 컴포넌트의 이벤트를 수신하기
    <child-component v-on:이벤트명 = "상위 컴포넌트의 메소드명"></child-component>
     
    cs

    (v-on: 속성을 이용한 이벤트 수신)


    호출한 이벤트는 하위 컴포넌트를 등록하는 태그에서 "v-on:"으로 받는다.

    하위 컴포넌트에서 발생한 이벤트 명을 v-on: 속성에 지정하고, 속성의 값에 이벤트가 발생했을 때 호출될 상위 컴포넌트의 메소드를 지정한다.



    (하위 컴포넌트에서 상위 컴포넌트로 이벤트를 전달하는 예제)


    이 코드는 child-component의 [show]버튼을 클릭하여 이벤트를 발생시키고, 발생한 이벤트로 상위 컴포넌트의 printText() 메소드를 실행시키는 예제이다.

    위의 코드의 처리 과정은 다음과 같다.

    1. [show]버튼을 클릭하면 클릭 이벤트 v-on:click = "showLog"에 따라 showLog()메소드가 실행된다.
    2. showLog() 메소드 안에 this.$emit('show-log')가 실행되면서 show-log 이벤트가 발생한다.
    3. show-log 이벤트는 v-on:show-log에 전달되고, v-on:show-log의 대상 메소드인 최상위 컴포넌트의 메소드 printText()가 실행된다.
    4. printText()는 로그를 출력하는 메소드이므로 콘솔에 로그가 출력된다.


    이러한 절차로 하위 컴포넌트에서 상위 컴포넌트로 이벤트를 전달하면 상위 컴포넌트의 메소드를 실행할 수 있고, 하위 컴포넌트로 내려보내는 props의 값을 사용할 수도 있다.




    같은 레벨의 컴포넌트 간 통신


    서로 같은 레벨(위치)의 컴포넌트 간 통신을 하기 위해서는 하위에서 공통 상위 컴포넌트로 이벤트를 전달하고, 그 후에 공통 상위 컴포넌트로부터 2개의 하위 컴포넌트들이 props 속성을 통해 내려 받아야 한다.

    (하위 컴포넌트 A에서 하위 컴포넌트 B로 통신을 하기 위해서 상위 컴포넌트를 통하는 구조이다.)


    이러한 방식으로 통신해야하는 이유는 컴포넌트간의 유효 범위 때문이다.

    다른 컴포넌트의 값을 직접 참조하지 못하기 때문에 공식적인 데이터 전달 방식을 활용해 같은 레벨간 통신이 가능하도록 구조를 갖춰야 한다.


    하지만 이런 구조의 단점은 상위 컴포넌트가 필요 없음에도 같은 레벨의 컴포넌트 간 통신을 위하여 반드시 상위 컴포넌트를 두어야 한다는 것이다.

    또한 상위 컴포넌트가 많을 경우 이것들을 거쳐 통신을 해야하므로 컴포넌트가 많으면 많을수록 통신을 위한 절차가 많아진다는 것이다.

    이와 같은 불편함을 해결하기 위해서 Event Bus라는 개념이 존재한다.




    관계 없는 컴포넌트 간 통신 - Event Bus


    이벤트 버스는 지정한 컴포넌트 간에 데이터를 주고받을 수 있는 방법이다.

    이벤트 버스를 이용하면 상위 - 하위 관계를 맺지 않은 컴포넌트 간에도 데이터를 전달하고 받을 수 있다.


    (왼쪽의 경우를 보자. 하위 컴포넌트 B에서 하위 컴포넌트 A까지 도달하기 위해서는 모든 컴포넌트를 거쳐야 한다.

    그러나 우측을 보면 알 수 있듯이 Event Bus를 통하면 통신하고 싶은 대상에게 다이렉트로 전달할 수 있다.)


    • 이벤트 버스 형식

    이벤트 버스 형식은 다음과 같다.

    1
    2
    // 이벤트 버스를 위한 인스턴스 생성
    var eventBus = new Vue();
    cs


    1
    2
    3
    4
    5
    6
    // 이벤트를 보내는 컴포넌트 - 하위 컴포넌트 B
    methods: {
        메소드 명 : function () {
            eventBus.$emit('이벤트 명', 데이터);
        }
    }
    cs


    1
    2
    3
    4
    5
    6
    7
    8
    // 이벤트를 받는 컴포넌트 - 하위 컴포넌트 A
    methods: {
        created : function () {
            eventBus.$on('이벤트 명'function (데이터) {
                ...
            });
        }
    }
    cs


    이벤트 버스를 구현하려면 애플리케이션 로직을 담는 인스턴스와 별개로 새로운 인스턴스 하나를 더 생성하고, 새 인스턴스를 이용해 이벤트를 보내고 받는다.

    보내는 컴포넌트에서는 $emit()을

    받는 컴포넌트에서는 $on()을 구현한다.



    위의 내용을 기반으로 예제를 만들어보자.


    (이벤트 버스가 하위 컴포넌트와 인스턴스[최상위 컴포넌트]간 통신을 연결해주는 다리가 된다.)



    이벤트 버스를 사용하면 props 속성을 이용하지 않아도 데이터를 전달할 수 있기 때문에 편리하지만 컴포넌트가 많아질 수록 데이터를 전달하는 출처를 관리하기 힘들어지게 된다.

    이러한 문제를 해결하려면 뷰엑스(Vuex)라는 상태 관리 도구를 이용하여 관리를 할 수 있다.

    지금 당장 필요하진 않으므로 이 챕터에서는 스킵한다.




    해당 내용은 'Do it! Vue.js 입문'을 참고하여 공부한 내용을 정리한 글입니다.

    'JavaScript > Vue.js' 카테고리의 다른 글

    뷰 템플릿  (0) 2019.02.28
    뷰 HTTP 통신  (0) 2019.02.28
    뷰 라우터  (0) 2019.02.27
    뷰 컴포넌트  (0) 2019.02.26
    뷰 인스턴스  (0) 2019.02.26
Designed by Tistory.