Search
🌍

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

프론트엔드 프레임워크 뷰(Vue)의 기본에 충실하면 프레임워크 자체의 디자인패턴이랑과 별개로 우리가 작성하는 코드(from1)는 모델-뷰-컨트롤러 패턴에 맞게 분리된다(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