JavaScript/Vue.js

Vue Component teleport(뷰 컴포넌트 통신) - 2

devstep88 2021. 1. 7. 22:22

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

이벤트 발생과 수신

앞에서 배운 props는  상위에서 하위 컴포넌트로 데이터를 전달하는 방식이었다.

반대로 하위 컴포넌트에서 상위 컴포넌트로 데이터를 전달하는 것은?

이벤트를 발생시켜 상위 컴포넌트에 신호를 보내면 된다.

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

 

이벤트 발생과 수신 형식

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

//이벤트 발생
this.$emit('이벤트명')

//이벤트 수신
<child-component v-on:이벤트명="상위 컴포넌트의 메서드명"></child-component>

$emit를 호출하면 괄호 안에 정의된 이벤트가 발생한다. 그리고 일반적으로 $emit()을 호출하는 위치는 하위 컴포넌트의 특정 메서드 내부이다. 따라서 $emit()을 호출할 때 사용하는 this는 하위 컴포넌트를 가리킨다.

호출한 이벤트는 하위 컴포넌트를 등록하는 태그(상위 컴포넌트의 template속성에 위치)에서 v-on:으로 받는다.

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

 

...
<div id="app">	
	<child-component v-on:show-log="printText"></child-component>
</div>

<script>
	Vue.component('child-component', {
    	template: '<button v-on:click="showLog">show</button>,
        methods: {
        	showLog: function(){
            	this.$emit('show-log');
            }
        }
    });
    
    var app = new Vue({
    	el : '#app',
        data: {
        	message: 'Hello, Vue!
        },
        methods: {
        	printText: function(){
            	console.log("received an event");
            }
        }
    });
</script>

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

 

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

뷰는 상위에서 하위로만 데이터를 전달해야 하는 기본적인 통신 규칙을 따르기 때문에 바로 옆 컴포넌트에 값을 전달할려면 하위에서 공통 상위 컴포넌트로 이벤트를 전달한 후 공통 상위 컴포넌트에서 2개의 하위 컴포넌트에 props를 내려 보내야 한다. 이렇게 하는 이유는 컴포넌트 고유의 유효 범위 때문이다.

하지만 이런 통신 구조를 유지하다 보면 상위 컴포넌트가 필요 없음에도 불구하고 같은 레벨 간에 통신하기 위해 강제로 상위 컴포넌트를 두어야 한다. 이를 해결할 방법이 바로 이벤트 버스이다.

 

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

이벤트 버스(Event Bus)는 개발자가 지정한 2개의 컴포넌트 간에 데이터를 주고받을 수 있는 방법이다.

이벤트 버스를 이용하면 상위 ~ 하위 관계를 유지하고 있지 않아도 데이터를 한 컴포넌트에서 다른 컴포넌트로 전달할 수 있다.

이벤트 버스 형식

 

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

//이벤트를 보내는 컴포넌트
methods: {
	메서드명: function(){
    	eventBus.$emit('이벤트명',데이터);
    }
}

//이벤트를 받는 컴포넌트
methods: {
	created: function(){
    	eventBus.$on('이벤트명', function(데이터){
        	....
        });
    }
}

이벤트 버스를 구현하려면 애플리케이션 로직을 담는 인스턴스와는 별개로 새로운 인스턴스를 1개 더 생성하고 새 인스턴스를 이용하여 이벤트를 보내고 받는다. 보내는 컴포넌트에서는 .$emit()을, 받는 컴포넌트에서는 .$on()을 구현한다.

 

..
<div id="app">
	<child-component></child-component>
</div>
...

<script>
	var eventBus = new Vue();
    Vue.component('child-component', {
    	template: '<div>하위 컴포넌트 영역입니다.<button v-on:click="showLog">show</button></div>',
        methods: {
        	showLog: function(){
            	eventBus.$emit('triggerEventBus', 100);
            }
        }
    });
    
    var app = new Vue({
    	el: '#app',
        created: function(){
        	eventBus.$on('triggerEventBus', function(value) {
            	console.log("이벤트를 전달받음. 전달받은 값 : ", value);
            });
        }
    });
</script>

위 코드는 등록한 하위 컴포넌트의 show버튼을 클릭했을 때 이벤트 버스를 이용하여 상위 컴포넌트로 데이터를 전달하는 코드이다.

 

1. 먼저 이벤트 버스로 활용할 새 인스턴스 1개를 생성하고 eventBus라는 변수에 참조한다. 이제 eventBus변수로 새 인스턴스의 속성과 메서드에 접근할 수 있습니다.

2. 하위 컴포넌트에는 template속성과 methods속성을 정의. template속성에는 '하위 컴포넌트 영역입니다.' 텍스트와 show버튼을 추가한다. methods속성에는 showLog()메서드를 정의하고 메서드 안에는 eventBus.$emit()을 선언하여  triggerEventBus라는 이벤트를 발생하는 로직을 추가한다. 이 이벤트는 발생할 때 수신하는 쪽에 인자 값으로 100이라는 숫자를 함께 전달한다.

3. 상위 컴포넌트의 created 라이프 사이클 훅에 eventBus.$on()으로 이벤트를 받는 로직을 선언한다. 발생한 이벤트 triggerEventBus를 수신할 때 앞에서 전달된 인자 값 100이 콘솔에 출력된다.

 

이벤트 버스를 활용하면 props속성을 사용하지 않아도 원하는 컴포넌트 간에 직접적으로 데이터를 전달할 수 있어 편리하지만 컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않는 문제가 발생한다. 이 문제를 해결하기 위해서 Vuex라는 상태 관리 도구가 필요하다. Vuex는 후에 알아보기로 한다.