이번 포스팅은 뷰 컴포넌트의 통신에 대하여 알아보도록 하겠습니다.
#1. 컴포넌트 간 통신과 유효 범위
앵귤러1이나 백본(backbone.js)과 같은 초창기 자바스크립트 프레임워크에서는 한 화면을 1개의 뷰로 간주했습니다. 따라서 한 화면의 데이터를 해당 화면 영역 어디서든지 호출할 수 있었죠. 하지만 뷰(Vue.js)의 경우 컴포넌트로 화면을 구성하므로 같은 웹 페이지라도 데이터를 공유할 수 없습니다. 그 이유는 컴포넌트마다 자체적으로 고유한 유효 범위(Scope)를 갖기 때문입니다. 이는 뷰 프레임워크 내부적으로 정의된 특징입니다. 따라서 각 컴포넌트의 유효 범위가 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수가 없습니다.
다음 예제를 보겠습니다.
<div id="app"> <my-component1> </my-component1> <my-component2> </my-component2> </div> <script> // 첫 번째 컴포넌트 내용 var cmp1 = { template : '<div>첫 번째 컴포넌트 : {{ cmp1Data }}</div>' data : function() { return { cmp1Data : 100 } } }; // 두 번째 컴포넌트 내용 var cmp2 = { template : '<div>두 번째 컴포넌트 : {{ cmp2Data }}</div>' data : function() { return { cmp1Data : cmp1.data.cmp1Data } } }; new Vue({ el : '#app', components : { 'my-component1' : cmp1, 'my-component2' : cmp2 } }); </script> |
이 예제는 2개의 지역 컴포넌트를 등록하고, 한 컴포넌트에서 다른 컴포넌트의 값을 직접 참조하는 예제입니다. my-component2 컴포넌트 내용에서 {{ cmp2Data }}는 my-component1컴포넌트의 data.cmp1Data를 참조하고 있습니다. 자바스크립트의 객체 참조 방식을 생각해 보면 참조값 100이 화면에 표시되어야 합니다. 하지만 {{ cmp2Data }}는 아무것도 표시하지 않습니다. 이 예제를 실행하면 다음과 같은 결과 화면이 나옵니다.
첫 번째 지역 컴포넌트 : 100 두 번째 지역 컴포넌트 : |
{{ cmp2Data }}에 아무 값도 출력되지 않은 이유는 my-component2에서 my-component1의 값을 직접 참조할 수 없기 때문입니다. 즉, 앞에서 언급했듯이 컴포넌트의 유효 범위로 인해 다른 컴포넌트의 값을 직접 접근하지 못하기 때문에 나타난 결과입니다. 이렇게 다른 컴포넌트의 값을 참조하지 못하기 때문에 생기는 특징도 있습니다. 뷰에서 미리 정의해 놓은 데이터 전달 방식에 따라 일관된 구조로 애플리케이션을 작성하게 됩니다. 그러므로 개발자 개개인의 스타일대로 구성되지 않고, 애플리케이션이 모두 동일한 데이터 흐름을 갖습니다. 이렇게 되면 다른 사람의 코드를 빠르게 파악할 수 있어 협업에도 좋습니다.
#2. 상.하위 컴포넌트 관계
앞에서 살펴본 것처럼 컴포넌트는 가각가 고유한 유효 범위를 갖고 있기 때문에 직접 다른 컴포넌트의 값을 참조할 수 없습니다. 따라서 뷰 프레임워크 자체에서 정의한 컴포넌트 데이터 전달 방법을 따라야 합니다. 가장 기본적인 데이터 전달 방법은 바로 상위(부모)-하위(자식) 컴포넌트 간의 데이터 전달 방법입니다.
상위-하위 컴포넌트란 트리 구조에서 부모 노드, 자식 노드처럼 컴포넌트 간의 관계가 부모, 자식으로 이루어진 컴포넌트를 의미합니다. 컴포넌트 등록 방법으로 지역 또는 전역 컴포넌트를 등록하면 등록된 컴포넌트는 자연스럽게 하위 컴포넌트(자식 컴포넌트)가 됩니다. 그리고 하위 컴포넌트를 등록한 인스턴스는 상위 컴포넌트(부모 컴포넌트)가 됩니다.
다음 그림은 뷰에서 상위-하위 컴포넌트 간에 데이터를 전달하는 기본적인 구조를 나타냅니다.
먼저 상위에서 하위로는 props라는 특별한 속성을 전달합니다. 그리고 하위에서 상위로는 기본적으로 이벤트만 전달할 수 있습니다. 그러면 각 전달 방법에 대해 살펴보겠습니다.
▶ 이벤트와 함께 데이터를 전달하고 싶은 경우에는 이벤트의 두 번재 인자 값으로 전달하거나 이벤트 버스(Event Bus)를 활용하는 방법이 있습니다.
#3. 상위에서 하위 컴포넌트로 데이터 전달하기
props 속성
props는 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 사용하는 속성입니다. props 속성을 사용하려면 먼저 오른쪽처럼 하위 컴포넌트의 속성에 정의합니다.
Vue.component ('child-component', { props : ['props 속성 이름'] }); |
그런 다음 상위 컴포넌트의 HTML코드에 등록된 child-component컴포넌트 태그에 v-bind 속성을 추가합니다.
<child-component v-bind:props 속성 이름="상위 컴포넌트의 data 속성"> </child-component> |
v-bind 속성의 왼쪽 값으로 하위 컴포넌트에서 정의한 props 속성을 넣고, 오른쪽 값으로 하위 컴포넌트에 전달할 상위 컴포넌트의 data 속성을 지정합니다.
그러면 props 속성을 사용해서 데이터를 전달하는 예제를 살펴보겠습니다.
이 코드는 상위 컴포넌트의 message 속성을 하위 컴포넌트에 props로 전달하여 메시지를 출력하는 예제입니다. props 속성을 이해하기 위해 코드를 작성한 순서대로 살펴보겠습니다.
1) new Vue() 인스턴스를 하나 생성합니다.
2) Vue.component()를 이용하여 하위 컴포넌트인 child-component를 등록합니다.
3) child-component의 내용에 props속성으로 propsdata를 정의합니다.
4) HTML에 컴포넌트 태그를 추가합니다. <child-component> 태그의 v-bind 속성을 보면, v-bind:prosdata="message"는 상위 컴포넌트의 message 속성 값인 Hello Vue! passed from parent Component 텍스트를 하위 컴포넌트의 prosdata로 전달하였습니다.
5) child-component의 template 속성에 정의된 <p>{{ propsdata }}</p>는 Hello Vue! passed from Parent Component가 됩니다.
위 내용을 더 간단히 정리하면 뷰 인스턴스의 data 속성에 정의된 message 속성을 하위 컴포넌트에 props로 전달하여 화면에 나타냅니다.
브라우저로 실핸한 결과 화면은 다음과 같습니다.
Hello Vue! passed from Parent Component |
여기서 한 가지 짚고 넘어가야 할 부분이 있습니다. 바로 컴포넌트 간의 관계입니다.
예제 코드에서는 child-component를 전역으로 등록한 것 이외에 딱히 상위 컴포넌트를 지정하지 않았습니다. 그럼에도 불구하고 뷰 인스턴스 안에 마치 상위 컴포넌트가 존재하는 것처럼 하위 컴포넌트로 props를 내려보냈습니다. 그 이유는 컴포넌트를 등록함과 동시에 뷰 인스턴스 자체가 상위 컴포넌트가 되기 때문입니다. 관계를 정확히 이해하기 위해 다음 그림을 살펴보겠습니다.
이렇게 인스턴스에 새로운 컴포넌트를 등록하면 기존에 있는 컴포넌트는 상위 컴포넌트(부모)가 되고, 새로 등록된 컴포넌트는 하위(자식) 컴포넌트가 됩니다. 그리고 이렇게 새 컴포넌트를 등록한 인스턴스를 최상위 컴포넌트(Root Component)라고도 부릅니다.
#4. 하위에서 상위 컴포넌트로 이벤트 전달하기
이벤트 발생과 수신
앞에서 배운 props는 상위에서 하위 컴포넌트로 데이터를 전달하는 방식입니다. 그럼 반대로 하위 컴포넌트에서 상위 컴포넌트로의 통신은 어떻게 할까요? 이벤트를 발생시켜(event emit) 상위 컴포넌트에 신호를 보내면 됩니다. 상위 컴포넌트에서 하위 컴포넌트의 특정 이벤트가 발생하기를 기다리고 있다가 하위 컴포넌트에서 특정 이벤트가 발생하면 상위 컴포넌트에서 해당 이벤트를 수신하여 상위 컴포넌트의 메서드를 호출하는 것입니다.
▶뷰 공식 사이트의 이벤트 발생 사용 방법에서는 하위에서 상위로 데이터를 전달하는 방법을 다루지 않습니다. 왜냐하면 이는 뷰의 단방향 데이터 흐름에 어긋나는 구현 방법이기 때문이죠. 하지만 향후에 복잡한 뷰 애플리케이션을 구축할 때 이벤트 버스(Event Bus)를 이용하여 데이터를 전달해야 할 경우가 있기 때문에 이벤트 인자로 데이터를 전달하는 방법에 대해서는 별도로 포스팅 하도록 하겠습니다.
이벤트 발생과 수신 형식
이벤트 발생과 수신은 $emit()과 v-on: 속성을 사용하여 구현합니다. 각각 형식은 아래와 같습니다.
// 이벤트 발생 this.$emit('이벤트명'); // 이벤트 수신 <child-component v-on:이벤트명="상위 컴포넌트의 메소드명"></child-component> |
$emit()을 호출하면 괄호 안에 정의된 이벤트가 발생합니다. 그리고 일반적으로 $emit()을 호출하는 위치는 하위 컴포넌트의 특정 메소드 내부입니다. 따라서 $emit()을 호출할 때 사용하는 this는 하위 컴포넌트를 가리킵니다.
호출한 이벤트는 하위 컴포넌트를 등록하는 태그(상위 컴포넌트의 template속성에 위치)에서 v-on:으로 받습니다. 하위 컴포넌트에서 발생한 이벤트명을 v-on: 속성에 지정하고, 속성의 값에 이벤트가 발생하였을 때 호출될 상위 컴포넌트의 메서드를 지정합니다.
Reference
1. 장기효 [예제로 이해하고 실전 프로젝트로 완성한다! Vue.js 입문]
'Development > Web' 카테고리의 다른 글
[Web] Spring Framework의 특징 (0) | 2022.09.24 |
---|---|
[Web] 크로스 도메인 (0) | 2022.09.23 |
[Web] 뷰 컴포넌트 (0) | 2022.09.18 |
[Web] 뷰 인스턴스 라이프 사이클 (0) | 2022.09.18 |
[Web] 뷰 인스턴스 (0) | 2022.09.18 |