탱구탱구 개발자 일기

Vue의 기본적인 컴포넌트 관계인 상-하위 컴포넌트간 통신은 props와 이벤트를 통해서 이뤄졌다.

하지만 동일한 level의 컴포넌트 가령 아래 그림처럼 하나의 상위 컴포넌트와 두개의 하위 컴포넌트 구조에서

두 하위 컴포넌트끼리 통신을 해야하는 상황이라면??

 

Vue.js는 기본적으로 상위 -> 하위로의 데이터 전달 구조를 갖기 때문에 이러한 경우 props를 통해 다음과 같은 과정을 거쳐야 한다.

  • 하위 A 컴포넌트에서 공통 상위 컴포넌트로 이벤트 전달
  • 공통 상위 컴포넌트에서 하위 B 컴포넌트로 props 전달

즉, 결국 상위 컴포넌트를 강제로 거쳐야하는 상황이 발생한다.

 

이러한 경우 불필요한 컴포넌트를 만들어야하는 상황이 오기 때문에 이를 해결하기 위한 방법으로 Event Bus가 있다.

 

Event Bus : 컴포넌트 계층 구조와 관계 없어 두 컴포넌트 사이에 데이터를 전달할 수 있다.

 

기본적인 Event Bus 형식으로는 $emit(), $on()이 있다.

 

  • $emit() : 이벤트와 데이터를 송신함(보내는 컴포넌트)
  • $on() : 이벤트와 데이터를 수신함(받는 컴포넌트)

Event Bus를 사용하려면 기본적으로 애플리케이션 로직을 담는 Vue 인스턴스와 별도로 새로운 Vue 인스턴스를 생성하여 import해서 사용해야한다.

 

다음과 같이 Event Bus 및 컴포넌트를 정의했다.

  • Event Bus 생성 : eventBus.js
  • 보내는 컴포넌트 : InputArea.vue
  • 받는 컴포넌트 : Home.vue

요구사항은 다음과 같다.

  • 보내는 컴포넌트에서 input에 입력 받은 데이터를 이벤트에 정의하고 전송버튼을 누르면 송신한다.
  • 받는 컴포넌트에서 이벤트를 수신받아 해당 데이터를 네이버연결버튼 속성(href)에 연결한다.

[화면]

 

입력 화면

코드 구현 과정은 다음과 같다.

 

  1. 이벤트 버스 생성
  2. 송신 컴포넌트인 InputArea에서 v-model 디렉티브를 통해 data 속성에 연결한다.
  3. eventBus.$emit을 통해 해당 data 속성을 전달하는 이벤트 및 데이터를 methods에 정의한다.
  4. 전송 버튼에 해당 method를 연결한다.
  5. 수신 컴포넌트인 Home에서 화면에 render되기 전 라이프 사이클 훅인 created 단계에서 eventbus.$on을 통해 이벤트를 수신하고 해당 data를 조작하는 callback 함수를 호출한다.

[App.vue]

<template>
  <v-app id="inspire">
    <v-main>
      <InputArea></InputArea>
      <Home v-bind:linkInfo="path"></Home>
    </v-main>
  </v-app>
</template>
<script>
import Home from "@/components/preset/Home";
import InputArea from "@/components/preset/InputArea";

export default {
  name: 'App',

  components: {
    Home,
    InputArea
  },

  data: () => ({
    
  })
};
</script>
<style scoped>

</style>

 

InputArea와 Home 컴포넌트는 App 컴포넌트를 상위로 갖는 동일한 레벨의 컴포넌트다.

 

 

[eventBus.js]

 

EventBus를 위한 Vue 인스턴스를 생성 후 export 한다.

import Vue from 'vue';

const EventBus = new Vue();

export default EventBus;

 

[InputArea.vue]

<template>
  <v-container fluid>
  <!-- v-model을 통해 input에 입력되는 value를 data 속성에 바인딩 -->
    <v-text-field label="검색바" v-model="inputText"></v-text-field>
    <v-btn v-on:click="transferText">전송</v-btn>
    <!-- 전송버튼 click 시 transferText 라는 이벤트 송신 메서드 실행 -->
  </v-container>
</template>

<script>
import EventBus from "@/eventBus";

export default {

  data () {
    return {
      inputText: ''
    }
  },
  name: "InputArea",
  methods: {
    transferText: function () {
      console.log('이벤트 송신');
      EventBus.$emit('transferText', this.inputText); //이벤트명, 데이터 정의
    }
  }
}
</script>

[Home.vue]

<template>
  <v-container
      class="fill-height"
      fluid
  >
    <v-row
        align="center"
        justify="center"
    >
      <v-col class="text-center">
       	<!-- v-bind를 통한 href 속성 변경 -->
        <v-btn :href="path">네이버연결</v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
import EventBus from "@/eventBus"; // 생성한 EventBus import

export default {
   data() {
    return {
      path: '' //최초 rendering 시 기본 값
    }
  },
  created() {
  	// eventBus를 통해 transferText라는 이벤트를 수신하고 넘어온 res라는 데이터를 data 속성인 path에 연결
    EventBus.$on('transferText', (res) => {
      console.log('이벤트 수신');
      this.path = res;
    });
  }
}
</script>

 

[결과]

 

네이버연결버튼 href 설정

 

사진에서처럼 eventBus를 통해 전달받은 데이터를 네이버연결버튼 href 속성에 잘 변경한 것을 알 수 있다.

vue-devtools를 통해서도 확인해보면 아래와 같이 Event가 잘 발생하는 것을 알 수 있다.

 

[InputArea에서 클릭시 transferText 이벤트 발생]

[Home에 data 속성 변경 확인]

지금까지 EventBus를 통한 두 컴포넌트 간 데이터 전달과정을 알아봤다.

 

Vue.js 공식 문서에 $emit, $on과 같은 이벤트 관련 인스턴스 메소드들이 정리되어 있으니 참고하면 좋을 것 같다.

 

[참고]

https://kr.vuejs.org/v2/api/index.html#%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8

 

API — Vue.js

Vue.js - 프로그레시브 자바스크립트 프레임워크

kr.vuejs.org

 

이 글을 공유합시다

facebook twitter kakaoTalk kakaostory naver band
loading