728x90
반응형

지난 글에 이어서 컴포넌트에 대하여 알아보도록 하겠습니다. 이번에는 컴포넌트 간 통신에 대하여 자세히 알아 봅니다.

 

컴포넌트

- 컴포넌트 통신하기

. 컴포넌트 간 통신과 유효범위

뷰는 컴포넌트로 화면을 구성하므로 같은 웹 페이지라도 데이터를 공유할 수 없습니다. 그 이유는 컴포넌트마다 자체적으로 고유한 유효 범위를 갖기 때문입니다. 이것은 뷰 프레임워크 내부적으로 정의된 특징이라고 합니다. 각 컴포넌트의 유효 범위가 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수가 없다는 것 입니다.

뷰 프레임워크 자체에서 정의한 컴포넌트 데이터 전달 방법을 따라야 값을 참조할 수 있습니다. 가장 기본적인 데이터 전달 방법은 바로 상위(부모) - 하위(자식) 컴포넌트 간의 데이터 전달 방법입니다.

 |==============|  -- (props 전달) →  |=============|
 | 상위 컴포넌트 |                                      | 하위 컴포넌트 |
 |==============|   (이벤트 발생) -- |=============|

상위에서 하위로는 props라는 특별한 속성을 전달합니다. 그리고 하위에서 상위로는 기본적으로 이벤트만 전달할 수 있습니다.

 

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

props는 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 사용하는 속성입니다. props 형태는 아래와 같습니다. 아래의 형태를 하위 컴포넌트 속성에 정의를 합니다.

Vue.component('child-component', {
   props : ['props 속성 이름']
});

상위 컴포넌트의 HTML 코드에 등록된 child-component 컴포넌트 태그에 v-bind 속성을 추가합니다. v-bind 속성의 왼쪽 값은 하위 컴포넌트에서 정의한 props 속성을 넣고, 오른쪽 값은 하위 컴포넌트에 전달할 상위 컴포넌트의 data 속성을 지정합니다.

<child-component v-bind:props 속성이름="상위 컴포넌트의 data 속성"></child-component>

아래는 예제 코드 입니다.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Vue component registration</title>
  </head>
  <body>
    <div id="app">
      <h3>app 인스턴스 영역</h3>
      <!-- // 4 -->
      <child-component v-bind:propsdata="message"></child-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

// 2
      Vue.component('child-component',{
        // 3
        props : ['propsdata'],
        // 5
        template : '<p>{{ propsdata }}</p>'
      });

// 1
      new Vue({
        el:'#app',
        data: {
          message : 'Test Vue! 상위 컴포넌트를 통한 통신'
        }
      });

    </script>
  </body>
</html>

아래는 실행 화면입니다.

동작 과정 (코드에서 주석에 달린 번호를 참고하십시오)

  1. new Vue() 로 인스턴스를 생성
  2. Vue.component() 를 이용하여 하위 컴포넌트 child-component를 등록
  3. child-component 내용에 props 속성으로 propsdata 를 정의
  4. HTML에 컴포넌트 태그 추가
    v-bind 속성 propsdata="message" 는 상위 컴포넌트의 message 속성 값을 하위 컴포넌트의 propsdata로 전달 함
  5. child-component 의 template 속성에 정의된 값으로 message 에 정의된 텍스트가 됨

여기서 인스턴스가 상위 컴포넌트가 되며, child-component가 하위 컴포넌트가 됩니다. 이렇게 새 컴포넌트를 등록한 인스턴스를 최상위 첨포넌트라고도 부릅니다.

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

이번에는 하위 컴포넌트에서 상위 컴포넌트로 이벤트를 발생시켜 보겠습니다. 상위 컴포넌트는 하위 컴포넌트의 특정 이벤트가 발생하기를 기다리고 있다가 하위 컴포넌트에서 특정 이벤트가 발생하면 상위 컴포넌트에서 해당 이벤트를 수신하여 상위 컴포넌트의 메서드를 호출하는 방식 입니다.

이벤트 발생과 수신은 $emit() 과 v-on: 속성을 사용하여 구현합니다. 각각의 형식은 아래와 같습니다.

// 이벤트 발생
this.$emit('이벤트명');
// 이벤트 수신
<child-component v-on:이벤트명="상위 컴포넌트의 메서드명"></child-component>

$emit()을 호출하면 괄호 안에 정의된 이벤트가 발생합니다. $emit()을 호출하는 위치는 일반적으로 하위 컴포넌트의 특정 메스트 내부입니다. 이때 this는 하위 컴포넌트를 가리킵니다.

호출한 이벤트는 하위 컴포넌트를 등록하는 태그에서 v-on:으로 받습니다. 속성의 값에 이벤트가 발생했을 때 호출될 상위 컴포넌트의 메서드를 지정합니다. 아래는 예제 코드입니다.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Vue component registration</title>
  </head>
  <body>
    <div id="app">
      <h3>app 인스턴스 영역</h3>
      <!-- // 3 -->
      <child-component v-on:show-log="printText"></child-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

      Vue.component('child-component',{
        // 1
        template : '<button v-on:click="showLog">show</button>',
        // 2
        methods: {
          showLog: function(){
            this.$emit('show-log');
          }
        }
      });

      new Vue({
        el:'#app',
        data: {
          message : 'Test Vue!'
        },
        // 4
        methods:{
          printText: function(){
            console.log("이벤트 받음");
          }
        }
      });

    </script>
  </body>
</html>

아래는 실행결과 입니다.

 

동작 과정 (코드에서 주석에 달린 번호를 참고하십시오)

  1. [show] 버튼을 클릭하면 클릭 이벤트 showLog() 메서드가 실행
  2. showLog() 메서트 안에서 this.$emit('show-log') 가 실행되어 show-log 이벤트가 발생
  3. show-log 이벤트는 <child-component> 에 정의한 v-on:show-log 에 전달되고 지정한 printText() 를 실행
  4. printText() 에서 콘솔에 로그를 출력

뷰에서는 미리 정의해 놓은 데이터 전달 방식에 따라 일관된 구조로 애플리케이션을 작성하게 됩니다. 그러므로 개발자 개개인의 스타일대로 구정되지 않고, 애플리케이션의 모두 동일한 데이터 흐름을 갖습니다. 이는 다른 사람의 코드를 빠르게 파악할 수 있어서 협업하기에 좋습니다.

 

. 이벤트 버스

상위에서 하위로 props를 전달하고 하위에서 상위로 이벤트를 전달하는 방법을 알아보았습니다. 이번에는 같은 레벨에 있는 컴포넌트 혹은 관계가 없는 컴포넌트 간 통신은 어떻게 해야할까요?  이럴 경우 이벤트 버스를 활용하면 원하는 컴포넌트에 바로 데이터를 전달할 수 있습니다. 이벤트 버스의 형식은 다음과 같습니다.

// 이벤트 버스를 위한 추가 인스턴스 1개 생성
var eventBus = new Vue();
// 이벤트를 보내는 컴포넌트
methods: {
    메서트명: function(){
        eventBus.$emit('이벤트명', 데이터);
    }
}
// 이벤트를 받는 컴포넌트
methods: {
    created : function(){
       eventBus.$on('이벤트명', function(데이터){
           ...
        });
    }
}

위의 형식을 사용하여 이벤트 버스를 구현하는 코드입니다.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Vue component registration</title>
  </head>
  <body>
    <div id="app">
      <h3>app 인스턴스 영역</h3>
      <child-component></child-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      // 1
      var eventBus = new Vue();

      Vue.component('child-component',{
        // 2
        template : '<div>하위 컴포넌트 영역 : <button v-on:click="showLog">show</button></div>',
        methods: {
          showLog: function(){
            console.log("이벤트 전송 start");
            eventBus.$emit('sendEventBus', 100);
            console.log("이벤트 전송 end");
          }
        }
      });

      var app = new Vue({
        el:'#app',
        created: function(){
          // 3
          eventBus.$on('sendEventBus', function(value){
            console.log("이벤트 전달 받음. 데이터:",value);
          });
        }
      });

    </script>
  </body>
</html>

실행 결과입니다.

 

동작 과정 (코드에서 주석에 달린 번호를 참고하십시오)

  1. 이벤트 버스로 활용할 새 인스턴스 1개를 생성
  2. 하위 컴포넌트에 template 속성과 methods 속성을 정의, showLog() 메서드 안에서 eventBus.$emit('sendEventBus', 100)를 선언하여 sendEventBus 라는 이벤트를 발생. 또한, 데이터 100이라는 숫자도 전달
  3. 상위 컴포넌트의 created 라이프 사이클 훅에 eventBus.$on() 으로 이벤트를 받는 로직 선언. 발생한 이벤트를 수신할때 데이터 100 도 넘겨받아 로그로 출력

이벤트 버스를 활용하면 props 속성을 이용하지 않고도 원하는 컴포넌트 간의 직접적으로 데이터를 전달할 수 있어 편리하지만 컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않는 문제가 발생할 수 있습니다.

 

지금까지 인스턴스와 컴포넌트에 관련하여 학습하였습니다. 가장 기본이 되는 개념이기때문에 확실히 익히고 넘어가야 할 것 입니다.

 

2021.06.22 - [Programe Note/Vue.js] - [Vue.js] 인스턴스 와 컴포넌트 개념(1)

 

[Vue.js] 인스턴스 와 컴포넌트 개념(1)

간단한 화면부터 복잡한 화면까지, 멋진 화면을 만들기 위해서는 UI를 생각해 봐야합니다. 이를 설계라고 하는데,  인스턴스와 컴포넌트가 있어야 합니다. 이 두가지에 대해서 알아보도록 하겠

tylee82.tistory.com

2021.06.22 - [Programe Note/Vue.js] - [Vue.js] 인스턴스 와 컴포넌트 개념(2)

 

[Vue.js] 인스턴스 와 컴포넌트 개념(2)

컴포넌트 컴포넌트란 조합하여 화면을 구성할 수 있는 블록을 의미합니다. 화면을 빠르게 구조화하여 일괄적인 패턴으로 개발할 수 있습니다. 화면의 영역을 컴포넌트로 쪼개서 재활용할 수 있

tylee82.tistory.com

 

728x90
반응형
728x90
반응형

컴포넌트

컴포넌트란 조합하여 화면을 구성할 수 있는 블록을 의미합니다. 화면을 빠르게 구조화하여 일괄적인 패턴으로 개발할 수 있습니다. 화면의 영역을 컴포넌트로 쪼개서 재활용할 수 있는 형태로 관리하면 나중에 코드를 다시 사용하기가 편리합니다. 참고로 컴포넌트 간의 관계는 자료구조의 트리(Tree) 모양과 유사합니다.

 

- 컴포넌트 등록하기

컴포넌트를 등록하는 방법은 전역과 지역 두 가지가 있습니다.

  • 지역(Local) 컴포넌트 : 특정 인스턴스에서만 유효한 범위를 가지고 있음, 특정 범위에서만 사용
  • 전역(Global) 컴포넌트 : 여러 인스턴스에서 공통으로 사용할 수 있음, 뷰로 접근 가능한 모든 범위에서 사용 가능

전역 컴포넌트 등록

뷰 라이브러리를 로딩하고 나면 접근 가능한 Vue 변수를 이용하여 등록합니다. Vue 생성자에 .component()를 호출하여 됩니다. 형식은 아래와 같습니다.

Vue.component('컴포넌트이름', {
    // 컴포넌트 내용
});

아래는 전역 컴포넌트를 1개 등록하고 화면에 그리는 예제입니다.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Vue component registration</title>
  </head>
  <body>
    <div id="app">
      <button>컴포넌트 등록</button>
      <my-component></my-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      Vue.component('my-component',{
        template : '<div>전역 컴포넌트가 등록되었습니다.</div>'
      });

      new Vue({
        el:'#app'
      });
    </script>
  </body>
</html>

 

아래는 실행결과 화면 입니다.

인스턴스가 생성되고, 인스턴스 내용이 화면 요소로 변환될 때 컴포넌트 태그도 함께 변합니다. 아래는 처리과정 입니다.

뷰 라이브러리 파일 로딩 → 뷰 생성자로 컴포넌트 등록 Vue.component() → 인스턴스 객체 생성 → 특정 화면 요소에 인스턴스 부착 → 인스턴스 내용 변환 (<my-component> 가 <div>로 변환됨) → 변환된 화면 요소를 사용자가 최종 확인

 

지역 컴포넌트 등록

지역 컴포넌트는 전역 컴포넌트 등록과 다르게 인스턴스에 components 속성을 추가하고 등록할 컴포넌트 이름과 내용을 정의하면 됩니다.

new Vue({
    components: {
        '컴포넌트 이름': 컴포넌트 내용
    }
})

아래는 지역 컴포넌트 등록 예제입니다.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Vue component registration</title>
  </head>
  <body>
    <div id="app">
      <button>컴포넌트 등록</button>
      <my-local-component></my-local-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      var cmp = {
        // 컴포넌트 내용
        template: '<div> 지역 컴포넌트가 등록되었습니다.</div>'
      };

      new Vue({
        el:'#app',
        components: {
          'my-local-component':cmp
        }
      });
    </script>
  </body>
</html>

 

아래는 실행 결과 입니다.

 

여기까지 지역 컴포넌트 등록과 전역 컴포넌트 등록에 대해 알아보았습니다. 그렇다면 이 둘의 차이점은 무엇일까요?

 

지역 컴포넌트와 전역 컴포넌트의 차이

차이점을 이해하기 위해서는 인스턴스의 유효 범위를 이해해야 합니다. 다시 이야기하면, 인스턴스의 유효 범위란 HTML의 특정 범위 안에서만 인스턴스의 내용이 유효한 것 입니다. 아래 코드를 보도록 하겠습니다.

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Vue component registration</title>
  </head>
  <body>
    <div id="app">
      <h3>app 인스턴스 영역</h3>
      <my-component></my-component>
      <my-local-component></my-local-component>
    </div>
    <hr/>
    <div id="app2">
      <h3>app2 인스턴스 영역</h3>
      <my-component></my-component>
      <my-local-component></my-local-component>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

      Vue.component('my-component',{
        template : '<div>전역 컴포넌트가 등록되었습니다.</div>'
      });

      var cmp = {
        // 컴포넌트 내용
        template: '<div> 지역 컴포넌트가 등록되었습니다.</div>'
      };

      new Vue({
        el:'#app',
        components: {
          'my-local-component':cmp
        }
      });

      new Vue({
        el:'#app2'
      });
    </script>
  </body>
</html>

실행 화면입니다.

에러가 발생하였습니다. 이것은 app2 영역에서 지역 컴포넌트 태그를 인식하지 못하여 생기는 오류입니다.

 

전역 컨포넌트는 인스턴스를 새로 생서할 때마다 인스턴스에 components 속성으로 등록할 필요가 없이 한 번 등록하면 어느 인스턴스에서든지 사용할 수 있습니다. 반대로 지역 컴포넌트는 새 인스턴스를 생성할 때마다 등록을 해줘야 합니다.

728x90
반응형

+ Recent posts