15 –애플리케이션 실전 파트– 컴포넌트 디자인 패턴
source: categories/study/vue-beginner-lv3/vue-beginner-lv3_9-06.md
15.1 Component Design Patterns 소개
컴포넌트 디자인 패턴
- Common - Common 패턴은 기본적인 컴포넌트 등록과 컴포넌트 통신 방식을 의미합니다.
- Slot - Slot 패턴은 마크업 확장이 가능한 컴포넌트를 의미합니다.
- Controlled - Controlled 패턴은 결합력이 높은 컴포넌트를 의미합니다.
아직까지 한번도 보여드리진 않았습니다.
Controlled 패턴을 사용하시게되면 특히 써드 파티 라이브러리들.. 그리고 체크박스를 컨트롤할 때 쉽게 컨트롤할 수 있게끔 컴포넌트를 설계할 수 있습니다. - Renderless - Renderless 패턴은 데이터 처리 컴포넌트를 의미합니다.
한마디로template
표현식이 없습니다.
script
단에서 어떤 비즈니스 로직만 처리해서 상위 컴포넌트로 다시 데이터를 노출시켜주는 역할을 합니다.
(feat. + HOC, Mixins)
15.2 Common Approach
props validation 문법
export default {
// 일반적으로 props를 아래와 같이 배열로 선언했었다.
// props: ['title'],
// props는 컴포넌트간 주고받는 데이터이다보니까 validation이라고 하는 유효성 검사가 필요해서,
// 아래처럼 type이라던지, 혹은 required - 필수 속성이라던지, 기본값을 설정할 수가 있다.
// props validation 문법이다.
props: {
title: String,
}
}
App.vue
데이터를 내려주고(props
)event emit
을 받는 컨테이너 컴포넌트AppContent.vue
,AppHeader.vue
화면에 뿌려주는 프레젠테이셔널 컴포넌트- 단방향 통신 준수
- N:N 통신 지양
15.3 Component with Slots - Slot VS Props
15.4 Component with Slots 구현 방법과 활용처
위와 같이 slot
을 사용하게되면 하위 컴포넌트의 데이터에 대한 의존성이 없어집니다.
위와 같이 Item
에 데이터는 없고 데이터가 여전히 ‘상위 컴포넌트'에만 머물러있습니다.
현재 Item.vue
컴포넌트는 App.vue
에 있는 데이터를 표현만 했을 뿐입니다.
만약 props
로 내려준다면 하위 컴포넌트에서 내려받은 props
를 바꾸지 못한다거나 제약사항이 생기는거죠.
slot을 쓰는 이유
‘요구 사항' 때문.
위와 같은 list
컴포넌트를 사용할 때, 특정 list
부분에 버튼이 들어가게 해주세요. 라는 요구사항이 왔다면,
slot
을 사용하지 않은 상태라면 해당 컴포넌트는 사용하지 못하게됩니다.
왜냐면 굳어있기 때문입니다.
slot
을 사용하면 유연하게 확장할 수 있습니다.
15.5 Controlled Component - Input 박스를 다룰 때 생기는 문제점
위와 같이 컴포넌트를 세밀하게 쪼갤 때가 온다.
이런 경우를 대비해서 살펴보도록하자.
위와 같이 코딩을 하면,
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "checked"
위와 같이 에러가 뜨게된다.
props
로 내려받은 값을 하위 컴포넌트에서 직접적으로 수정하지 말라 라는 에러메시지입니다.
N:N 통신을 방지하기 위해 컴포넌트 통신 방식 위에서 아래로는 props
아래에서 위로는 event emit
이 존재하는건데,
<template>
<!-- 일반적으로 input에 v-model을 썼다. 2-way 바인딩. -->
<!-- 그런데 이와 같은 코딩은 잘못된 코딩이다. -->
<input type="checkbox" v-model="checked">
</template>
<script>
export default {
props: ['checked'],
}
</script>
위와 같이 v-model
로 하위 컴포넌트에서 2-way 데이터 바인딩으로 묶어버리면,
<template>
<!-- 일반적으로 input에 v-model을 썼다. 2-way 바인딩. -->
<!-- 그런데 이와 같은 코딩은 잘못된 코딩이다. -->
<input type="checkbox" :value="checked" @input="updateInput">
</template>
<script>
export default {
props: ['checked'],
methods: {
updateInput: function(event) {
var updatedText = event.target.value;
this.checked = updatedText;
}
},
}
</script>
v-model
은 사실 위와 같은 구조입니다.
즉, 하위 컴포넌트에서 상위 data의 값을 바꿔버리는 상황입니다.
이를 어떻게 해결할 수 있을까?
15.6 Controlled Component - 구현 방법과 활용처
이 방법도 그렇고 slot도 그렇고 하위에서 관리해야될 데이터(props)를 상위 컴포넌트에서 관리하도록 만든다.
컴포넌트의 데이터 의존성을 분리하는 것들이 저희가 지금 배우고있는 디자인 패턴의 핵심이다.
달력, 모달, 간단 입력폼 - 위와 같은 방법으로하면 굉장히 유용하다.
번외 - vuex, redux 같은 상태관리 상태의 컴포넌트(컨테이너 컴포넌트, 프레젠테이셔널 컴포넌트)를 위한 storybook 설정 연구
겨우 에러안나게는 했다..
그런데 실제로 사용하려면..?
좀 더 연구 필요..
15.7 Renderless Component - 소개
위의 FetchData.vue
를 보면 template
이 없다.
오직 script
만 있다.
데이터 제공만을 위한 컴포넌트이다.
FetchData.vue
<script>
import axios from 'axios';
export default {
props: ['url'],
data() {
return {
response: null,
loading: true,
}
},
created() {
axios.get(this.url)
.then(response => {
this.response = response.data;
this.loading = false;
})
.catch(error => {
alert('[ERROR] fetching the data', error);
console.log(error);
});
},
render() {
return this.$scopedSlots.default({
response: this.response,
loading: this.loading,
});
},
}
</script>
15.8 Renderless Component - Render Function
15.9 Renderless Component - 구현 방법과 활용처
아래에서 데이터를 전달하기 때문에 하위 컴포넌트에 데이터가 있다.
그리고 기존과는 다르게 하위 컴포넌트의 데이터 값을 변경하면 그것이 화면에 반영된다.
(이런 점은 기존과 달라서 좀 헷갈릴수도 있으려나?)
하위 컴포넌트의 데이터를 상위컴포넌트에서 참고하는 기이한 현상이 발생한다는 것이다.
원래 데이터는 상위 컴포넌트에서 하위 컴포넌트로 props
로 내려주는데..
여튼 이런식으로 유연하게 컴포넌트를 설계할 수 있다.