[Front-End] 우테코 프리코스 4주차 회고 (크리스마스 프로모션)

2023. 11. 16. 17:52우아한 테크 코스/프리코스

이번 4주차 미션은 여태까지의 프리코스에서 나왔던 다리 건너기가 아닌 새로운 문제가 나왔다.

그리고 저장소는 비공개로 생성해야 된다는 제출 방식과 관련된 규정도 추가됐다.

 

아마도 폭발적으로 증가한 지원자 수를 의식한 것이 아닐까 생각했다. (웹 프론트엔드 첫번째 미션 fork수만 950,,ㅜㅜ)

 

1. MVC 패턴 적용

 

지난 3주차에서 클린 아키텍처를 구현하는 것에 어려움을 느꼈었다. 그래서 이번 4주차 미션에서는 디자인 패턴을 적용하여 깔끔하고 이해하기 쉬운 구조를 설계하고자 MVC 패턴에 대해서 공부했다. MVC 패턴에 대해 학습하고 미션에서 적용해본 것이 이번 미션 얻은 가장 큰 경험인 것 같다.

 

3주차 미션 폴더 구조

src  
 ┣ Constants  
 ┃ ┣ InputConstants.js  
 ┃ ┣ LottoConstants.js  
 ┃ ┗ ValidateConstants.js  
 ┣ io  
 ┃ ┣ UserInput.js  
 ┃ ┗ UserOutput.js  
 ┣ services  
 ┃ ┣ Calculator.js  
 ┃ ┣ LottoResultChecker.js  
 ┃ ┗ NumberIssuance.js  
 ┣ utils  
 ┃ ┗ ValidateInput.js  
 ┣ App.js  
 ┣ index.js  
 ┣ Lotto.js  
 ┗ LottoGame.js

 

 

4주차 미션 폴더 구조

src  
 ┣ controller  
 ┃ ┗ AppController.js  
 ┣ model  
 ┃ ┣ BadgeModel.js  
 ┃ ┣ DateModel.js  
 ┃ ┣ EventModel.js  
 ┃ ┗ OrderModel.js  
 ┣ utils  
 ┃ ┣ Constants.js  
 ┃ ┣ DisplayGenerator.js  
 ┃ ┣ Parser.js  
 ┃ ┗ Validator.js  
 ┣ view  
 ┃ ┣ InputView.js  
 ┃ ┗ OutputView.js  
 ┣ App.js  
 ┗ index.js

 

 

MVC 패턴을 적용하고 느낀 점은 도메인 로직을 분리하기 정말 용이하다는 것이다.

그리고 전체적인 흐름을 어떻게 관리해야 될지 고민이었는데 MVC패턴의 컨트롤러가 이에 대한 해답이 되어주었다.

마찬가지로 배웠던 자세한 내용은 따로 포스팅하도록 하겠다.

 

이제 미션이 전부 끝났으니 결과가 발표되기전까지 열심히 배운것들을 정리하고 포스팅할 예정이다.

 

 

2. 기능 목록

 

플로우 차트도 한번 그려보고 나니 첫번째보다 훨씬 그리기 수월했고, MVC 패턴을 적용한 것을 플로우 차트에도 적용하여 섹터를 나누니 보기에도 훨씬 편해졌다.

 

1. 플로우 차트

 

 

 

그리고 이번 미션에서는 클래스 다이어그램도 공부해서 그려 봤는데, 아직 연관 관계를 나타내는 것에서 정확한 이해가 부족해서 제대로 된 클래스 다이어그램을 그리지 못한 것 같아 기능 목록에 첨부하지 말까 고민했지만, 그래도 조금은 코드 이해에 보탬이 되지 않을까 하여 첨부했다.

 

 

2. 클래스 다이어그램

 

 

코드

 

아래는 이번 미션의 가장 핵심 역할인 이벤트 기능들을 구현해 놓은 EventModel이다. 주요 기능을 아래와 같이 모델을 생성하고 해당 모델 안에서 관련된 도메인 로직들을 전부 수행한 뒤 결과만을 외부로 수출하도록 설계했다.

 

하지만 여전히 아쉬운 점은 지난주 배웠던 private 키워드를 활용해보는 것이 또 하나의 목표였으나 MVC 패턴만을 공부하고 적용해보기도 벅찼다,, 그래서 모든 변수들이 외부에서 접근 가능하도록 구현되어 있는 것이 아쉬움으로 남았다.

 
 import Constants from '../utils/Constants.js';

 class EventModel {
   constructor(dateModel, orderModel) {
     this.date = dateModel;
     this.order = orderModel;
     this.discount = {
       xmasDday: 0,
       weekday: 0,
       weekend: 0,
       special: 0,
     };
     this.present = { menu: '없음', quantity: 0, price: 0 };
     this.totalValueOfBenefits = 0;
   }

   calculateBenefits() {
     if (this.order.totalAmount >= Constants.NUMBER.MIN_AMOUNT_OF_EVENT) {
       this.discount.xmasDday = this.discountXmasDday();
       this.discount.weekday = this.discountWeekday();
       this.discount.weekend = this.discountWeekend();
       this.discount.special = this.discountStarDay();
       this.present = this.givePresent();
       this.totalValueOfBenefits = this.calculateTotalValueOfBenefits();
     }
   }

   discountXmasDday() {
     const passedDay = this.date.date - 1;

     if (passedDay >= Constants.NUMBER.DAY_OF_CHRISTMAS) {
       return 0;
     }

     return (
       Constants.NUMBER.DEFAULT_D_DAY_DISCOUNT +
       passedDay * Constants.NUMBER.DISCOUNT_WEIGHT_OF_D_DAY
     );
   }

   discountWeekday() {
     if (!this.date.isWeekend) {
       return this.order.dessertCount * Constants.NUMBER.DISCOUNT_DAY_OF_WEEK;
     }

     return 0;
   }

   discountWeekend() {
     if (this.date.isWeekend) {
       return this.order.mainCount * Constants.NUMBER.DISCOUNT_DAY_OF_WEEK;
     }

     return 0;
   }

   discountStarDay() {
     if (this.date.isStarDay) {
       return Constants.NUMBER.DISCOUNT_SPECIAL_DAY;
     }

     return 0;
   }

   givePresent() {
     let menu = '없음';
     let quantity = 0;
     let price = 0;
     if (this.order.totalAmount >= Constants.NUMBER.AMOUNT_OF_PRESENT) {
       menu = '샴페인';
       quantity += 1;
       price = Constants.MENU[menu].price * quantity;
     }

     return { menu, quantity, price };
   }

   calculateTotalValueOfBenefits() {
     let price = 0;
     Object.entries(this.discount).forEach(([, value]) => {
       price += value;
     });

     price += this.present.price;

     return price;
   }
 }
 export default EventModel;

 

 

 

아래는 프로그램 전역에서 사용되는 상수들을 모아놓은 객체이다. 이 코드에는 크게 두가지 깨달음이 바탕이 되어있다.

 

1. 창피한 일이지만, 나는 다른 파일에서 사용하기 위해 객체를 export하려면 반드시 클래스로 작성해야 되는 줄 알았다.

 

 

그런데 이번 미션에서 사전에 제공된 InputView, OutputView 파일이 객체 리터럴 방식으로 생성되고 export되어 있는 것을 보고 클래스가 아닌 일반적인 객체도 export가 가능하다는 것을 깨달았다.

 

2. 1주차 미션에서는 매직 넘버라는 것에 대한 개념이 없었고,

    2주차 미션에서는 매직 넘버를 해당 파일의 최상단에 상수로 포장하여 지양했다.

    3주차 미션에서는 상수들을 한 곳에 모아 관리하고자 객체로 관리했다.

 

그런데 객체로 만들어 관리하면 상수의 불변성이란 이점을 살릴 수 없고, 각 파일에 최상단에 const로 선언하여 사용하자니 객체처럼 한 곳에 모아 유지보수를 용이하게 하고픈 욕구가 있었다.

 

그래서 위 두가지 조건을 모두 충족할 방법을 찾다가 Object.freeze() 라는 객체의 내장함수를 알게 되었다.

 
 const Constants = {
   MENU: Object.freeze({
     양송이수프: { type: 'appetizer', price: 6000 },
     타파스: { type: 'appetizer', price: 5500 },
     시저샐러드: { type: 'appetizer', price: 8000 },
     티본스테이크: { type: 'main', price: 55000 },
     바비큐립: { type: 'main', price: 54000 },
     해산물파스타: { type: 'main', price: 35000 },
     크리스마스파스타: { type: 'main', price: 25000 },
     초코케이크: { type: 'dessert', price: 15000 },
     아이스크림: { type: 'dessert', price: 5000 },
     제로콜라: { type: 'drink', price: 3000 },
     레드와인: { type: 'drink', price: 60000 },
     샴페인: { type: 'drink', price: 25000 },

     SEPERATOR: ',',
     NAME_AND_QUANTITY_SEPERATOR: '-',
   }),

   NUMBER: Object.freeze({
     EVENT_YEAR: 2023,
     EVENT_MONTH: 12,
     STANDARD_OF_WEEKEND: 5,
     START_DATE_OF_MONTH: 1,
     END_DATE_OF_MONTH: 31,

     MIN_COUNT_OF_MENU: 1,
     MAX_COUNT_OF_TOTAL_MENU: 20,
     DAY_OF_CHRISTMAS: 25,

     DEFAULT_D_DAY_DISCOUNT: 1000,
     DISCOUNT_WEIGHT_OF_D_DAY: 100,
     DISCOUNT_DAY_OF_WEEK: 2023,
     DISCOUNT_SPECIAL_DAY: 1000,

     MIN_AMOUNT_OF_EVENT: 10000,
     AMOUNT_OF_PRESENT: 120000,
     AMOUNT_OF_STAR_BADGE: 5000,
     AMOUNT_OF_TREE_BADGE: 10000,
     AMOUNT_OF_SANTA_BADGE: 20000,
   }),

   INPUT_MESSAGE: Object.freeze({
     DATE: '12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n',
     ORDER:
       '주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
   }),

   ERROR_MESSAGE: Object.freeze({
     DATE: '[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.',
     ORDER: '[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.',
   }),

   STAR_DAYS: Object.freeze([3, 10, 17, 24, 25, 31]),

   NAME_OF_EVENT: Object.freeze({
     xmasDday: '크리스마스 디데이 할인',
     weekday: '평일 할인',
     weekend: '주말 할인',
     special: '특별 할인',
     present: '증정 이벤트',
   }),
 };

 export default Constants;

 

 

 

 

매주 모든 미션들이 그러했지만, 이번 4주차 미션은 특히나 많은 시간을 쏟았던 것 같다.

 

원인으로는 단순히 미션 난이도의 상승도 기인했지만, 그보다는 1주차 2주차 3주차에 배운 것들을 흘리지 않고 잘지키면서 새로운 것들을 배우고 적용하려 했던 것이 컸다.

 

 

 

-4주차 프리코스 회고 끝-