Front-End(Web)/Vue

[Vue.js] 컴포넌트(Component), Props & Event

ttaeng_99 2021. 5. 26. 21:04
반응형

이번에는 비교적 친숙한! 뷰의 컴포넌트 개념에 대해 알아보고자 한다. React와 마찬가지로 화면을 구성하는 요소이다.

현대의 모던 프레임워크들은 대부분 컴포넌트 개념을 차용하고 있기 때문에 뷰 역시 이를 기반으로 하고 있는 것 같다.

 

컴포넌트와 관련된 개념과 영상이 많기 때문에 포스팅 분량이 길어지고 몇 부분으로 나눠질 것 같다.

 

* 이 글은 캡틴판교님의 Vue.js 강의 및 블로그를 기반으로 학습한 내용을 정리하고 있습니다.


💚 Vue 컴포넌트

React와 마찬가지로 화면을 영역 단위로 쪼개서, 재사용 가능한 코드로 캡슐화하는 개념이다.

뷰에서 컴포넌트를 등록하는 방법은 2가지가 있다. 전역 등록, 지역 등록이 바로 그것들이다.

 

- 전역 컴포넌트

<template>
  <컴포넌트 이름></컴포넌트 이름>
</template>

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

 

- 지역 컴포넌트 

new Vue({
  el: '#app',
  
  // 지역 컴포넌트
  components: {
    // '컴포넌트 이름': '컴포넌트 내용'
    'app-footer': {
      template: '<footer>Footer</footer>'
    }
  }
  
  
  // 혹은, 변수에 담은 객체를 전달하는 방법도 있다.
  const anotherWay = {
    template: '<div>Another Way!</div>'
  }
  
  components: {
    'another-way': anotherWay,
  }
});

실제 개발환경에선 지역 컴포넌트의 방식을 대부분 등록한다. (전역방식은 플러그인이나, 전역에 사용되는 컴포넌트 등록에만 사용됨)

통상은, 해당 인스턴스(vue 파일) 내에서 사용할 수 있는 컴포넌트들을 components 필드에 추가하여 사용하게 된다.

 

* new Vue() 인스턴스는 <Root> 루트 컴포넌트와 1:1 관계이다.

 

 

💚 컴포넌트 통신 방식

컴포넌트들은 자체 격리된 고유한 데이터 유효 범위를 갖는다. 따라서, 컴포넌트 간의 데이터 통신을 위해 아래 규칙을 따른다.

출처 : https://kr.vuejs.org/v2/guide/components.html

 

- Props

상위 컴포넌트에서 하위 컴포넌트로 특정 속성값을 넘겨주는 문법이다. 기본적인 바인딩 문법으로, "v-bind" 디렉티브를 사용한다.

<template>
  <div id="app">
    <app-header v-bind:props명="상위 컴포넌트 데이터명"></app-header>
  </div>
</template>

<script>
  var appHeader = {
    template: '<h1>{{ props명 }}</h1>',
    props: ['props명'],
  }
</script>

템플릿 리터럴 문법( {{ 값 }} )을 통해서 data, props 등을 바인딩할 수 있으며, props 필드에 참조할 props명을 배열형태로 관리한다.

이렇게 전달된 props는, 부모 컴포넌트의 조작을 통해 변형되면 바인딩된 자식 컴포넌트에도 전달된다. (Reactivity) 

 

* props 검증

다음과 같이 props에 type 검증, default 값 설정, validator 설정 등 다양한 추가동작이 가능하다.

Vue.component('example', {
  props: {
    // 기본 타입 확인 (`null` 은 어떤 타입이든 가능하다는 뜻입니다)
    propA: Number,
    // 여러개의 가능한 타입
    propB: [String, Number],
    // 문자열이며 꼭 필요합니다
    propC: {
      type: String,
      required: true
    },
    // 숫자이며 기본 값을 가집니다
    propD: {
      type: Number,
      default: 100
    },
    // 객체/배열의 기본값은 팩토리 함수에서 반환 되어야 합니다.
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 사용자 정의 유효성 검사 가능
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

 

* props 작명법

html에서는 케밥 케이스(props-data), 스크립트에선 카멜 케이스(propsData)로 각각의 문법에 맞게 작성되야 올바르게 인식함

 

- Event Emit (Event Bus)

하위 컴포넌트에서 상위 컴포넌트로의 통신을 위해 Event Emit 문법을 활용한다. 특정 값이 아닌, 이벤트를 올려 값 수정을 유도한다.

<template>
  <div id="app">
    <app-button v-on:이벤트명(하위 컴포넌트)="메서드명(상위 컴포넌트)"></app-button>
  </div>
</template>

<script>
  var appButton = {
    template: '<button v-on:click="passEvent">button</button>',
    methods: {
      passEvent: function() {
        this.$emit('이벤트명(하위 컴포넌트)', payload)
      }
    }
  }

  new Vue({
    el: '#app',
    components: {
      'app-button': appButton
    }
    methods: {
      '메서드명': function() {
        console.log('hi');
      }
    }
  })
</script>
  1. "v-on:이벤트명" 디렉티브를 통해 특정 이벤트와 메서드를 연계할 수 있다.
  2. 먼저, 이벤트를 발생시킬 하위 컴포넌트에서 $emit Vue API를 통해 상위 컴포넌트로 이벤트명을 전달해준다.
  3. 특정 값을 넘겨주려는 경우는, $emit의 두 번째 인자부터 추가해주면 payload 필드로 값이 넘어간다.
  4. 이벤트를 받는 상위 컴포넌트에서는 하위 컴포넌트의 이벤트명과 상위 컴포넌트에서 동작할 메서드를 연결한다.

 

- 동등 레벨 컴포넌트 간의 통신방법

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Getting Started</title>
  </head>
  <body>
    <!-- 2) Root : v-on을 통해 받은 payload 값을, 메서드를 통해 data 최신화. 이 data를 app-header의 props로 하달 -->
    <div id="app">
      <app-header v-bind:propsNum="num"></app-header>
      <app-content v-on:pass="deliverVal"></app-content>
    </div>

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

      // 3) app-header : props 속성으로 받음
      var appHeader = {
        template: '<div>header</div>',
        props: ['propsNum']
      }

      // 1) app-content : 이벤트, $emit을 통해 값 전달. payload 활용
      var appContent = {
        template: '<button v-on:click="passVal">pass</button>',
        methods: {
          passVal: function() {
            this.$emit('pass', 10);
          }
        },
      }

	  // 0) Vue 인스턴스 세팅
      new Vue({
        el: '#app',
        components: {
          'app-header': appHeader,
          'app-content': appContent,
        },
        data: {
          num: 0,
        },
        methods: {
          deliverVal: function(val) {
            this.num = val
          }
        },
      })
    </script>
  </body>
</html>

확실히 React 컴포넌트의 문법이 Javascript 친화적이었다는 점을 느낀다.

Vue만의 독특한 문법으로 작성되어야 하며, 각각의 속성(template, data, methods) 과 이를 연동하는 디렉티브가 그렇다.

 

Vue 컴포넌트를 만들어보면서 느낀 점은, 확실히 협업에 장점이 있다고 느꼈다. (문법이 일관적이므로)

반대로, 컴포넌트 간의 통신을 위해 Props, Event를 교환함에 따라 명명해야 할 요소가 많다.

이 부분을 깔끔하게 해야 할 필요성을 느꼈다!!

 

다음 글은 Vue 프로젝트에서 SPA 라우팅을 위해 사용되는 Vue Router에 대해 작성하고자 한다.

 

[출처]

- Vue 공식문서 : https://kr.vuejs.org/v2/guide/instance.html

- 캡틴판교 님의 블로그 : https://joshua1988.github.io/web-development/vuejs/vuejs-tutorial-for-beginner/  

반응형