[Vue.js] 컴포넌트(Component), Props & Event
이번에는 비교적 친숙한! 뷰의 컴포넌트 개념에 대해 알아보고자 한다. 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 관계이다.
💚 컴포넌트 통신 방식
컴포넌트들은 자체 격리된 고유한 데이터 유효 범위를 갖는다. 따라서, 컴포넌트 간의 데이터 통신을 위해 아래 규칙을 따른다.
- 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>
- "v-on:이벤트명" 디렉티브를 통해 특정 이벤트와 메서드를 연계할 수 있다.
- 먼저, 이벤트를 발생시킬 하위 컴포넌트에서 $emit Vue API를 통해 상위 컴포넌트로 이벤트명을 전달해준다.
- 특정 값을 넘겨주려는 경우는, $emit의 두 번째 인자부터 추가해주면 payload 필드로 값이 넘어간다.
- 이벤트를 받는 상위 컴포넌트에서는 하위 컴포넌트의 이벤트명과 상위 컴포넌트에서 동작할 메서드를 연결한다.
- 동등 레벨 컴포넌트 간의 통신방법
<!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/