ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,90 @@
+const MENU_KIND = Object.freeze({
+  APPETIZER: '애피타이저',
+  MAIN: '메인',
+  DESSERT: '디저트',
+  DRINK: '음료',
+});
+
+const MENU = Object.freeze({
+  양송이수프: { price: 6000, kind: MENU_KIND.APPETIZER },
+  타파스: { price: 5500, kind: MENU_KIND.APPETIZER },
+  시저샐러드: { price: 8000, kind: MENU_KIND.APPETIZER },
+  티본스테이크: { price: 55000, kind: MENU_KIND.MAIN },
+  바비큐립: { price: 54000, kind: MENU_KIND.MAIN },
+  해산물파스타: { price: 35000, kind: MENU_KIND.MAIN },
+  크리스마스파스타: { price: 25000, kind: MENU_KIND.MAIN },
+  초코케이크: { price: 15000, kind: MENU_KIND.DESSERT },
+  아이스크림: { price: 5000, kind: MENU_KIND.DESSERT },
+  제로콜라: { price: 3000, kind: MENU_KIND.DRINK },
+  레드와인: { price: 60000, kind: MENU_KIND.DRINK },
+  샴페인: { price: 25000, kind: MENU_KIND.DRINK },
+});
+
+const DATE = Object.freeze({
+  EVENT_START: 1,
+  EVENT_END: 25,
+  PERIOD_DISCOUNT: 1000,
+  SPECIAL_DISCOUNT: 1000,
+  SPECIAL_DATE: [3, 10, 17, 24, 25, 31],
+  PER_DAY_DISCOUNT: 100,
+});
+
+const INFO = Object.freeze({
+  UNIT: '원',
+  COUNT: '개',
+  WEEK_DISCOUNT: 2023,
+  ORDER_MINIMUM: 10000,
+  GIFT_CONDITION: 120000,
+  GIFT_PRICE: 25000,
+  GIFT: '샴페인 1개',
+  NONE: '없음',
+  BADGE: {
+    SANTA: { PRICE: 20000, NAME: '산타' },
+    TREE: { PRICE: 10000, NAME: '트리' },
+    STAR: { PRICE: 5000, NAME: '별' },
+  },
+});
+
+const DISCOUNT = Object.freeze({
+  CHRISTMAS: '크리스마스 디데이 할인',
+  WEEK: '평일 할인',
+  WEEKEND: '주말 할인',
+  SPECIAL: '특별 할인',
+  GIFTS: '증정 이벤트',
+});
+
+const INPUT_MESSAGE = Object.freeze({
+  DATE: '12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n',
+  ORDER:
+    '주문하실 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
+});
+
+const OUTPUT_MESSAGE = Object.freeze({
+  INTRO: '안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.\n',
+  TITLE: {
+    PREVIEW: (date) => `12월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리보기!\n`,
+    ORDER_MENU: '<주문 메뉴>',
+    BEFORE_DISCOUNT: '<할인 전 총주문 금액>',
+    AFTER_DISCOUNT: '<할인 후 예상 결제 금액>',
+    GIFT: '<증정 메뉴>',
+    BENEFIT: '<혜택 내역>',
+    BENEFIT_AMOUNT: '<총혜택 금액>',
+    BADGE: '<12월 이벤트 배지>',
+  },
+});
+
+const ERROR_MESSAGE = Object.freeze({
+  INVALID_DATE: '[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.',
+  INVALID_ORDER: '[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.',
+});
+
+export {
+  MENU_KIND,
+  MENU,
+  DATE,
+  INFO,
+  DISCOUNT,
+  INPUT_MESSAGE,
+  OUTPUT_MESSAGE,
+  ERROR_MESSAGE,
+};

리뷰: 미션을 진행하면서 개인적으론 key값을 한글로 하는게 맞는가? 하는 의문이 들었습니다!
여경님 생각이 궁금합니다!
@@ -0,0 +1,28 @@
+import InputView from '../View/InputView';
+import OutputView from '../View/OutputView';
+import Client from './Client';
+
+class PromotionController {
+  async insertInput() {
+    const client = new Client(
+      await InputView.readDate(),
+      await InputView.readOrder(),
+    );
+    const date = client.getDate();
+    this.printOutput(client, date);
+  }
+
+  printOutput(client, date) {
+    OutputView.printIntro();
+    OutputView.printPreview(date);
+    OutputView.printMenuTitle(client);
+    OutputView.printBeforeDiscount(client);
+    OutputView.printGift(client);
+    OutputView.printBenefit(client);
+    OutputView.printDiscountAmount(client);
+    OutputView.printAfterDiscount(client);
+    OutputView.printEventBadge(client);
+  }
+}
+
+export default PromotionController;

리뷰: Controller 로직이 깔끔해서 보기 좋네요 배워가겠습니다!
@@ -0,0 +1,90 @@
+const MENU_KIND = Object.freeze({
+  APPETIZER: '애피타이저',
+  MAIN: '메인',
+  DESSERT: '디저트',
+  DRINK: '음료',
+});
+
+const MENU = Object.freeze({
+  양송이수프: { price: 6000, kind: MENU_KIND.APPETIZER },
+  타파스: { price: 5500, kind: MENU_KIND.APPETIZER },
+  시저샐러드: { price: 8000, kind: MENU_KIND.APPETIZER },
+  티본스테이크: { price: 55000, kind: MENU_KIND.MAIN },
+  바비큐립: { price: 54000, kind: MENU_KIND.MAIN },
+  해산물파스타: { price: 35000, kind: MENU_KIND.MAIN },
+  크리스마스파스타: { price: 25000, kind: MENU_KIND.MAIN },
+  초코케이크: { price: 15000, kind: MENU_KIND.DESSERT },
+  아이스크림: { price: 5000, kind: MENU_KIND.DESSERT },
+  제로콜라: { price: 3000, kind: MENU_KIND.DRINK },
+  레드와인: { price: 60000, kind: MENU_KIND.DRINK },
+  샴페인: { price: 25000, kind: MENU_KIND.DRINK },
+});
+
+const DATE = Object.freeze({
+  EVENT_START: 1,
+  EVENT_END: 25,
+  PERIOD_DISCOUNT: 1000,
+  SPECIAL_DISCOUNT: 1000,
+  SPECIAL_DATE: [3, 10, 17, 24, 25, 31],
+  PER_DAY_DISCOUNT: 100,
+});
+
+const INFO = Object.freeze({
+  UNIT: '원',
+  COUNT: '개',
+  WEEK_DISCOUNT: 2023,
+  ORDER_MINIMUM: 10000,
+  GIFT_CONDITION: 120000,
+  GIFT_PRICE: 25000,
+  GIFT: '샴페인 1개',
+  NONE: '없음',
+  BADGE: {
+    SANTA: { PRICE: 20000, NAME: '산타' },
+    TREE: { PRICE: 10000, NAME: '트리' },
+    STAR: { PRICE: 5000, NAME: '별' },
+  },
+});
+
+const DISCOUNT = Object.freeze({
+  CHRISTMAS: '크리스마스 디데이 할인',
+  WEEK: '평일 할인',
+  WEEKEND: '주말 할인',
+  SPECIAL: '특별 할인',
+  GIFTS: '증정 이벤트',
+});
+
+const INPUT_MESSAGE = Object.freeze({
+  DATE: '12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n',
+  ORDER:
+    '주문하실 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
+});
+
+const OUTPUT_MESSAGE = Object.freeze({
+  INTRO: '안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.\n',
+  TITLE: {
+    PREVIEW: (date) => `12월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리보기!\n`,
+    ORDER_MENU: '<주문 메뉴>',
+    BEFORE_DISCOUNT: '<할인 전 총주문 금액>',
+    AFTER_DISCOUNT: '<할인 후 예상 결제 금액>',
+    GIFT: '<증정 메뉴>',
+    BENEFIT: '<혜택 내역>',
+    BENEFIT_AMOUNT: '<총혜택 금액>',
+    BADGE: '<12월 이벤트 배지>',
+  },
+});
+
+const ERROR_MESSAGE = Object.freeze({
+  INVALID_DATE: '[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.',
+  INVALID_ORDER: '[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.',
+});
+
+export {
+  MENU_KIND,
+  MENU,
+  DATE,
+  INFO,
+  DISCOUNT,
+  INPUT_MESSAGE,
+  OUTPUT_MESSAGE,
+  ERROR_MESSAGE,
+};

리뷰: 저도 이부분 때문에 곤혹을 치뤘는데 다들 비슷한 생각이셨나봐요 🤔 특히 샴페인 증정 여부를 판별할 때 `샴페인.price` 식으로 호출해야하는 점 때문에 샴페인의 가격과 명칭은 따로 증정메뉴에 대한 상수로 선언하기도 했습니다.. 
로직상에서는 호출되지 않지만, 테스트 코드에서는 또 호출해야하는 부분들이 있어 고민이 많았네요 ㅎㅎ
사실..
```js
SOUP : { name: 양송이 수프, price : 6000, ... }
```
요런 식으로 선언 형태를 바꾸거나 id 로 SOUP를 선언해서 키값을 판별하는 방식을 사용한다면 한글로 작성하는 로직을 덜어낼 수 있었을 거란 생각을 했었어요...ㅎㅎ 입력에 대한 변환이 쉬운 형태를 채택하고자 했던 거 같습니다 🤣
@@ -0,0 +1,90 @@
+const MENU_KIND = Object.freeze({
+  APPETIZER: '애피타이저',
+  MAIN: '메인',
+  DESSERT: '디저트',
+  DRINK: '음료',
+});
+
+const MENU = Object.freeze({
+  양송이수프: { price: 6000, kind: MENU_KIND.APPETIZER },
+  타파스: { price: 5500, kind: MENU_KIND.APPETIZER },
+  시저샐러드: { price: 8000, kind: MENU_KIND.APPETIZER },
+  티본스테이크: { price: 55000, kind: MENU_KIND.MAIN },
+  바비큐립: { price: 54000, kind: MENU_KIND.MAIN },
+  해산물파스타: { price: 35000, kind: MENU_KIND.MAIN },
+  크리스마스파스타: { price: 25000, kind: MENU_KIND.MAIN },
+  초코케이크: { price: 15000, kind: MENU_KIND.DESSERT },
+  아이스크림: { price: 5000, kind: MENU_KIND.DESSERT },
+  제로콜라: { price: 3000, kind: MENU_KIND.DRINK },
+  레드와인: { price: 60000, kind: MENU_KIND.DRINK },
+  샴페인: { price: 25000, kind: MENU_KIND.DRINK },
+});
+
+const DATE = Object.freeze({
+  EVENT_START: 1,
+  EVENT_END: 25,
+  PERIOD_DISCOUNT: 1000,
+  SPECIAL_DISCOUNT: 1000,
+  SPECIAL_DATE: [3, 10, 17, 24, 25, 31],
+  PER_DAY_DISCOUNT: 100,
+});
+
+const INFO = Object.freeze({
+  UNIT: '원',
+  COUNT: '개',
+  WEEK_DISCOUNT: 2023,
+  ORDER_MINIMUM: 10000,
+  GIFT_CONDITION: 120000,
+  GIFT_PRICE: 25000,
+  GIFT: '샴페인 1개',
+  NONE: '없음',
+  BADGE: {
+    SANTA: { PRICE: 20000, NAME: '산타' },
+    TREE: { PRICE: 10000, NAME: '트리' },
+    STAR: { PRICE: 5000, NAME: '별' },
+  },
+});
+
+const DISCOUNT = Object.freeze({
+  CHRISTMAS: '크리스마스 디데이 할인',
+  WEEK: '평일 할인',
+  WEEKEND: '주말 할인',
+  SPECIAL: '특별 할인',
+  GIFTS: '증정 이벤트',
+});
+
+const INPUT_MESSAGE = Object.freeze({
+  DATE: '12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n',
+  ORDER:
+    '주문하실 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
+});
+
+const OUTPUT_MESSAGE = Object.freeze({
+  INTRO: '안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.\n',
+  TITLE: {
+    PREVIEW: (date) => `12월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리보기!\n`,
+    ORDER_MENU: '<주문 메뉴>',
+    BEFORE_DISCOUNT: '<할인 전 총주문 금액>',
+    AFTER_DISCOUNT: '<할인 후 예상 결제 금액>',
+    GIFT: '<증정 메뉴>',
+    BENEFIT: '<혜택 내역>',
+    BENEFIT_AMOUNT: '<총혜택 금액>',
+    BADGE: '<12월 이벤트 배지>',
+  },
+});
+
+const ERROR_MESSAGE = Object.freeze({
+  INVALID_DATE: '[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.',
+  INVALID_ORDER: '[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.',
+});
+
+export {
+  MENU_KIND,
+  MENU,
+  DATE,
+  INFO,
+  DISCOUNT,
+  INPUT_MESSAGE,
+  OUTPUT_MESSAGE,
+  ERROR_MESSAGE,
+};

리뷰: Object.freeze() 는 얕은 동결을 해주기 때문에 deepFreeze에 대해서 알아보시면 좋을 것 같아요!!

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#%EB%B0%B0%EC%97%B4_%EB%8F%99%EA%B2%B0
@@ -0,0 +1,90 @@
+const MENU_KIND = Object.freeze({
+  APPETIZER: '애피타이저',
+  MAIN: '메인',
+  DESSERT: '디저트',
+  DRINK: '음료',
+});
+
+const MENU = Object.freeze({
+  양송이수프: { price: 6000, kind: MENU_KIND.APPETIZER },
+  타파스: { price: 5500, kind: MENU_KIND.APPETIZER },
+  시저샐러드: { price: 8000, kind: MENU_KIND.APPETIZER },
+  티본스테이크: { price: 55000, kind: MENU_KIND.MAIN },
+  바비큐립: { price: 54000, kind: MENU_KIND.MAIN },
+  해산물파스타: { price: 35000, kind: MENU_KIND.MAIN },
+  크리스마스파스타: { price: 25000, kind: MENU_KIND.MAIN },
+  초코케이크: { price: 15000, kind: MENU_KIND.DESSERT },
+  아이스크림: { price: 5000, kind: MENU_KIND.DESSERT },
+  제로콜라: { price: 3000, kind: MENU_KIND.DRINK },
+  레드와인: { price: 60000, kind: MENU_KIND.DRINK },
+  샴페인: { price: 25000, kind: MENU_KIND.DRINK },
+});
+
+const DATE = Object.freeze({
+  EVENT_START: 1,
+  EVENT_END: 25,
+  PERIOD_DISCOUNT: 1000,
+  SPECIAL_DISCOUNT: 1000,
+  SPECIAL_DATE: [3, 10, 17, 24, 25, 31],
+  PER_DAY_DISCOUNT: 100,
+});
+
+const INFO = Object.freeze({
+  UNIT: '원',
+  COUNT: '개',
+  WEEK_DISCOUNT: 2023,
+  ORDER_MINIMUM: 10000,
+  GIFT_CONDITION: 120000,
+  GIFT_PRICE: 25000,
+  GIFT: '샴페인 1개',
+  NONE: '없음',
+  BADGE: {
+    SANTA: { PRICE: 20000, NAME: '산타' },
+    TREE: { PRICE: 10000, NAME: '트리' },
+    STAR: { PRICE: 5000, NAME: '별' },
+  },
+});
+
+const DISCOUNT = Object.freeze({
+  CHRISTMAS: '크리스마스 디데이 할인',
+  WEEK: '평일 할인',
+  WEEKEND: '주말 할인',
+  SPECIAL: '특별 할인',
+  GIFTS: '증정 이벤트',
+});
+
+const INPUT_MESSAGE = Object.freeze({
+  DATE: '12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n',
+  ORDER:
+    '주문하실 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
+});
+
+const OUTPUT_MESSAGE = Object.freeze({
+  INTRO: '안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.\n',
+  TITLE: {
+    PREVIEW: (date) => `12월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리보기!\n`,
+    ORDER_MENU: '<주문 메뉴>',
+    BEFORE_DISCOUNT: '<할인 전 총주문 금액>',
+    AFTER_DISCOUNT: '<할인 후 예상 결제 금액>',
+    GIFT: '<증정 메뉴>',
+    BENEFIT: '<혜택 내역>',
+    BENEFIT_AMOUNT: '<총혜택 금액>',
+    BADGE: '<12월 이벤트 배지>',
+  },
+});
+
+const ERROR_MESSAGE = Object.freeze({
+  INVALID_DATE: '[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.',
+  INVALID_ORDER: '[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.',
+});
+
+export {
+  MENU_KIND,
+  MENU,
+  DATE,
+  INFO,
+  DISCOUNT,
+  INPUT_MESSAGE,
+  OUTPUT_MESSAGE,
+  ERROR_MESSAGE,
+};

리뷰: 이번에 금액에 관한 숫자를 다루면서 `numeric seperator` 라는 방식을 알게되었는데
개인적으로 숫자 단위가 한눈에 들어와서 좋더라구요!

```suggestion
  GIFT_CONDITION: 120_000,
```
@@ -0,0 +1,10 @@
+const parseInput = userInput =>
+  userInput
+    .trim()
+    .split(',')
+    .map(item => {
+      const [name, quantity] = item.trim().split('-');
+      return [name, Number(quantity)];
+    });
+
+export default parseInput;

리뷰: 함수 로직이 여러줄이라 아래처럼 수정하는건 어떨까요??

```suggestion
const parseInput = (userInput) => {
  return userInput
    .trim()
    .split(",")
    .map((item) => {
      const [name, quantity] = item.trim().split("-");
      return [name, Number(quantity)];
    });
};
```

https://github.com/airbnb/javascript#whitespace--implicit-arrow-linebreak
https://github.com/airbnb/javascript#arrows--paren-wrap
@@ -0,0 +1,60 @@
+import { ERROR_MESSAGE, MENU, MENU_KIND } from './constants';
+
+const validateDate = date => {
+  if (!(date >= 1 && date <= 31)) {
+    throw new Error(ERROR_MESSAGE.INVALID_DATE);
+  }
+  if (typeof date !== 'number' || !Number.isInteger(date)) {
+    throw new Error(ERROR_MESSAGE.INVALID_DATE);
+  }
+};
+
+const validateName = menu => {
+  const checkedName = new Set();
+  menu.forEach(([name]) => {
+    if (!Object.keys(MENU).includes(name)) {
+      throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+    }
+    checkedName.add(name);
+  });
+};
+
+const validateDuplicate = menu => {
+  const checkedName = new Set();
+  menu.forEach(([name]) => {
+    if (checkedName.has(name)) {
+      throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+    }
+    checkedName.add(name);
+  });
+};
+
+const validateOnlyDrink = menu => {
+  const types = new Set(menu.map(([name]) => MENU[name].type));
+  if (types.size === 1 && types.has(MENU_KIND.DRINK)) {
+    throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+  }
+};
+
+const validateCount = menu => {
+  if (!menu.every(([, count]) => count >= 1)) {
+    throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+  }
+};
+
+const validateTotalCount = menu => {
+  const total = menu.reduce((acc, [, count]) => acc + count, 0);
+  if (total > 20) {
+    throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+  }
+};
+
+const validateOrder = order => {
+  validateName(order);
+  validateDuplicate(order);
+  validateOnlyDrink(order);
+  validateCount(order);
+  validateTotalCount(order);
+};
+
+export { validateDate, validateOrder };

리뷰: 사용하지 않는 파라미터는 언더바로 표현하는 관습이 있다고합니다!

```suggestion
  const total = menu.reduce((acc, [_, count]) => acc + count, 0);
```
@@ -0,0 +1,60 @@
+import { ERROR_MESSAGE, MENU, MENU_KIND } from './constants';
+
+const validateDate = date => {
+  if (!(date >= 1 && date <= 31)) {
+    throw new Error(ERROR_MESSAGE.INVALID_DATE);
+  }
+  if (typeof date !== 'number' || !Number.isInteger(date)) {
+    throw new Error(ERROR_MESSAGE.INVALID_DATE);
+  }
+};
+
+const validateName = menu => {
+  const checkedName = new Set();
+  menu.forEach(([name]) => {
+    if (!Object.keys(MENU).includes(name)) {
+      throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+    }
+    checkedName.add(name);
+  });
+};
+
+const validateDuplicate = menu => {
+  const checkedName = new Set();
+  menu.forEach(([name]) => {
+    if (checkedName.has(name)) {
+      throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+    }
+    checkedName.add(name);
+  });
+};
+
+const validateOnlyDrink = menu => {
+  const types = new Set(menu.map(([name]) => MENU[name].type));
+  if (types.size === 1 && types.has(MENU_KIND.DRINK)) {
+    throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+  }
+};
+
+const validateCount = menu => {
+  if (!menu.every(([, count]) => count >= 1)) {
+    throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+  }
+};
+
+const validateTotalCount = menu => {
+  const total = menu.reduce((acc, [, count]) => acc + count, 0);
+  if (total > 20) {
+    throw new Error(ERROR_MESSAGE.INVALID_ORDER);
+  }
+};
+
+const validateOrder = order => {
+  validateName(order);
+  validateDuplicate(order);
+  validateOnlyDrink(order);
+  validateCount(order);
+  validateTotalCount(order);
+};
+
+export { validateDate, validateOrder };

리뷰: 오 좋은 방법이네요.
@@ -0,0 +1,10 @@
+const parseInput = userInput =>
+  userInput
+    .trim()
+    .split(',')
+    .map(item => {
+      const [name, quantity] = item.trim().split('-');
+      return [name, Number(quantity)];
+    });
+
+export default parseInput;

리뷰: 이 패턴 자체는 상당히 좋네요. 그런데 아래와 같은 경우는 커버가 안 될 것 같기도 합니다.
```javascript
const item = '타파스-1-'
const [name, menu] = item.trim().split('-')
console.log(name, menu) // 타파스, 1
```
@@ -0,0 +1,58 @@
+import { DATE, DISCOUNT, INFO, MENU, MENU_KIND } from '../Util/constants';
+
+class Discount {
+  calculateDiscount(date, menu) {
+    const discount = {};
+    const addDisCount = (key, value) => {
+      if (value !== 0) discount[key] = value;
+    };
+
+    addDisCount(DISCOUNT.CHRISTMAS, this.isPeriod(date));
+    addDisCount(DISCOUNT.WEEK, this.isWeekday(date, menu));
+    addDisCount(DISCOUNT.WEEKEND, this.isWeekend(date, menu));
+    addDisCount(DISCOUNT.SPECIAL, this.isSpecialDay(date));
+    return discount;
+  }
+
+  isPeriod(date) {
+    let periodDiscount = 0;
+    if (date >= DATE.EVENT_START && date <= DATE.EVENT_END) {
+      periodDiscount +=
+        -DATE.PERIOD_DISCOUNT + (date - 1) * -DATE.PER_DAY_DISCOUNT;
+    }
+    return periodDiscount;
+  }
+
+  isWeekday(date, menu) {
+    let weekDisCount = 0;
+    if (!(date % 7 === 1 || date % 7 === 2)) {
+      let count = 0;
+      Object.keys(menu).forEach(name => {
+        if (MENU[name].kind === MENU_KIND.DESSERT) count += menu[name];
+      });
+      weekDisCount += -INFO.WEEK_DISCOUNT * count;
+    }
+    return weekDisCount;
+  }
+
+  isWeekend(date, menu) {
+    let weekendDisCount = 0;
+    if (date % 7 === 1 || date % 7 === 2) {
+      let count = 0;
+      Object.keys(menu).forEach(name => {
+        if (MENU[name].kind === MENU_KIND.MAIN) count += menu[name];
+      });
+      weekendDisCount += -INFO.WEEK_DISCOUNT * count;
+    }
+    return weekendDisCount;
+  }
+
+  isSpecialDay(date) {
+    let specialDayDiscount = 0;
+    if (DATE.SPECIAL_DATE.includes(date))
+      specialDayDiscount += -DATE.SPECIAL_DISCOUNT;
+    return specialDayDiscount;
+  }
+}
+
+export default Discount;

리뷰: 매우 사소한 부분이지만, 7과 1,2도 상수화를 진행하면 어떨까 싶네요!
1과 2가 무엇을 의미하는지 헷갈릴 수도 있고, 동일하게 수정할 수 있도록요 :)
@@ -0,0 +1,96 @@
+import { DISCOUNT, INFO, MENU } from '../Util/constants';
+import Discount from './Discount';
+
+class Client {
+  #date;
+  #order;
+
+  constructor(date, order) {
+    this.#date = date;
+    this.#order = order;
+  }
+
+  calculateBenefits() {
+    const discount = new Discount();
+    return discount.calculateDiscount(this.#date, this.#order);
+  }
+
+  getDate() {
+    return this.#date;
+  }
+
+  getOrderList() {
+    let output = '';
+    Object.entries(this.#order).forEach(([name, count]) => {
+      output += `${name} ${count}${INFO.COUNT}\n`;
+    });
+    return output;
+  }
+
+  getBeforeDiscount() {
+    let cost = 0;
+    Object.entries(this.#order).forEach(([name, count]) => {
+      cost += MENU[name].price * count;
+    });
+    return cost;
+  }
+
+  getGift() {
+    const isGift = this.getBeforeDiscount() >= INFO.GIFT_CONDITION;
+    return (isGift && INFO.GIFT) || INFO.NONE;
+  }
+
+  getBenefitList() {
+    const benefit = this.calculateBenefits();
+    if (!benefit || Object.keys(benefit).length === 0) {
+      return INFO.NONE;
+    }
+    let output = '';
+    Object.entries(benefit).forEach(([discount, cost]) => {
+      output += `${discount}: ${cost.toLocaleString()}${INFO.UNIT}\n`;
+    });
+    if (this.getGift() !== INFO.NONE)
+      output += `${DISCOUNT.GIFTS}: -${INFO.GIFT_PRICE.toLocaleString()}${
+        INFO.UNIT
+      }\n`;
+    return output;
+  }
+
+  getDiscountAmount() {
+    const benefit = this.calculateBenefits();
+    if (!benefit || Object.keys(benefit).length === 0) return 0;
+
+    let totalAmount = Object.values(benefit).reduce((acc, cur) => acc + cur);
+    if (this.getGift() !== INFO.NONE) totalAmount += -INFO.GIFT_PRICE;
+    return totalAmount;
+  }
+
+  getAfterDiscount() {
+    const beforeDiscount = this.getBeforeDiscount();
+    const discountAmount = this.getDiscountAmount();
+    let gift = 0;
+
+    if (this.getGift() !== INFO.NONE) {
+      gift = INFO.GIFT_PRICE;
+    }
+
+    const total = beforeDiscount + discountAmount + gift;
+    return total;
+  }
+
+  getEventBadge() {
+    const benefit = this.getDiscountAmount();
+
+    switch (true) {
+      case benefit <= -INFO.BADGE.SANTA.PRICE:
+        return INFO.BADGE.SANTA.NAME;
+      case benefit <= -INFO.BADGE.TREE.PRICE:
+        return INFO.BADGE.TREE.NAME;
+      case benefit <= -INFO.BADGE.STAR.PRICE:
+        return INFO.BADGE.STAR.NAME;
+      default:
+        return INFO.NONE;
+    }
+  }
+}
+export default Client;

리뷰: ```javascript
  getOrderList() {
    Object.entries(this.#order).map(([name, count]) =>`${name} ${count}${INFO.COUNT}`);
    return output.join('\n)';
  }
```

```javascript
Object.entries(this.#order)
    .map(([name, count]) => MENU[name].price * count)
    .reduce((acc, cur) => acc+cur, 0)
```
사소한 스타일 차이지만 이렇게 고칠 수도 있겠네요.
@@ -0,0 +1,96 @@
+import { DISCOUNT, INFO, MENU } from '../Util/constants';
+import Discount from './Discount';
+
+class Client {
+  #date;
+  #order;
+
+  constructor(date, order) {
+    this.#date = date;
+    this.#order = order;
+  }
+
+  calculateBenefits() {
+    const discount = new Discount();
+    return discount.calculateDiscount(this.#date, this.#order);
+  }
+
+  getDate() {
+    return this.#date;
+  }
+
+  getOrderList() {
+    let output = '';
+    Object.entries(this.#order).forEach(([name, count]) => {
+      output += `${name} ${count}${INFO.COUNT}\n`;
+    });
+    return output;
+  }
+
+  getBeforeDiscount() {
+    let cost = 0;
+    Object.entries(this.#order).forEach(([name, count]) => {
+      cost += MENU[name].price * count;
+    });
+    return cost;
+  }
+
+  getGift() {
+    const isGift = this.getBeforeDiscount() >= INFO.GIFT_CONDITION;
+    return (isGift && INFO.GIFT) || INFO.NONE;
+  }
+
+  getBenefitList() {
+    const benefit = this.calculateBenefits();
+    if (!benefit || Object.keys(benefit).length === 0) {
+      return INFO.NONE;
+    }
+    let output = '';
+    Object.entries(benefit).forEach(([discount, cost]) => {
+      output += `${discount}: ${cost.toLocaleString()}${INFO.UNIT}\n`;
+    });
+    if (this.getGift() !== INFO.NONE)
+      output += `${DISCOUNT.GIFTS}: -${INFO.GIFT_PRICE.toLocaleString()}${
+        INFO.UNIT
+      }\n`;
+    return output;
+  }
+
+  getDiscountAmount() {
+    const benefit = this.calculateBenefits();
+    if (!benefit || Object.keys(benefit).length === 0) return 0;
+
+    let totalAmount = Object.values(benefit).reduce((acc, cur) => acc + cur);
+    if (this.getGift() !== INFO.NONE) totalAmount += -INFO.GIFT_PRICE;
+    return totalAmount;
+  }
+
+  getAfterDiscount() {
+    const beforeDiscount = this.getBeforeDiscount();
+    const discountAmount = this.getDiscountAmount();
+    let gift = 0;
+
+    if (this.getGift() !== INFO.NONE) {
+      gift = INFO.GIFT_PRICE;
+    }
+
+    const total = beforeDiscount + discountAmount + gift;
+    return total;
+  }
+
+  getEventBadge() {
+    const benefit = this.getDiscountAmount();
+
+    switch (true) {
+      case benefit <= -INFO.BADGE.SANTA.PRICE:
+        return INFO.BADGE.SANTA.NAME;
+      case benefit <= -INFO.BADGE.TREE.PRICE:
+        return INFO.BADGE.TREE.NAME;
+      case benefit <= -INFO.BADGE.STAR.PRICE:
+        return INFO.BADGE.STAR.NAME;
+      default:
+        return INFO.NONE;
+    }
+  }
+}
+export default Client;

리뷰: 개인적인 의견이지만 이 경우는 `model`이 `view`의 지식을 너무 많이 알고 있는 것 같습니다. 어떤 `view`에서는 "{할인}: {금액}원"형식으로 받길 원할 수도 있지만 다른 `view`에서는 "{금액}만큼 {할인}을 받으셨어요"와 같이 출력하기를 원할 수 있다는 생각이 들고, 따라서 `domain` 영역이  `view` 영역에서 처리되어야 하는 게 아닐까 생각이 되네요.