Search
🌍

bb7.1.1.2_1.2.1.1.5_1. title: 뷰(Vue)의 기본에 충실하면 작성하는 코드는 모델-뷰-컨트롤러(‣) 패턴에 맞게 분리된다.

생성
🚀 prev note
🚀 next note
♻️ next note
bb7.1.1.2_1.2.1.1.5_1.1. title: 모델-뷰-컨트롤러(‣) 패턴에서 모델(Model)과 뷰(View)의 관계는 엄밀하게 정의되지 않는다. 뷰는 컨트롤러와만 소통하기도 하고, 뷰가 모델과 직접 소통하기도 한다. MVC의 본질은 모델 뷰 컨트롤러가 분리되어 있고, 컨트롤러가 중앙에서 모델을 통제한다는 것이다. 프레임워크에서 양방향 데이터바인딩을 지원하며 패턴이 흐트러졌다.
bc1_2. title: 컴포넌트 구조에서 모델-뷰-컨트롤(‣) 패턴을 어기고 양방향 바인딩을 이용하다 보니, 프로퍼티 내려꽂기(Props drilling) 문제와 이벤트 버스(Event bus)로 주고받는 데이터의 추적이 어려워져 복잡성이 배가 되었다. 이러한 복잡성을 덜기 위해 중앙집중식으로 상태를 관리하는 디자인 패턴이 등장했고 그것이 플럭스(‣)패턴이다.
bb7.1.1.2_1.2.1.1.5_1.1_1. title: 모델-뷰-컨트롤러(‣) 패턴의 핵심은, 모델 뷰 컨트롤 코드를 최대한 분리해야 한다는 것, 그리고 뷰(View)에서 모델(Model)을 직접 변경하는 일을 최대한 피해 상태를 잘 관리하는 것이다.
14 more properties
프론트엔드 프레임워크 뷰(Vue)의 기본에 충실하면 프레임워크 자체의 디자인패턴이랑과 별개로 우리가 작성하는 코드(from1)는 모델-뷰-컨트롤러(‣ Model-View-Controller (MVC)) 패턴에 맞게 분리된다(sup1:분리해서 보는 것이 맞긴 한지, MVC가 엄밀하게 지켜지곤 하는건지).
예를 들어 뷰(View)에 버튼과 버튼이 몇 번 클릭되었는지를 표시하는 상황을 떠올려 보자. <template> 태그 안에 버튼이 정의되어 있을 것이다. 버튼이 클릭되면 이벤트 바인드를 통해 컨트롤러(Controller) 메서드를 호출한다. 호출된 컨트롤러는 버튼이 몇 번 클릭되었는지에 대한 상태(State, 혹은 Model)를 1만큼 늘린다. 뷰(View)는 상태(State, 혹은 Model)의 변화를 감지하고 UI에 표시하는 버튼 클릭 횟수를 업데이트한다.’
// View Area ---------------------------- <template> ... <button @click="onClickController"> {{ state }} </button> ... </template> <script> export default { // Model Area --------------------------- data() { state: 0 } // Controller Area ---------------------- method { onClickController() { return this.state += 1 } } }; </script>
JavaScript
복사
이렇게 뷰-모델-컨트롤러를 잘 분리하라는 원칙은 모범 사례에도 나타나 있다. 템플릿(template) 속성에서 복잡한 연산을 하거나 메소드(method) 속성을 더럽히지 말고 컴퓨티드(comuted) 속성으로 분리하라는 말이 있다(from2). 메소드는 모델이나 뷰 영역이 아니라 컨트롤러 영역이기 때문이다(sup1).
// View Area ---------------------------- <template> ... <button @click="onClickController"> {{ cntString }} </button> ... </template> <script> export default { computed: { cntString: function() { return this.state.toString() + "회 클릭됨" } } // Model Area --------------------------- data() { state: 0 } // Controller Area ---------------------- method { onClickController() { return this.state += 1 } } }; </script>
JavaScript
복사
MVC 패턴을 이해했다면 조금 더 복잡해진 아래 코드에 computed 프로퍼티를 이용해 컨트롤과 모델을 분리해 보자. 아래 코드는 리팩터링 전 코드이다.
<template> <div class="compare-box-outer"> <h1 id="compare-box-title">Which one is better?</h1> <b-card-group v-show="showCardGroup"> <b-card :title="getPercentage('A')" class="rounded-2" :class="{ selected: selectedCard == 'A' }" :img-src="imgSrcA" img-alt="ImageA" img-top> <b-card-text> </b-card-text> <template #footer> <small class="text-muted"></small> </template> <div> <b-button v-if="!isButtonLocked" variant="outline-primary" @click="lockAllButton('A')">❤️ 이게 더 좋아요!</b-button> <b-button v-else disabled :variant="getLockButton('A')">❤️ 이게 더 좋아요!</b-button> </div> </b-card> <div class="interval"></div> <b-card :title="getPercentage('B')" class="rounded-2" :class="{ selected: selectedCard == 'B' }" :img-src="imgSrcB" img-alt="ImageB" img-top> <b-card-text> </b-card-text> <template #footer> <small class="text-muted"></small> </template> <div> <b-button v-if="!isButtonLocked" variant="outline-primary" @click="lockAllButton('B')">❤️ 이게 더 좋아요!</b-button> <b-button v-else disabled :variant="getLockButton('B')">❤️ 이게 더 좋아요!</b-button> </div> </b-card> </b-card-group> </div> </template> <script> import { EventBus } from '@/main.js' export default { data() { return { imgSrcA: "https://placekitten.com/g/1080/1350", imgSrcB: "https://placekitten.com/g/1080/1350", percentageLikeA: 68, percentageLikeB: 32, isButtonLocked: false, selectedCard: "", showCardGroup: true, }; }, watch: { isButtonLocked() { if (this.isButtonLocked){ window.setTimeout(() => { this.showCardGroup = false; }, 10); } } }, methods: { lockAllButton(AorB) { this.isButtonLocked = true; this.selectedCard = AorB; }, getLockButton(AorB){ if (AorB == this.selectedCard){ return 'primary'; } else { return 'outline-primary'; } }, getPercentage(AorB){ if (this.isButtonLocked) { if (AorB == 'A') { return this.percentageLikeA.toString() + "%" } else { return this.percentageLikeB.toString() + "%" } } else { return AorB } }, onAfterLeave(){ EventBus.$emit('singleCompareEnd', true) } }, }; </script>
JavaScript
복사
위 코드를 리팩터링하면 아래와 같이 재구성된다.
<template> <div class="compare-box-outer"> <h1 id="compare-box-title">Which one is better?</h1> <b-card-group v-show="showCardGroup"> <b-card :title="cardApercentageString" class="rounded-2" :class="{ selected: selectedCard == 'A' }" :img-src="imgSrcA" img-alt="ImageA" img-top> <b-card-text> </b-card-text> <template #footer> <small class="text-muted"></small> </template> <div> <b-button v-if="!isButtonLocked" variant="outline-primary" @click="lockAllButton('A')">❤️ 이게 더 좋아요!</b-button> <b-button v-else disabled :variant="conditionalLockedButtonA">❤️ 이게 좋아요!</b-button> </div> </b-card> <div class="interval"></div> <b-card :title="cardBpercentageString" class="rounded-2" :class="{ selected: selectedCard == 'B' }" :img-src="imgSrcB" img-alt="ImageB" img-top> <b-card-text> </b-card-text> <template #footer> <small class="text-muted"></small> </template> <div> <b-button v-if="!isButtonLocked" variant="outline-primary" @click="lockAllButton('B')">❤️ 이게 더 좋아요!</b-button> <b-button v-else disabled :variant="conditionalLockedButtonB">❤️ 이게 좋아요!</b-button> </div> </b-card> </b-card-group> </div> </template> <script> import { EventBus } from '@/main.js' export default { computed: { cardApercentageString: function() { if (this.isButtonLocked) { return this.percentageLikeA.toString() + "%" } else { return 'A' } }, cardBpercentageString: function() { if (this.isButtonLocked) { return this.percentageLikeA.toString() + "%" } else { return 'B' } }, conditionalLockedButtonA: function() { if (this.selectedCard == 'A') { return 'primary' } else { return 'outline-primary' } }, conditionalLockedButtonB: function() { if (this.selectedCard == 'B') { return 'primary' } else { return 'outline-primary' } } }, data() { return { imgSrcA: "https://placekitten.com/g/1080/1350", imgSrcB: "https://placekitten.com/g/1080/1350", percentageLikeA: 68, percentageLikeB: 32, isButtonLocked: false, selectedCard: "", showCardGroup: true, }; }, watch: { isButtonLocked() { if (this.isButtonLocked){ window.setTimeout(() => { this.showCardGroup = false; }, 10); } } }, methods: { lockAllButton(AorB) { this.isButtonLocked = true; this.selectedCard = AorB; }, onAfterLeave(){ EventBus.$emit('singleCompareEnd', true) } }, }; </script>
JavaScript
복사
parse me : 언젠가 이 글에 쓰이면 좋을 것 같은 재료들.
1.
None
Quiz
from : 과거의 어떤 생각이 이 생각을 만들었는가?
2.
supplementary : 어떤 새로운 생각이 이 문서에 작성된 생각을 뒷받침하는가?
opposite : 어떤 새로운 생각이 이 문서에 작성된 생각과 대조되는가?
1.
None
to : 이 문서에 작성된 생각이 어떤 생각으로 발전되고 이어지는가?
참고 : 레퍼런스
1.
None