vue.js

[Vue.js] #3 뷰 컴포넌트란?

로춘남 2021. 11. 9. 22:00
728x90


컴포넌트(Component)란? 

조합하여 화면을 구성 할 수 있는 블록(화면의 특정 영역)을 의미. 컴포넌트를 활용하면 빠르게 구조화하여 일괄적인 패턴으로 개발이 가능하다. → 화면의 영역을 컴포넌트로 쪼개서 활용하다보니 나중에 관리하기도 편함. 또한 정해진 방식대로 컴포넌트를 사용하기때문에 다른 사람이 보기에도 직관적이다.

 

컴포넌트로 구분한 화면 영역 간의 관계도

이렇게보니깐 컴포넌트의 관계도는 자료구조 트리(Tree)와 비슷한거 같다.

 

※ 트리 구조란?

자료구조 중 하나로, 노드끼리의 연결이 부모 - 자식의 구조를 따름. 트리는 윈도우 파일 시스템 체계를 비롯하여 각종 데이터베이스에 활용되고 있고 뷰에서도 컴포넌트를 이해할 때 필요한 개념이다.

 

컴포넌트를 등록하는 방법

 

1) 전역(Local) 컴포넌트

특정 인스턴스에서만 유효범위를 가짐. → 특정 범위에서만 사용 가능

접근 가능한 Vue 변수를 이용하여 등록한다.

아래는 전역 컴포넌트의 등록 형식

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

컴포넌트 이름 : template 속성에서 사용할 HTML 사용자 정의 태그 이름을 의미

컴포넌트 내용 : 컴포넌트 태그가 실제 화면의 HTML 요소로 변환될 때 표시될 속성들을 작성. template, data, methods 등 인스턴스 옵션 속성을 정의 할 수 있음.

 

<body>
    <div id="app">
    <button>컴포넌트 등록</button>
    <my-component></my-component>   <!--전역 컴포넌트 표시-->
    
    <!--
    <button>컴포넌트 등록</button>
    <!-- 등록한 my-component가 최종적으로 변환된 모습 -->
    <div>전역 컴포넌트가 등록되었습니다.</div>
    -->
    
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            template: '<div>전역 컴포넌트가 등록되었습니다!</div>'  //전역 컴포넌트 등록
        });
        new Vue({
            el : '#app'
        });
    </script>
</body>

 

전역 컴포넌트가 화면에 나타나기까지의 처리 과정

1) 뷰 라이브러리 파일 로딩

2) 뷰 생성자로 컴포넌트 등록 Vue.component()

3) 인스턴스 객체 생성(옵션 속성 포함)

4) 특정 화면 요소에 인스턴스 부착

5) 인스턴스 내용 변환(등록된 컴포넌트 내용도 변환) - 이 과정에서 <my-component>가 <div>로 변환

6) 변환된 화면 요소를 사용자가 최종 확인

 

 

2) 지역(Global) 컴포넌트

여러 인스턴스에서 공통으로 사용 할 수 있음. → 뷰로 접근이 가능한 모든 범위내에서 사용 가능

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

컴포넌트 이름 : 전역 컴포넌트와 마찬가지로 HTML에 등록할 사용자 정의 태그

컴포넌트 내용 : 컴포넌트 태그가 실제 화면 요소로 변환될 때의 내용을 의미.

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

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

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

전역 컴포넌트는 인스턴스를 새로 생성 할 때마다 인스턴스에 components 속성으로 등록할 필요 없이 한 번 등록하면 어느 인스턴스에서든지 사용이 가능하지만, 지역 컴포넌트는 새 인스턴스를 생성 할 때마다 등록해줘야함.

 

뷰 컴포넌트 통신

<body>
    <div id="app">
        <my-component1></my-component1>
        <my-component2></my-component2>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script>
    <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>

이렇게 2개의 컴포넌트를 등록하면 둘다 값이 100이 나올것 같지만, 두 번째 지역 컴포넌트에서는 100이 나오지 않는다. 그 이유는 my-component2가 my-component1을 직접 참조 할 수 없기 때문. 유효 범위가 다르기 때문에 다른 컴포넌트의 값에 직접 접근하지 못하기 때문이다.

→ 뷰에서 미리 정의한 데이터 전달 방식에 따라 일관된 구조로 작성해야하기때문에 개발자 개개인의 스타일이 들어가지 않는다. 이는 곧 동일한 데이터 흐름을 갖기때문에 다른 사람의 코드를 파악하는데 수월함.

 

상-하위 컴포넌트 관계

상위-하위 컴포넌트 간 통신 방식

컴포넌트는 각각 고유한 범위를 가지고 있기 때문에 직접 다른 컴포넌트의 값을 참조 할 수가 없다. 그래서 뷰 프레임워크 자체에서 정의한 컴퓨넌트 데이터 전달 방법을 따라야함. 그중 가장 기본적인 방법이 상위(부모) - 하위(자식) 컴포넌트 간의 데이터 전달이다.

 

상위→하위 컴포넌트 데이터 전달

props: 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달 할 때 사용하는 속성. props 속성을 사용하기 위해선 하위 컴포넌트의 속성에 정의를 해야한다.

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

이후 HTML 코드에 v-bind 속성을 추가해야함

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

 

props 속성을 통해 데이터를 전달하는 예제(상위 컴포넌트의 message 속성을 하위 컴포넌트에 props로 전달하여 메시지를 출력)

<body>
    <div id="app">
        <!--Tip: 우측에서 좌측으로 속성을 읽는것이 더 수월-->
(4)        <child-component v-bind:propsdata="message"></child-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/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: 'Hello Vue! passed from Paranet Component'
          }
      });
    </script>
</body>

<작성 순서>

1) new Vue()로 인스턴스 생성

2) Vue.component()로 하위 컴포넌트인 child-component를 등록

3) child-component 내용에 props 속성으로 propsdata를 정의

4) HTML에 컴포넌트 태그 추가.v-bind:propsdata="message"는 상위 컴포넌트인 message 속성 값인 Hello Vue! passed from Parent Component 텍스트를 하위 컴포넌트 propsdata로 전달

5) child-component의 template에 정의된 <p>{{ propsdata }}</p>는 Hello Vue! passed from Parent Component가 됨.

 

뷰 인스턴스의 data 속성에 정의된 message 속성을 하위 컴포넌트에 props로 전달하여 화면에 출력!

※ 인스턴스에 새로운 컴포넌트를 등록하면 기존에 있는 컴포넌트는 컴포넌트는 상위(부모)가 되고, 새로 등록한 녀석은 하위(자식)이 된다. 새 컴포넌트를 등록한 인스턴스를 최상위 컴포넌트라고 부름(Root Component)

 

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

하위에서 상위 컴포넌트로의 통신은 이벤트를 발생시켜서 상위 컴포넌트에 신호를 보내면 된다. 상위는 하위의 특정 이벤트가 발생할때까지 대기하다가 하위에서 특정 이벤트가 발생되면 상위에서 해당 이벤트를 수신! 이후 메서드를 호출하게 되는 것!

 

//이벤트 발생
this.$emit('이벤트명');
//이벤트 수신
<child-component v-on:이벤트명="상위 컴포넌트의 메서드명"></child-component>
<body>
    <div id="app">
(3)        <child-component v-on:show-log="printText"></child-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/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');
             }
         }
     });

     var app = new Vue({
         el: '#app',
         data: {
             message: 'Hello Vue! passed from Parent Component'
         },
         methods: {
(4)             printText: function(){
                 console.log("received an event");
             }
         }
     });
    </script>
</body>

1) show 버튼을 클릭하면 이벤트 v-on:click="showLog"에 따라 showLog() 메서드가 실행

2) showLog() 메서드 안에 this.$emit('show-log')가 실행되면서 show-log 이벤트 발생

3) show-log 이벤트는 <child-component>에 정의한 v-on:show-log에 전달되고, v-on:show-log의 대상 메서드인 최상위 컴포넌트 메서드 printText()가 실행

4) printText()는 received an event라는 로그를 출력하는 메서드이므로 마지막으로 콘솔에 로그가 출력됨

 

같은 레벨간 컴포넌트 통신

뷰는 상위에서 하위로만 데이터를 전달하는 기본적인 통신 규칙이 있기 때문에 바로 옆 컴포넌트에 전달하기 위해선 하위에서 공통 상위로 이벤트를 전달 한 후 상위 컴포넌트에서 하위의 2개 컴포넌트에 props를 내려줘야한다. 기본적으로 상위 컴포넌트를 가지 2개의 하위 컴포넌트로 나타냄.

 

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

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

 

컴포넌트 관계도에서 이벤트 버스의 모습

이벤트 버스를 이용하면, 최하위 컴포넌트에서 최상위 컴포넌트를 거치지 않고도 데이터를 전달 할 수 있다.

 

이벤트 버스 형식

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

//이벤트를 보내는 컴포넌트
methods: {
	메서드명 : function(){
    	eventBus.$emit('이벤트명', 데이터);
        }
      }
      
// 이벤트를 받는 컴포넌트
methods: {
	created: function(){
     eventBust.$on('이벤트명', function(데이터) {
     ...
     });
   }
 }
<body>
    <div id="app">
        <child-component></child-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.2/dist/vue.js"></script>
    <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>
</body>

show 버튼을 클릭하여 showLog()가 실행 될때 eventBus의 이벤트가 발생. 발생된 이벤트는 상위 컴포넌트 created()에 있는 eventBus.$on에서 전달을 받게 된다. → 콘솔 로그에 100 출력!

 

단, 이벤트 버스를 활용하면 props속성을 이용하지 않고도 원하는 컴포넌트 간에 직접적으로 데이터를 전달 할 수 있어 편리하지만! 컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않는 문제가 발생 할 수 있다. → 뷰엑스(Vuex)라는 상태 관리 도구로 해결!(이건 나중에..따로..)


참고 : Do it! Vue.js 입문

728x90

'vue.js' 카테고리의 다른 글

[Vue.js] #5 뷰 템플릿(template)이란?  (0) 2021.11.11
[Vue.js] #4 뷰 라우터(Router)란?  (0) 2021.11.10
[Vue.js] #2 뷰 인스턴스란?  (0) 2021.11.08
[Vue.js] #1 Vue.js 란? 뷰제이에스  (0) 2021.11.08