-
3장. 화면을 개발하기 위한 필수 단위 - 인스턴스 & 컴포넌트 - 3개발 공부/Do it! Vue.js 입문 2025. 5. 22. 09:00반응형
3장 - 3
03-3 뷰 컴포넌트 통신
<컴포넌트 간 통신과 유효 범위>
- 앵귤러1이나 백본(Backbone.js)과 같은 초창기 자바스크립트 프레임워크에서는 한 화면을 1개의 뷰(View)로 간주했다. 따라서 한 화면의 데이터를 해당 화면 영역 어디서든 호출할 수 있었다.
- 백본: MV*(Model-View-Controller 또는 Presentational) 자바스크립트 프레임워크로, 앵귤러1이 유행하던 시기에 쌍벽을 이루던 오픈 소스 라이브러리
- 하지만 뷰(Vue.js)의 경우 컴포넌트로 화면을 구성하므로 같은 웹 페이지라도 데이터를 공유할 수 없다. 그 이유는 컴포넌트마다 자체적으로 고유한 유효 범위(Scope)를 갖기 때문이다.
- 이는 뷰 프레임워크 내부적으로 정의된 특징임으로, 각 컴포넌트의 유효 범위가 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수가 없다.
- 그에 대한 아래의 예제를 살펴보자.
<html> <head> <title>Vue Component Scope</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script> </head> <body> <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 { cmp2Data : cmp1.data.cmp1Data } } }; new Vue({ el: '#app', // 지역 컴포넌트 등록 components: { 'my-component1': cmp1, 'my-component2': cmp2 } }); </script> </body> </html>
- 이 예제는 2개의 지역 컴포넌트를 등록하고, 한 컴포넌트에서 다른 컴포넌트의 값을 직접 참조하는 예제이다.
- 이 예제를 실행하면 다음과 같은 결과 화면이 나온다.
컴포넌트 유효 범위 확인 코드 결과 화면 - {{ cmp2Data }}에 값이 출력되지 않은 이유는 my-component2에서 my-component1의 값을 직접 참조할 수 없기 때문이다. 즉, 컴포넌트의 유효 범위로 인해 다른 컴포넌트의 값을 직접 접근하지 못하기 때문에 나타난 결과이다.
- 뷰에서 미리 정의해 놓은 데이터 전달 방식에 따라 일관된 구조로 애플리케이션을 작성하게 된다. 그러므로 개발자 개개인의 스타일대로 구성되지 않고 애플리케이션이 모두 동일한 데이터 흐름을 갖는다.
- 이렇게 되면 다른 사람의 코드를 빠르게 파악할 수 있어 협업하기에도 좋다.
<상 · 하위 컴포넌트 관계>
- 뷰 프레임워크에서 컴포넌트는 직접 다른 컴포넌트의 값을 참조할 수 없기 때문에 가장 기본적인 데이터 전달 방법은 바로 상위(부모) - 하위(자식) 컴포넌트 간의 데이터 전달 방법이다.
- 상위 - 하위 컴포넌트란 트리 구조에서 부모 노드, 자식 노드처럼 컴포넌트 간의 관계가 부모, 자식으로 이루어진 컴포넌트를 의미한다.
- 지역 또는 전역 컴포넌트를 등록하면 등록된 컴포넌트는 자연스럽게 하위(자식) 컴포넌트가 되고, 하위 컴포넌트를 등록한 인스턴스는 상위(부모) 컴포넌트가 된다.
상위 - 하위 컴포넌트 간 통신 방식 - 위 그림은 뷰에서 상위 - 하위 컴포넌트 간에 데이터를 전달하는 기본적인 구조를 나타낸다.
- 상위에서 하위로는 props라는 특별한 속성을 전달하며, 하위에서 상위로는 기본적으로 이벤트만 전달할 수 있다.
- 참고로 이벤트와 함께 데이터를 전달하고 싶은 경우에는 이벤트의 두 번째 인자 값으로 전달하거나 이벤트 버스(Event Bus)를 활용하는 방법이 있다.
<상위에서 하위 컴포넌트로 데이터 전달하기>
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 속성을 사용하여 데이터를 전달하는 아래의 예제를 살펴보자.
<html> <head> <title>Vue Props Sample</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 팁 : 오른쪽에서 왼쪽으로 속성을 읽으면 더 수월하다. --> <child-component v-bind:propsdata="message"></child-component> <!-- 4 --> </div> <script> Vue.component('child-component', { // 2 props: ['propsdata'], // 3 template: '<p>{{ propsdata }}</p>' //5 }); new Vue({ // 1 el: '#app', data: { message: 'Hello Vue! passed from Parent Component' } }); </script> </body> </html>
- 위 코드는 상위 컴포넌트의 message 속성을 하위 컴포넌트에 props로 전달하여 메시지를 출력하는 예제이다.
- props 속성을 이해하기 위해 코드를 작성한 순서대로 살펴보자.
- new Vue( )로 인스턴스를 하나 생성한다.
- Vue.component( )를 이용하여 하위 컴포넌트인 child-component를 등록한다.
- child-component의 내용에 props 속성으로 propsdata를 정의한다.
- HTML에 컴포넌트 태그를 추가한다. <child-component> 태그의 v-bind 속성을 보면, v-bind:propsdata="message"는 상위 컴포넌트의 message 속성 값인 Hello Vue! passed from Parent Component 텍스트를 하위 컴포넌트의 propsdata로 전달하였다.
- child-component의 template 속성에 정의된 <p>{{ propsdata }}</p>는 Hello Vue! passed from Parent Component가 된다.
- 위 내용을 더 간단히 정리하면 뷰 인스턴스의 data 속성에 정의된 message 속성을 하위 컴포넌트 props로 전달하여 화면에 나타낸다.
- 위 코드의 실행 결과는 아래와 같다.
props 속성 전달 코드 결과 화면 - 예제 코드에서는 child-component를 전역으로 등록한 것 이외에 딱히 지정한 것이 없지만 뷰 인스턴스 안에 마치 상위컴포넌트가 존재하는 것처럼 하위 컴포넌트로 props를 내려보냈다.
- 그 이유는 컴포넌트를 등록함과 동시에 뷰 인스턴스 자체가 상위 컴포넌트가 되기 때문이다.
- 아래의 그림을 살펴보자.
뷰 인스턴스에 child-component를 등록한 모습 - 인스턴스에 새로운 컴포넌트를 등록하면 기존에 있는 컴포넌트는 상위 컴포넌트(부모)가 되고, 새로 등록된 컴포넌트는 하위(자식) 컴포넌트가 된다.
- 그리고 이렇게 새 컴포넌트를 등록한 인스턴스를 최상위 컴포넌트(Root Component)라고 부른다.
<하위에서 상위 컴포넌트로 이벤트 전달하기>
이벤트 발생과 수신
- 하위 컴포넌트에서 상위 컴포넌트로의 통신은 이벤트를 발생시켜(event emit) 상위 컴포넌트에 신호를 보낸다.
이벤트 발생과 수신 형식
- 이벤트 발생과 수신은 $emit( )과 v-on: 속성을 사용하여 구현한다. 각각의 형식은 아래와 같다.
// 이벤트 발생 this.$emit('이벤트명');
<!-- 이벤트 수신 --> <child-component v-on:이벤트명="상위 컴포넌트의 메서드명"></child-component>
- $emit( )을 호출하면 괄호 안에 정의된 이벤트가 발생한다. 그리고 일반적으로 $emit( )을 호출하는 위치는 하위 컴포넌트의 특정 메서드 내부이다. 따라서 $emit( )을 호출할 때 사용하는 this는 하위 컴포넌트를 가리킨다.
- 호출한 이벤트는 하위 컴포넌트를 등록하는 태그(상위 컴포넌트의 template 속성에 위치)에서 v-on:으로 받는다. 하위 컴포넌트에서 발생한 이벤트 명을 v-on: 속성에 지정하고, 속성의 값에 이벤트가 발생했을 때 호출될 상위 컴포넌트의 메서드를 지정한다.
- 아래의 예제를 살펴보자.
<!DOCTYPE html> <html> <head> <title>Vue Event Emit Sample</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script> </head> <body> <div id="app"> <child-component v-on:show-log="printText"></child-component> <!-- 3 --> </div> <script> Vue.component('child-component', { template: '<button v-on:click="showLog">show</button>', // 1 methods: { // 2 showLog: function() { this.$emit('show-log'); } } }); var app = new Vue({ el: '#app', data: { message: 'Hello Vue! passed form Parent Component' }, methods: { printText: function() { // 4 console.log("received an event"); } } }); </script> </body> </html>
- 위 코드는 child-component의 [show] 버튼을 클릭하여 이벤트를 발생시키고, 발생한 이벤트로 상위 컴포넌트(여기서 루트 컴포넌트)의 printText( ) 메서드를 실행시키는 예제이다.
- [show] 버튼을 클릭했을 때 처리되는 과정은 다음과 같다.
- [show] 버튼을 클릭하면 클릭 이벤트 v-on:click="showLog"에 따라 showLog( ) 메서드가 실행된다.
- showLog( ) 메서드 안에 this.$emit('show-log')가 실행되면서 show-log 이벤트가 발생한다.
- show-log 이벤트는 <child-component>에 정의한 v-on:show-log에 전달되고, v-on:show-log의 대상 메서드인 최상위 컴포넌트의 메서드 printText( )가 실행된다.
- printText( )는 received an event라는 로그를 출력하는 메서드이므로 마지막으로 콘솔에 로그가 출력된다.
- [show] 버튼을 클릭한 실행 결과 화면은 아래와 같다.
이벤트 발생 예제 실행 결과 화면 - 이와 같은 방식으로 하위 컴포넌트에서 상위 컴포넌트로 신호를 올려보내면 상위 컴포넌트의 메서드를 실행할 수 있다.
<같은 레벨의 컴포넌트 간 통신>
- 상위 - 하위 관계가 아니라 같은 레벨에 있는 컴포넌트끼리 어떻게 통신하는지 알아보자.
같은 레벨 간의 컴포넌트 통신 흐름 - 위 그림은 같은 상위 컴포넌트를 가지는 2개의 하위 컴포넌트를 나타낸다.
- 뷰는 상위에서 하위로만 데이터를 전달해야 하는 기본적인 통신 규칙을 따르기 때문에 바로 옆 컴포넌트에 값을 전달하려면 하위에서 공통 상위 컴포넌트로 이벤트를 전달한 후 공통 상위 컴포넌트에서 2개의 하위 컴포넌트에 props를 내려보내야 한다.
- 이러한 이유는 컴포넌트 고유의 유효 범위 때문이다.
- 하지만 이런 통신 구조를 유지하다 보면 상위 컴포넌트가 필요 없음에도 불구하고 같은 레벨 간에 통신을 하기 위해 강제로 상위 컴포넌트를 두어야 한다.
- 그래서 이를 해결할 수 있는 방법이 바로 이벤트 버스이다.
반응형<관계 없는 컴포넌트 간 통신 - 이벤트 버스>
- 이벤트 버스(Event Bus)는 개발자가 지정한 2개의 컴포넌트 간에 데이터를 주고받을 수 있는 방법이다.
컴포넌트 관계도에서 이벤트 버스의 모습 - 위 그림에서 만약 하위 컴포넌트 B에서 상위 컴포넌트 A로 데이터를 전달하려면 왼쪽 그림처럼 여러 컴포넌트를 거쳐 가야한다. 하지만 웹 앱이 커져 컴포넌트가 많아지면 이런 방식은 매우 번거롭다.
- 이때 이벤트 버스를 활용하면 오른쪽 그림처럼 중간 컴포넌트를 거치지 않고 바로 데이터를 전달할 수 있다.
이벤트 버스 형식
- 이벤트 버스 형식은 아래와 같다.
// 이벤트 버스를 위한 추가 인스턴스 1개 생성 var eventBus = new Vue();
- 이벤트 버스를 구현하려면 애플리케이션 로직을 담는 인스턴스와는 별개로 새로운 인스턴스를 1개 더 생성하고, 새인스턴스를 이용하여 이벤트를 보내고 받는다.
- 보내는 컴포넌트에서는 .$emit( )을, 받는 컴포넌트에서는 .$on( )을 구현한다.
// 이벤트를 보내는 컴포넌트 methods: { 메서드명: function() { eventBus.$emit('이벤트명', 데이터); } }
// 이벤트를 받는 컴포넌트 methods: { created: function() { eventBus.$on('이벤트명', function(데이터) { ... }); } }
- 이 형식을 사용하여 이벤트 버스를 구현해보자.
<!DOCTYPE html> <html> <head> <title>Vue Event Bus1 Sample</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script> </head> <body> <div id="app"> <child-component></child-component> </div> <script> var eventBus = new Vue(); // 1 Vue.component('child-component', { // 2 template: '<div>하위 컴포넌트 영역입니다.<button v-on:click="showLog">show</button></div>', methods: { showLog: function() { eventBus.$emit('truggerEventBus', 100); } } }); var app = new Vue({ el: '#app', created: function() { eventBus.$on('triggerEventBus', function(value) { // 3 console.log("이벤트를 전달받음. 전달받은 값 : ", value); }); } }); </script> </body> </html>
- 위 코드는 등록한 하위 컴포넌트의 [show] 버튼을 클릭했을 때 이벤트 버스를 이용하여 상위 컴포넌트로 데이터를 전달하는 코드이다.
- 먼저 이벤트 버스로 활용할 새 인스턴스를 1개 생성하고 eventBus라는 변수에 참조한다. 이제 eventBus 변수로 새 인스턴스의 속성과 메서드에 접근할 수 있다.
- 하위 컴포넌트에는 template 속성과 methods 속성을 정의한다. template 속성에는 '하위 컴포넌트 영역입니다.'라는 텍스트와 [show] 버튼을 추가한다. methods 속성에는 showLog( ) 메서드를 정의하고, 메서드 안에는 eventBus.$emit( )을 선언하여 triggerEventBus라는 이벤트를 발생하는 로직을 추가한다. 이 이벤트는 발생할 때 수신하는 쪽에 인자 값으로 100이라는 숫자를 함께 전달한다.
- 상위 컴포넌트의 created 라이프 사이클 훅에 eventBus.$on( )으로 이벤트를 받는 로직을 선언한다. 발생한 이벤트 triggerEventBus를 수신할 때 앞에서 전달된 인자 값 100이 콘솔에 출력된다.
- 간단히 정리하면 [show] 버튼을 클릭하여 showLog( )가 실행되었을 때 eventBus의 이벤트가 발생한다. 그리고 발생한 이벤트는 상위 컴포넌트의 created( )에 있는 eventBus.$on( )에서 전달받는다. 이벤트와 함께 전달된 인자 값 100이 콘솔 로그에 함께 출력된다.
- [show] 버튼을 클릭하면 아래와 같은 결과 화면이 나온다.
이벤트 버스 예제 실행 결과 화면 - 이벤트 버스를 활용하면 props 속성을 이용하지 않고도 원하는 컴포넌트 간에 직접적으로 데이터를 전달할 수 있어 편리하지만 컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않는 문제가 발생한다.
- 이 문제를 해결하려면 뷰엑스(Vuex)라는 상태 관리 도구가 필요하다.
- 뷰엑스는 중, 대형 애플리케이션에서 컴포넌트 간의 데이터 관리를 효율적으로 하는 라이브러리이다. 구글에서 'vuex 시작하기', 'vuex 튜토리얼'로 검색해보아라.
해당 글은 [Do it! Vue.js 입문] 책을 토대로 공부한 내용을 기록하기 위하여 작성됨.
반응형'개발 공부 > Do it! Vue.js 입문' 카테고리의 다른 글
4장. 상용 웹 앱을 개발하기 위한 필수 기술들 - 라우터 & HTTP 통신 - 2 (2) 2025.05.24 4장. 상용 웹 앱을 개발하기 위한 필수 기술들 - 라우터 & HTTP 통신 - 1 (1) 2025.05.23 3장. 화면을 개발하기 위한 필수 단위 - 인스턴스 & 컴포넌트 - 2 (1) 2025.05.21 3장. 화면을 개발하기 위한 필수 단위 - 인스턴스 & 컴포넌트 - 1 (0) 2025.05.20 2장. 개발 환경 설정 및 첫 번째 프로젝트 (1) 2025.05.19 - 앵귤러1이나 백본(Backbone.js)과 같은 초창기 자바스크립트 프레임워크에서는 한 화면을 1개의 뷰(View)로 간주했다. 따라서 한 화면의 데이터를 해당 화면 영역 어디서든 호출할 수 있었다.