컴포넌트란 단어 자체의미만으로는 구성요소로 기계의 부품 정도로 생각하면 된다. vue.js에서 컴포넌트로 구성되며, 재활용에 있어서 컴포넌트는 매우 중요하다. 다룰 vue.js에서 컴포넌트는 버전 2에 기반한 컴포넌트 내용이다.
작성자: ospace114@empal.com, http://ospace.tistory.com/
들어가기
vue.js에 컴포넌트는 객체지향 프로그래밍에서 클래스와 비슷하다. 물론 형식이나 구조는 다르지만 중요성이나 사용목적이 비슷하다. vue 컴포넌트는 vue 인스탄스이기 때문에 vue에 있는 기능을 모두 사용할 수 있다. vue 컴포넌트에도 라이프 사이클이 있고 중간에 훅을 제공하기 때문에 해당 라이프사이클은 잘 알아두는게 좋다. [2]
컴포넌트 등록
컴포넌트 등록을 간단하게 살펴보고 가자. 컴포넌트 등록 방법은 전역 등록과 지역 등록이 있다. 전역 등록은 한번 등록하면 어느 곳에서 사용할 수 있지만, 지역 등록은 등록한 범위 내에서만 사용가능하다. 그렇기 때문에 공통으로 사용할 컴포넌트는 전역 등록으로 특정 화면에서만 사용한다면 지역 등록으로 하면 된다. 물론 자신의 목적에 맞게 적절하게 선택하면 된다.
전역 등록
전역 등록 방식은 다음과 같다. 컴포넌트태그명은 태그로 사용할 이름이다. 영문자와 대쉬가 사용가능하다. 그리고 {옵션}은 컴포넌트에 대한 정의가 포함된다. 물론 별로 파일로 분리할 수 있고, 임포트해서 사용할 수도 있다.
Vue.component('컴포넌트태그명', {옵션});
지역 등록
지역 등록 방식은 컴포넌트 옵션에서 등록하는 방식이다. 등록된 컴포넌트에 범위에서만 사용할 수 있다. 입력되는데이터는 전역 등록과 동일하다.
<script>
export default {
components: {
'컴포넌트태그명', {옵션}
},
//...
};
</script>
간단한 컴포넌트 정의
간단한 컴포넌트를 정의해보자. 컴포넌트 태그명은 foo로 사용했다. 단순 텍스트 출력이다.
// src/components/Foo.vue
<template>
<label>{{label}}</label>
</template>
<script>
export default {
props: {
label: {
type: String,
default: 'label',
},
},
}
</script>
<style></style>
컴포넌트는 크게 3개 요소로 구성된다.
- template은 화면에 표시되는 내용
- script은 데이터 관리 및 사용자 입력에 대한 처리를 수행
- style에서 화면에 CSS을 정의
props에 label 속성을 정의했다. props을 통해서 태그 속성 형태로 값을 부모로부터 받을 수 있다. label 속성은 간단한 문자열 받으며, 값이 없다면 'label' 문자열을 사용한다.
정의할 수 있는 props의 타입은 String, Number, Boolean, Function, Object, Array, Symbol 등이 가능하다.
한번 사용해보자. 해당 컴포넌트를 가져오는 방법은 지역 등록 방식을 사용하여 components 속성에 추가하면 된다. 아래 처럼 간단하게 처리하면 된다.
export default {
components: {
Foo: () => import('@/components/Foo.vue'),
},
}
대문자로 되어 있지만 소문자 형태로도 사용가능하다. 컴포넌트 태그명은 파스칼 형태나 캐밥형태 모두 가능하지만 dom에서 사용할 때에는 캐밥 형태만 가능하다. [3]
컴포넌트를 추가해서 사용하는 방법은 간단하다.
<template>
<div>
<foo></foo>
</div>
</template>
속성에 값도 넘겨보자. 부모 컴포넌트에서 자식 컴포넌트로 값을 넘겨주는 간단한 방법이다.
<template>
<div>
<foo label="like"></foo>
</div>
</template>
조금 확장해보자
다음으로 체크박스를 추가해보자. checked라는 데이터에 값도 바인딩해보자.
<template>
<label>
<input type="checkbox" v-model="checked"/>
{{label}}
</label>
</template>
<script>
export default {
props: {
label: {
type: String,
default: 'label',
},
},
data() {
return {
checked: false,
};
}
}
</script>
결과는 다음과 같다. 간단한 체크 박스가 표시된다.
자식 컴포넌트 값 가져오기
체크박스를 체크하면 값은 checked에 저장된다. 부모 컴포넌트가 필요한 것은 체크박스의 값인데 가져올 수가 없다. Foo 컴포넌트에 있는 checked은 외부에서 바로 접근이 안된다. 부모와 인터페이스할 수 있는 기능은 props인데, 이것도 단방향으로 부모에서 자식 방향으로만 가능하다.
vue.js에서는 "input" 이벤트로 값 변경을 알려서 갱신할 수 있다.
<template>
<label>
<input type="checkbox" v-model="checked" @change="$emit('input', checked)"/>
{{label}}
</label>
</template>
체크박스 컨트롤에서 "change" 이벤트에 대해 $emit을 사용해서 "input" 이벤트를 발생한다. 그리고 값은 checked 값을 넘겨준다. 직접 체크박스에서 이벤트 핸들러로 처리할 수도 있지만, watch을 사용할 수도 있다.
watch: {
checked() {
this.$emit('input', this.checked);
}
}
부모 컴포넌트에서 값이 변경되는지 확인해보자.
<template>
<div>
<foo label="like" @input="(val)=>this.myValue=val"> </foo>
<br/>
Selected: {{ myValue }}
</div>
</template>
<script>
export default {
components: {
Foo: () => import('@/components/Foo.vue'),
},
data() {
return {
myValue: true,
};
},
}
</script>
"@input"은 "input"에 대한 핸들러 등록으로 "v-on:input"에 대한 간략한 표기형태이다.
부모에서 "input" 이벤트에 핸들러를 추가하고 값을 받으면 "this.myValue"에 저장한다. 저장된 값을 화면에 표시된다.
선택되면 "true", 선택해지 되면 "false"가 잘 표시된다.
부모에서 추가 값을 획득하자
부모 컴포넌트에서 자식 컴포넌트의 체크여부 값을 제어하고 싶다. 이를 위해 value라는 새로운 props을 추가하겠다.
props: {
//...
value: {
type: Boolean,
default: false,
},
},
data() {
return {
checked: this.value,
};
},
추가되는 props 자료형은 Boolean이다. 새로운 추가된 value을 체크박스에 적용하려면 checked에 값을 넣어줘야한다. 위의 예제에서는 data에서 this.value로 바로 checked에 값을 저장하고 있다. computed을 사용하여 처리할 수도 있다.
computed: {
checked: function() {
return this.value;
}
}
이 방식은 획득만 가능하고 저장할 수 없다. 저장도 가능하지만, 복잡해진다.
props의 value을 체크박스에 바로 바인딩할 수 없다. 읽기 전용이라면 가능하지만, 쓰기까지 되는 경우 vue에서 경고문구가 표시된다. 그래서 중간에 data혹은 computed을 거쳐서 처리되어야 한다.
추가로 더 해줘야할 부분이, 초기 컴포넌트 생성시 부모 컴포넌트에서 값을 가져오지만, 추후 부모 컴포넌트에서 값이 변경되는 경우를 위해 아래 코드가 필요하다. value값이 변경되면 checked에 값을 반영해준다.
watch: {
value() {
this.checked = this.value;
},
//...
},
부모 컴포넌트에서는 추가 값에 대해서 바인딩한다.
<foo label="like" :value="myValue" @input="(val)=>this.myValue=val"> </foo>
":value" 표기는 "v-bind:value"을 간략하게 표기한 형태이다.
이제 최종 확인을 하면 다음과 같을 것이다. myValue의 초기값을 true로 하면 체크박스가 선택된 상태로 표시된다.
간략하게 해보자.
부모 컴포넌트에서 자식 컴포넌트와 값을 주고 받는 부분이 너무 복잡하고 해야할게 너무 많아 보인다. vue에서는 이를 간단하게 만드는 방법이 있다. 부모 컴포넌트에서 아래 처럼 표기가 가능하다.
<foo label="like" v-model="myValue"> </foo>
v-model을 사용해서 앞의 추가했던 작업을 처리하게 된다. 그렇기 때문에 조건이 있다.
값을 넘겨주는 props는 value이어야하고 변경 값은 input 이벤트로 받아야한다.
앞의 간략하게 표기하는 방식은 여러 개 값에 대해서는 불가능하다. 오브젝트로 한꺼번에 값을 처리해야한다. 만약, 분리해서 처리하고 싶다면 앞의 예제처럼 일일히 별도로 추가 처리하기에는 힘들기 때문에 vue에서 sync라는 추가 기능을 사용해서 가능하다. 조금 번거로울 뿐이다.
만약, props의 이름이 "value"가 아니고 이벤트가 "input" 아닌 다른 이름을 사용할 경우에는 어떻게 할까? 자식 컴포넌트에서 model이라는 속성을 지정해주면 된다.
model: {
prop: 'data',
event: 'change',
},
위의 예제는 props가 "data"이고 이벤트가 "change"를 사용하겠다는 의미이다.
부모 컴포넌트에서 자식 컴포넌트로 값 전달
부모 컴포넌트에서 자식 컴포넌트로 값만 넘겨줄 경우가 있다. 앞의 예제에서 label 처럼 문자열을 넘겨주는 경우이다.
값을 넘겨줄 때에 문자열이 아닌 다른 형식으로 주는 경우 값만 넘겨줄 수 없다. 아래 처럼 "true"로 하면 문자열로 넘어가기 때문에 foo 컴포넌트 내부에서 value 타입은 Boolean으로 에러가 발생한다.
<foo label="like" value="true"></foo>
그렇기 때문에 문자열이 아닌 다른 타입들은 값을 넘겨줄 때에 v-bind 바인딩 형태로 아래처럼 넘겨줘야 한다.
<foo label="like" :value="true"></foo>
참고
[1] 컴포넌트, https://kr.vuejs.org/v2/guide/components.html
[2] Vue 인스턴스, https://kr.vuejs.org/v2/guide/instance.html
[3] 컴포넌트 등록, https://kr.vuejs.org/v2/guide/components-registration.html
[4] 폼 입력 바인딩, https://kr.vuejs.org/v2/guide/forms.html
'3.구현 > HTML5&Javascript' 카테고리의 다른 글
[vue2] vuetify 팝업 모달창 사용 (0) | 2021.11.09 |
---|---|
[vue2] 여러 숫자 자동입력 컴포넌트 (0) | 2021.11.09 |
[vue2] 레이아웃 구성 (0) | 2021.10.27 |
[vue2] Router 활용한 접근제어 (0) | 2021.10.25 |
[vue2] Vue.js 프로젝트 기본 구성 (0) | 2021.10.21 |