ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,8 @@
+import { MissionUtils } from '@woowacourse/mission-utils';
+
+export const readLineAsync = async (query) => {
+  const input = await MissionUtils.Console.readLineAsync(query);
+  return input;
+};
+
+export const print = (message) => MissionUtils.Console.print(message);

리뷰: 구조분해 할당으로 Console로만 처리해도 되지 않나요?
@@ -0,0 +1,104 @@
+import { print } from '../utils/missionUtils.js';
+import {
+  GIFT_THRESHOLD,
+  BENEFITS_THRESHOLD,
+  MENU_PRICE,
+} from '../utils/constants.js';
+
+const OutputView = {
+  printWelcome() {
+    print('안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.');
+  },
+
+  printGuide(date) {
+    print(`12월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!`);
+  },
+
+  printMenu(order) {
+    const dividedOrder = order.split(',');
+    const orderArray = dividedOrder.map((value) => {
+      const [menuName, count] = value.split('-');
+      return [menuName, count];
+    });
+
+    print('\n<주문 메뉴>');
+    orderArray.forEach((item) => {
+      const [menuName, count] = item;
+      print(`${menuName} ${count}개`);
+    });
+  },
+
+  printTotalPriceAndGift(price) {
+    const showPrice = price.toLocaleString();
+
+    print('\n<할인 전 총주문 금액>');
+    print(`${showPrice}원`);
+    print('\n<증정 메뉴>');
+
+    if (price >= GIFT_THRESHOLD) {
+      print('샴페인 1개');
+    } else {
+      print('없음');
+    }
+  },
+
+  printBenefits(price, benefitsArray, totalBenefitPrice) {
+    print('\n<혜택 내역>');
+
+    if (price < BENEFITS_THRESHOLD) {
+      print('없음');
+      return;
+    }
+
+    this.dateBenefits(benefitsArray);
+
+    if (totalBenefitPrice === 0) {
+      print('없음');
+    }
+  },
+
+  dateBenefits(benefitsArray) {
+    benefitsArray.forEach((dateBenefits) => {
+      dateBenefits.forEach((giftBenefit) => {
+        const [eventName, discount] = giftBenefit;
+        const showDiscount = discount.toLocaleString();
+        if (discount !== 0) {
+          print(`${eventName}: -${showDiscount}원`);
+        }
+      });
+    });
+  },
+
+  printTotalBenefitAmount(totalPrice, totalBenefitPrice) {
+    const showTotalDiscount = totalBenefitPrice.toLocaleString();
+
+    print('\n<총혜택 금액>');
+
+    if (totalBenefitPrice === 0 || totalPrice < BENEFITS_THRESHOLD) {
+      print('0원');
+    }
+
+    if (totalBenefitPrice !== 0 && totalPrice >= BENEFITS_THRESHOLD) {
+      print(`-${showTotalDiscount}원`);
+    }
+  },
+
+  printEstimatedAmount(payment) {
+    const estimatedAmount = payment.toLocaleString();
+
+    print('\n<할인 후 예상 결제 금액>');
+    print(estimatedAmount);
+  },
+
+  printBadge(calculateBadge) {
+    print('\n<12월 이벤트 배지>');
+
+    if (calculateBadge === undefined) {
+      print('없음');
+      return;
+    }
+    print(calculateBadge);
+  },
+};
+
+export default OutputView;

리뷰: 변수명에 자료형을 넣는 게 좋지 않다고 들었어요.
orders와 같이 복수표현으로 바꾸어 주시면 좋을 것  같아요.
@@ -0,0 +1,115 @@
+import OrderMenu from '../domain/OrderMenu.js';
+
+describe('주문 메뉴 검증 테스트', () => {
+  const orderMenuTestCase = [
+    {
+      testName: '주문 형식이 예시와 다르면 예외가 발생한다.',
+      input: '양송이수프 1개, 초코케이크 2개',
+    },
+    {
+      testName: '메뉴판에 없는 메뉴가 있으면 예외가 발생한다.',
+      input: '티본스테이크-1,마라탕-1',
+    },
+    {
+      testName: '주문 수량이 1 미만이면 예외가 발생한다.',
+      input: '해산물파스타-0,제로콜라-1',
+    },
+    {
+      testName: '주문 수량의 총합이 20이 넘으면 예외가 발생한다.',
+      input: '시저샐러드-14,바비큐립-5,타파스-2',
+    },
+    {
+      testName: '중복된 메뉴가 있으면 예외가 발생한다.',
+      input: '바비큐립-2,레드와인-1,바비큐립-1',
+    },
+  ];
+
+  orderMenuTestCase.forEach((testCase) => {
+    test(testCase.testName, () => {
+      expect(() => {
+        new OrderMenu(testCase.input);
+      }).toThrow('[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.');
+    });
+  });
+
+  test('음료만 주문하면 예외가 발생한다.', () => {
+    expect(() => {
+      new OrderMenu('제로콜라-5,레드와인-2,샴페인-3');
+    }).toThrow('[ERROR] 음료만 주문할 수 없습니다. 다시 입력해 주세요.');
+  });
+});
+
+describe('상태값 활용 테스트', () => {
+  test('수량과 가격을 곱해서 총 주문금액을 계산한다.', () => {
+    const orderMenuTestCase = [
+      {
+        input: '해산물파스타-3,양송이수프-1,타파스-2,제로콜라-4',
+        result: 134000,
+      },
+      {
+        input: '바비큐립-5,시저샐러드-3,레드와인-2,아이스크림-5',
+        result: 439000,
+      },
+      {
+        input:
+          '티본스테이크-2,해산물파스타-1,크리스마스파스타-10,초코케이크-3,샴페인-4',
+        result: 540000,
+      },
+    ];
+
+    orderMenuTestCase.forEach((testCase) => {
+      // given
+      const orderMenu = new OrderMenu(testCase.input);
+
+      // when
+      const received = orderMenu.totalOrderAmount();
+
+      // then
+      expect(received).toBe(testCase.result);
+    });
+  });
+
+  test('디저트 메뉴의 주문 개수를 계산한다.', () => {
+    const orderMenuTestCase = [
+      { input: '바비큐립-1,초코케이크-9,제로콜라-2,아이스크림-3', result: 12 },
+      { input: '크리스마스파스타-3,티본스테이크-5,레드와인-1', result: 0 },
+      {
+        input: '양송이수프-2,시저샐러드-5,해산물파스타-3,초코케이크-1',
+        result: 1,
+      },
+    ];
+
+    orderMenuTestCase.forEach((testCase) => {
+      // given
+      const orderMenu = new OrderMenu(testCase.input);
+
+      // when
+      const received = orderMenu.dessertCount();
+
+      // then
+      expect(received).toBe(testCase.result);
+    });
+  });
+
+  test('메인 메뉴의 주문 개수를 계산한다.', () => {
+    const orderMenuTestCase = [
+      {
+        input: '티본스테이크-2,양송이수프-1,시저샐러드-1,해산물파스타-4',
+        result: 6,
+      },
+      { input: '타파스-7,크리스마스파스타-4,레드와인-1', result: 4 },
+      { input: '양송이수프-2,제로콜라-4,초코케이크-13', result: 0 },
+    ];
+
+    orderMenuTestCase.forEach((testCase) => {
+      // given
+      const orderMenu = new OrderMenu(testCase.input);
+
+      // when
+      const received = orderMenu.mainCount();
+
+      // then
+      expect(received).toBe(testCase.result);
+    });
+  });
+});

리뷰: test.each()를 사용하지 않고 이렇게 처리하신 이유가 있으실까요?
@@ -0,0 +1,88 @@
+const DATE = Object.freeze({
+  eventEndDay: 31,
+  christmasEndDay: 25,
+});
+
+const WEEKDAY = Object.freeze([
+  3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31,
+]);
+
+const WEEKEND = Object.freeze([1, 2, 8, 9, 15, 16, 22, 23, 29, 30]);
+
+const SPECIAL = Object.freeze([3, 10, 17, 24, 25, 31]);
+
+class Benefits {
+  #date;
+
+  constructor(date) {
+    this.#validateNumber(date); // 자연수 검증
+    this.#date = Number(date);
+    this.#validateDate(); // 날짜 범위 검증
+  }
+
+  #validateNumber(date) {
+    const regex = /^[1-9]\d*$/;
+    const testResult = regex.test(date);
+
+    if (testResult === false) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  #validateDate() {
+    if (this.#date > DATE.eventEndDay) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  // 크리스마스 디데이 할인
+  christmasDiscount() {
+    const START_PRICE = 1000;
+    const INCREASE_PRICE = 100;
+    let discountedAmount = 0;
+
+    if (this.#date <= DATE.christmasEndDay) {
+      discountedAmount = START_PRICE + (this.#date - 1) * INCREASE_PRICE;
+    }
+
+    return ['크리스마스 디데이 할인', discountedAmount];
+  }
+
+  // 평일 할인
+  weekdayDiscount(dessertCount) {
+    const WEEKDAY_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKDAY.includes(this.#date)) {
+      discountedAmount = WEEKDAY_DISCOUNT * dessertCount;
+    }
+
+    return ['평일 할인', discountedAmount];
+  }
+
+  // 주말 할인
+  weekendDiscount(mainCount) {
+    const WEEKEND_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKEND.includes(this.#date)) {
+      discountedAmount = WEEKEND_DISCOUNT * mainCount;
+    }
+
+    return ['주말 할인', discountedAmount];
+  }
+
+  // 특별 할인
+  specialDiscount() {
+    const DISCOUNT_PRICE = 1000;
+    let discountedAmount = 0;
+
+    if (SPECIAL.includes(this.#date)) {
+      discountedAmount = DISCOUNT_PRICE;
+    }
+
+    return ['특별 할인', discountedAmount];
+  }
+}
+
+export default Benefits;

리뷰: DATE.eventEndDay 상수처리화하신 부분이 인상깊네요:)
@@ -0,0 +1,88 @@
+const DATE = Object.freeze({
+  eventEndDay: 31,
+  christmasEndDay: 25,
+});
+
+const WEEKDAY = Object.freeze([
+  3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31,
+]);
+
+const WEEKEND = Object.freeze([1, 2, 8, 9, 15, 16, 22, 23, 29, 30]);
+
+const SPECIAL = Object.freeze([3, 10, 17, 24, 25, 31]);
+
+class Benefits {
+  #date;
+
+  constructor(date) {
+    this.#validateNumber(date); // 자연수 검증
+    this.#date = Number(date);
+    this.#validateDate(); // 날짜 범위 검증
+  }
+
+  #validateNumber(date) {
+    const regex = /^[1-9]\d*$/;
+    const testResult = regex.test(date);
+
+    if (testResult === false) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  #validateDate() {
+    if (this.#date > DATE.eventEndDay) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  // 크리스마스 디데이 할인
+  christmasDiscount() {
+    const START_PRICE = 1000;
+    const INCREASE_PRICE = 100;
+    let discountedAmount = 0;
+
+    if (this.#date <= DATE.christmasEndDay) {
+      discountedAmount = START_PRICE + (this.#date - 1) * INCREASE_PRICE;
+    }
+
+    return ['크리스마스 디데이 할인', discountedAmount];
+  }
+
+  // 평일 할인
+  weekdayDiscount(dessertCount) {
+    const WEEKDAY_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKDAY.includes(this.#date)) {
+      discountedAmount = WEEKDAY_DISCOUNT * dessertCount;
+    }
+
+    return ['평일 할인', discountedAmount];
+  }
+
+  // 주말 할인
+  weekendDiscount(mainCount) {
+    const WEEKEND_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKEND.includes(this.#date)) {
+      discountedAmount = WEEKEND_DISCOUNT * mainCount;
+    }
+
+    return ['주말 할인', discountedAmount];
+  }
+
+  // 특별 할인
+  specialDiscount() {
+    const DISCOUNT_PRICE = 1000;
+    let discountedAmount = 0;
+
+    if (SPECIAL.includes(this.#date)) {
+      discountedAmount = DISCOUNT_PRICE;
+    }
+
+    return ['특별 할인', discountedAmount];
+  }
+}
+
+export default Benefits;

리뷰: 평일에 대한 날짜를 직접 작성 및 상수처리하셔서 WEEKDAY 객체안에 해당 날짜가 포함되는 지를 비교한 로직으로 예상됩니다. 
만약 2023년 12월이라는 범위의 값이 변경이 된다면 (2024년 1월의 평일,2024년 4월의 평일 등) 매번 날짜를 평일인지 확인해 직접 처리해야 함으로 확장성에 어려움이 생길 것 같아요. 
  js 에서 제공하는 내장 객체 Date 를 활용해 평일을 구하는 메서드를 활용하는 건 어떠신가요?
@@ -0,0 +1,88 @@
+const DATE = Object.freeze({
+  eventEndDay: 31,
+  christmasEndDay: 25,
+});
+
+const WEEKDAY = Object.freeze([
+  3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31,
+]);
+
+const WEEKEND = Object.freeze([1, 2, 8, 9, 15, 16, 22, 23, 29, 30]);
+
+const SPECIAL = Object.freeze([3, 10, 17, 24, 25, 31]);
+
+class Benefits {
+  #date;
+
+  constructor(date) {
+    this.#validateNumber(date); // 자연수 검증
+    this.#date = Number(date);
+    this.#validateDate(); // 날짜 범위 검증
+  }
+
+  #validateNumber(date) {
+    const regex = /^[1-9]\d*$/;
+    const testResult = regex.test(date);
+
+    if (testResult === false) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  #validateDate() {
+    if (this.#date > DATE.eventEndDay) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  // 크리스마스 디데이 할인
+  christmasDiscount() {
+    const START_PRICE = 1000;
+    const INCREASE_PRICE = 100;
+    let discountedAmount = 0;
+
+    if (this.#date <= DATE.christmasEndDay) {
+      discountedAmount = START_PRICE + (this.#date - 1) * INCREASE_PRICE;
+    }
+
+    return ['크리스마스 디데이 할인', discountedAmount];
+  }
+
+  // 평일 할인
+  weekdayDiscount(dessertCount) {
+    const WEEKDAY_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKDAY.includes(this.#date)) {
+      discountedAmount = WEEKDAY_DISCOUNT * dessertCount;
+    }
+
+    return ['평일 할인', discountedAmount];
+  }
+
+  // 주말 할인
+  weekendDiscount(mainCount) {
+    const WEEKEND_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKEND.includes(this.#date)) {
+      discountedAmount = WEEKEND_DISCOUNT * mainCount;
+    }
+
+    return ['주말 할인', discountedAmount];
+  }
+
+  // 특별 할인
+  specialDiscount() {
+    const DISCOUNT_PRICE = 1000;
+    let discountedAmount = 0;
+
+    if (SPECIAL.includes(this.#date)) {
+      discountedAmount = DISCOUNT_PRICE;
+    }
+
+    return ['특별 할인', discountedAmount];
+  }
+}
+
+export default Benefits;

리뷰: 로직의 일관성이 높아 읽기 매우 편하네요!
@@ -0,0 +1,88 @@
+const DATE = Object.freeze({
+  eventEndDay: 31,
+  christmasEndDay: 25,
+});
+
+const WEEKDAY = Object.freeze([
+  3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31,
+]);
+
+const WEEKEND = Object.freeze([1, 2, 8, 9, 15, 16, 22, 23, 29, 30]);
+
+const SPECIAL = Object.freeze([3, 10, 17, 24, 25, 31]);
+
+class Benefits {
+  #date;
+
+  constructor(date) {
+    this.#validateNumber(date); // 자연수 검증
+    this.#date = Number(date);
+    this.#validateDate(); // 날짜 범위 검증
+  }
+
+  #validateNumber(date) {
+    const regex = /^[1-9]\d*$/;
+    const testResult = regex.test(date);
+
+    if (testResult === false) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  #validateDate() {
+    if (this.#date > DATE.eventEndDay) {
+      throw new Error('[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.');
+    }
+  }
+
+  // 크리스마스 디데이 할인
+  christmasDiscount() {
+    const START_PRICE = 1000;
+    const INCREASE_PRICE = 100;
+    let discountedAmount = 0;
+
+    if (this.#date <= DATE.christmasEndDay) {
+      discountedAmount = START_PRICE + (this.#date - 1) * INCREASE_PRICE;
+    }
+
+    return ['크리스마스 디데이 할인', discountedAmount];
+  }
+
+  // 평일 할인
+  weekdayDiscount(dessertCount) {
+    const WEEKDAY_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKDAY.includes(this.#date)) {
+      discountedAmount = WEEKDAY_DISCOUNT * dessertCount;
+    }
+
+    return ['평일 할인', discountedAmount];
+  }
+
+  // 주말 할인
+  weekendDiscount(mainCount) {
+    const WEEKEND_DISCOUNT = 2023;
+    let discountedAmount = 0;
+
+    if (WEEKEND.includes(this.#date)) {
+      discountedAmount = WEEKEND_DISCOUNT * mainCount;
+    }
+
+    return ['주말 할인', discountedAmount];
+  }
+
+  // 특별 할인
+  specialDiscount() {
+    const DISCOUNT_PRICE = 1000;
+    let discountedAmount = 0;
+
+    if (SPECIAL.includes(this.#date)) {
+      discountedAmount = DISCOUNT_PRICE;
+    }
+
+    return ['특별 할인', discountedAmount];
+  }
+}
+
+export default Benefits;

리뷰: 사소하다면 사소한 부분이지만 '[ERROR] '는 모든 예외 메시지에서 사용되므로 prefix로 상수화하시면 좋을 것 같아요. 뒤에 이어지는 예외 메시지도 마찬가지구요!
@@ -0,0 +1,141 @@
+## 📝 구현 대상
+
+### 크리스마스 프로모션
+
+### - 목표
+
+=> 유연성과 확장성을 강조한 12월 이벤트 플래너를 개발하여 변화에 대응하고, 객체지향의 장점을 최대한 활용한다. 
+중앙처리식 시스템이 아닌 객체 간의 협력을 강조하여 '살아있는' 시스템을 설계하고, 
+모든 도메인 로직에 철저한 단위 테스트를 도입하여 시스템의 무결성을 보장한다. 
+이를 통해 확장성 있고 유지보수가 용이한 효과적인 이벤트 플래너를 구축한다.
+
+### - 세부 목표 및 지침
+
+- 클래스보다는 메시지 중심 설계
+- UI는 도메인 로직과 분리
+- 단일 책임 원칙을 지키자
+- 의존성 역전 원칙을 지키자
+- 클래스 분리 및 역할 부여
+- 역할, 책임, 협력 설계
+- 인스턴스 변수의 수를 줄이기 위해 노력하자
+- 무분별한 getter 사용 대신 객체에 메시지를 보내 객체가 로직을 수행하도록 하자
+- 객체는 객체스럽게 사용하자
+- 단위 테스트하기 어려운 코드를 단위 테스트해보자
+
+---
+
+### - 설계
+
+#### 1차 단계 (메세지 다이어그램 구성)
+
+<img src="image/xmas_promo_3rd_msg.png">
+
+#### 기능 명세
+
+- 입력
+
+> [기능] 방문 날짜를 입력받는 기능<br>
+> [기능] 주문할 메뉴와 갯수를 입력받는 기능<br>
+
+- 출력
+
+> [기능] 고객에게 안내할 이벤트 주의사항을 출력하는 기능
+> - 총주문 금액 10,000원 이상부터 이벤트가 적용된다고 출력하는 기능<br>
+> - 음료만 주문 시, 주문할 수 없다고 출력하는 기능<br>
+> - 메뉴는 한 번에 최대 20개까지만 주문할 수 있다고 출력하는 기능<br>
+
+> [기능] 이벤트 플래너 실행 결과 출력하는 기능
+> - 주문 메뉴를 출력하는 기능<br>
+> - 할인 전 총 주문 금액 출력하는 기능<br>
+> - 증정 메뉴를 출력하는 기능<br>
+> - 혜택 내역 출력하는 기능<br>
+> - 총혜택 금액을 출력하는 기능<br>
+> - 할인 후 예상 결제 금액을 출력하는 기능<br>
+> - 12월 이벤트 배지를 출력하는 기능<br>
+
+> [기능] 에러 메시지 출력하는 기능
+> - 예외 명세서에 명시된 예외에 대한 메시지를 출력하는 기능
+
+- 로직
+
+> [기능] 메뉴 관련 기능 <br>
+> - 메뉴 목록을 만드는 기능<br>
+> - 메뉴의 이름을 반환하는 기능 <br>
+> - 메뉴의 가격을 반환하는 기능 <br>
+> - 메뉴의 타입(에피타이저/메인/디저트/음료)을 반환하는 기능<br>
+> - 메뉴 이름을 기반으로 메뉴 객체를 가져오는 기능<br>
+
+> [기능] 증정 메뉴 관련 기능 <br>
+> - 증정 메뉴 목록을 만드는 기능<br>
+> - 증정 메뉴의 이름을 반환하는 기능<br>
+> - 증정 메뉴의 총가격(가격 * 수량)을 반환하는 기능<br>
+> - 증정 메뉴 목록의 총 가격을 반환하는 기능<br>
+> - 출력할 증정 메뉴 정보를 만드는 기능<br>
+
+> [기능] 주문 관련 기능 <br>
+> - 주문 항목 하나의 총가격(가격 * 수량)을 계산하는 기능<br>
+> - 주문 항목의 이름을 반환하는 기능<br>
+> - 주문 항목의 수량을 반환하는 기능<br>
+> - 주문 항목의 메뉴 타입을 반환하는 기능<br>
+> - 주문에 주문 항목을 추가하는 기능 <br>
+> - 주문 항목들을 반환하는 기능 <br>
+> - 총 주문 금액(할인 전 총 주문 금액)을 계산하는 기능<br>
+> - 출력할 주문 정보를 만드는 기능<br>
+
+> [기능] 날짜 관련 기능 <br>
+> - 날짜를 반환하는 기능<br>
+> - 날짜가 금요일인지 체크하는 기능<br>
+> - 날짜가 토요일인지 체크하는 기능<br>
+> - 날짜가 일요일인지 체크하는 기능<br>
+> - 날짜가 크리스마스인지 체크하는 기능<br>
+> - 날짜가 크리스마스 디데이 기간인지 체크하는 기능<br>
+> - 날짜가 평일인지 체크하는 기능<br>
+> - 날짜가 주말인지 체크하는 기능<br>
+> - 날짜가 별을 가지는지 체크하는 기능<br>
+
+> [기능] 이벤트 할인과 관련된 기능 <br>
+> - 방문 날짜의 크리스마스 디데이 할인 금액을 계산하는 기능<br>
+> - 방문 날짜의 평일 할인 금액을 계산하는 기능<br>
+> - 방문 날짜의 주말 할인 금액을 계산하는 기능<br>
+> - 방문 날짜의 특별 할인 금액을 계산하는 기능<br>
+
+> [기능] 증정 이벤트와 관련된 기능 <br>
+> - 증정 이벤트를 적용 가능한지 체크하는 기능<br>
+> - 증정 이벤트로 혜택본 금액을 계산하는 기능<br>
+
+> [기능] 혜택 내역과 관련된 기능 <br>
+> - 주문에 이벤트를 적용 가능한지 체크하는 기능<br>
+> - 혜택 내역을 생성하는 기능 <br>
+> - 총 혜택 금액을 반환하는 기능<br>
+> - 할인 후 예상 결제 금액을 반환하는 기능<br>
+> - 출력할 혜택 내역 정보를 만드는 기능<br>
+
+> [기능] 이벤트 배지와 관련된 기능<br>
+> - 총혜택 금액에 따라 이벤트 베지를 만드는 기능<br>
+> - 이벤트 배지를 반환하는 기능<br>
+
+> [기능] 기타 기능 <br>
+> - 입력한 주문을 바탕으로 주문 객체를 만드는 기능<br>
+> - 금액을 ###.##0 형식으로 포매팅하는 기능<br>
+
+
+- 예외
+> [예외] 입력받은 방문 날짜 대한 예외처리
+> - 방문 날짜가 숫자가 아닌 경우<br>
+> - 방문 날짜가 1~31을 벗어나는 경우<br>
+
+> [예외] 입력받은 주문 메뉴에 대한 예외처리
+> - 메뉴 형식이 예시와 다른 경우<br>
+> - 메뉴판에 없는 메뉴를 입력한 경우<br>
+> - 중복 메뉴를 입력한 경우<br>
+> - 입력한 메뉴의 갯수가 1 이상의 숫자가 아닌 경우<br>
+
+> [예외] 런타임 예외처리
+> - 주문내역이 20개를 초과하는 경우<br>
+> - 음료만 주문한 경우<br>
+  
+---
+### - 최종 클래스 다이어그램
+<img src="image/xmasFinalDiagram.png">
+
+

리뷰: 어떤 부분을 고민하고 구현하셨는지 보기 편해서 좋네요 👍👍
@@ -0,0 +1,54 @@
+package christmas.controller;
+
+import christmas.domain.entity.Day;
+import christmas.domain.entity.Order;
+import christmas.view.InputView;
+import java.util.function.Supplier;
+
+import static christmas.domain.verifier.RuntimeVerifier.RUNTIME_VERIFIER;
+import static christmas.system.IOMessage.ORDER_PROMPT_MESSAGE;
+import static christmas.system.IOMessage.VISIT_DATE_PROMPT_MESSAGE;
+import static christmas.view.InputView.*;
+import static christmas.view.OutputView.*;
+
+public class PlannerSystem {
+
+    private final Day day;
+    private final Order order;
+    private final SettlementSystem settlementSystem;
+
+    public PlannerSystem() {
+        printMessage(VISIT_DATE_PROMPT_MESSAGE);
+        this.day = readDay();
+        printEventNotice();
+        printMessage(ORDER_PROMPT_MESSAGE);
+        this.order = readOrder(InputView::tryReadOrder);
+        this.settlementSystem = new SettlementSystem(day, order);
+    }
+    public void run() {
+        renderResult();
+    }
+
+    private Order readOrder(Supplier<Order> orderSupplier) {
+        while (true) {
+            try {
+                Order order = orderSupplier.get();
+                RUNTIME_VERIFIER.validate(order);
+                return order;
+            } catch (IllegalStateException e) {
+                printMessage(e.getMessage());
+            }
+        }
+    }
+
+    private void renderResult() {
+        renderPlannerResult();
+        settlementSystem.renderSettlementResult();
+
+    }
+
+    private void renderPlannerResult() {
+        printResultStartMessage(day);
+        printOrderList(order);
+    }
+}

리뷰: renderResult()를 따로 메서드로 빼신 이유가 있으실까요?
@@ -0,0 +1,54 @@
+package christmas.controller;
+
+import christmas.domain.entity.Day;
+import christmas.domain.entity.Order;
+import christmas.view.InputView;
+import java.util.function.Supplier;
+
+import static christmas.domain.verifier.RuntimeVerifier.RUNTIME_VERIFIER;
+import static christmas.system.IOMessage.ORDER_PROMPT_MESSAGE;
+import static christmas.system.IOMessage.VISIT_DATE_PROMPT_MESSAGE;
+import static christmas.view.InputView.*;
+import static christmas.view.OutputView.*;
+
+public class PlannerSystem {
+
+    private final Day day;
+    private final Order order;
+    private final SettlementSystem settlementSystem;
+
+    public PlannerSystem() {
+        printMessage(VISIT_DATE_PROMPT_MESSAGE);
+        this.day = readDay();
+        printEventNotice();
+        printMessage(ORDER_PROMPT_MESSAGE);
+        this.order = readOrder(InputView::tryReadOrder);
+        this.settlementSystem = new SettlementSystem(day, order);
+    }
+    public void run() {
+        renderResult();
+    }
+
+    private Order readOrder(Supplier<Order> orderSupplier) {
+        while (true) {
+            try {
+                Order order = orderSupplier.get();
+                RUNTIME_VERIFIER.validate(order);
+                return order;
+            } catch (IllegalStateException e) {
+                printMessage(e.getMessage());
+            }
+        }
+    }
+
+    private void renderResult() {
+        renderPlannerResult();
+        settlementSystem.renderSettlementResult();
+
+    }
+
+    private void renderPlannerResult() {
+        printResultStartMessage(day);
+        printOrderList(order);
+    }
+}

리뷰: outputView를 static으로 하신 이유가 있으실까요?
@@ -0,0 +1,54 @@
+package christmas.controller;
+
+import christmas.domain.entity.Day;
+import christmas.domain.entity.Order;
+import christmas.view.InputView;
+import java.util.function.Supplier;
+
+import static christmas.domain.verifier.RuntimeVerifier.RUNTIME_VERIFIER;
+import static christmas.system.IOMessage.ORDER_PROMPT_MESSAGE;
+import static christmas.system.IOMessage.VISIT_DATE_PROMPT_MESSAGE;
+import static christmas.view.InputView.*;
+import static christmas.view.OutputView.*;
+
+public class PlannerSystem {
+
+    private final Day day;
+    private final Order order;
+    private final SettlementSystem settlementSystem;
+
+    public PlannerSystem() {
+        printMessage(VISIT_DATE_PROMPT_MESSAGE);
+        this.day = readDay();
+        printEventNotice();
+        printMessage(ORDER_PROMPT_MESSAGE);
+        this.order = readOrder(InputView::tryReadOrder);
+        this.settlementSystem = new SettlementSystem(day, order);
+    }
+    public void run() {
+        renderResult();
+    }
+
+    private Order readOrder(Supplier<Order> orderSupplier) {
+        while (true) {
+            try {
+                Order order = orderSupplier.get();
+                RUNTIME_VERIFIER.validate(order);
+                return order;
+            } catch (IllegalStateException e) {
+                printMessage(e.getMessage());
+            }
+        }
+    }
+
+    private void renderResult() {
+        renderPlannerResult();
+        settlementSystem.renderSettlementResult();
+
+    }
+
+    private void renderPlannerResult() {
+        printResultStartMessage(day);
+        printOrderList(order);
+    }
+}

리뷰: static import를 하실 때는 어떤 것들을 import했는지 잘 나타날 수 있도록 와일드카드(*)를 사용하지않고 전부 표시하는 것을 추천하더라구요.
참고하시면 좋을 것 같습니다!
@@ -0,0 +1,32 @@
+package christmas.controller;
+
+import christmas.domain.entity.Day;
+import christmas.domain.entity.DiscountRecord;
+import christmas.domain.entity.Order;
+import christmas.domain.manager.BonusEventManager;
+import christmas.domain.manager.DiscountManager;
+
+import static christmas.domain.entity.EventBadge.createBadge;
+import static christmas.view.OutputView.*;
+
+public class SettlementSystem {
+
+    private final Order order;
+    private final DiscountRecord discountRecord;
+
+    public SettlementSystem(Day day, Order order) {
+        DiscountManager discountManager = new DiscountManager();
+        BonusEventManager bonusEventManager = new BonusEventManager();
+        this.order = order;
+        this.discountRecord = DiscountRecord.create(day, order, discountManager, bonusEventManager);
+    }
+
+    public void renderSettlementResult() {
+        printOriginalTotalAmount(order);
+        printBonusMenu(order);
+        printDiscountRecord(discountRecord);
+        printTotalDiscountAmount(discountRecord);
+        printExpectedPayment(order, discountRecord);
+        printEventBadge(createBadge(discountRecord));
+    }
+}

리뷰: 저도 컨트롤러를 여러가지로 나눠보려고 했어서 반갑네요!
어떤 기준으로 나눠보려고 하셨는지 궁금해요