ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,117 @@
+import { EVENT_NAMES } from '../constants/event.js';
+import { DESSERTS, MAIN_COURSES } from '../constants/menus.js';
+
+export const CHRISTMAS_D_DAY = {
+  getBenefit({ date }) {
+    const baseAmount = 1000;
+    const weightAmount = 100;
+
+    return {
+      amount: 1,
+      price: baseAmount + weightAmount * (date - 1),
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.CHRISTMAS_D_DAY,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isChristmasPeriod }) {
+    return isChristmasPeriod === true;
+  },
+};
+
+export const WEEKDAY = {
+  getBenefit({ orderCntByCategory }) {
+    const discountAmount = 2_023;
+    return {
+      amount: orderCntByCategory[DESSERTS],
+      price: discountAmount,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.WEEKDAY,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isWeekend, orderCategories }) {
+    return isWeekend === false && orderCategories.includes(DESSERTS);
+  },
+};
+
+export const WEEKEND = {
+  getBenefit({ orderCntByCategory }) {
+    const discountAmount = 2_023;
+    return {
+      amount: orderCntByCategory[MAIN_COURSES],
+      price: discountAmount,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.WEEKEND,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isWeekend, orderCategories }) {
+    return isWeekend === true && orderCategories.includes(MAIN_COURSES);
+  },
+};
+
+export const SPECIAL = {
+  getBenefit() {
+    const discountAmount = 1_000;
+    return {
+      amount: 1,
+      price: discountAmount,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.SPECIAL,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isSpecialDate }) {
+    return isSpecialDate === true;
+  },
+};
+
+export const GIFT = {
+  getBenefit() {
+    return {
+      menu: '샴페인',
+      amount: 1,
+      price: 25_000,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.GIFT,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ totalPrice }) {
+    return totalPrice >= 120_000;
+  },
+};
+
+export const ALL_EVENTS = Object.freeze({
+  CHRISTMAS_D_DAY,
+  WEEKDAY,
+  WEEKEND,
+  SPECIAL,
+  GIFT,
+});

리뷰: 1_000, 1000 중에 하나로 통일하면 좋을 것 같아요!
@@ -0,0 +1,94 @@
+import OrderItem from './OrderItem.js';
+
+import MESSAGES from '../constants/messages.js';
+
+import throwError from '../utils/throwError.js';
+import {
+  hasDuplicatedMenu,
+  hasOnlyBeverages,
+  isTotalAmountOfMenusValid,
+} from '../utils/validators.js';
+
+class Receipt {
+  #orders;
+
+  #orderItems;
+
+  constructor(orders) {
+    this.#validate(orders);
+    this.#orders = orders;
+    this.#orderItems = [];
+  }
+
+  #validate(orders) {
+    if (!isTotalAmountOfMenusValid(orders))
+      throwError(MESSAGES.errors.invalidOrders);
+
+    if (hasDuplicatedMenu(orders)) throwError(MESSAGES.errors.invalidOrders);
+
+    if (hasOnlyBeverages(orders)) throwError(MESSAGES.errors.invalidOrders);
+  }
+
+  generateOrders() {
+    this.#orderItems = this.#orders.map(
+      ({ menu, amount }) => new OrderItem(menu, amount),
+    );
+  }
+
+  get totalPrice() {
+    return this.#orderItems.reduce(
+      (totalPrice, orderItem) => totalPrice + orderItem.totalPrice,
+      0,
+    );
+  }
+
+  get receipt() {
+    const receipt = {};
+
+    this.#orderItems.forEach((orderItem) => {
+      const { category, menu, amount } = orderItem.item;
+
+      if (!Array.isArray(receipt[category])) {
+        receipt[category] = [];
+      }
+
+      receipt[category].push({ menu, amount });
+    });
+
+    return receipt;
+  }
+
+  get orderCategories() {
+    const categories = new Set();
+
+    this.#orderItems.forEach((orderItem) => {
+      const { category } = orderItem.item;
+      categories.add(category);
+    });
+
+    return [...categories];
+  }
+
+  get orderCntByCategory() {
+    const orderCnt = {};
+
+    this.#orderItems.forEach((orderItem) => {
+      const { category, amount } = orderItem.item;
+
+      if (!orderCnt[category]) orderCnt[category] = 0;
+
+      orderCnt[category] += amount;
+    });
+
+    return orderCnt;
+  }
+
+  get menus() {
+    return this.#orderItems.map((orderItem) => {
+      const { menu, amount } = orderItem.item;
+      return { menu, amount };
+    });
+  }
+}
+
+export default Receipt;

리뷰: 혹시 여기 공백이 있는 이유가 있을까요??
@@ -0,0 +1,94 @@
+import OrderItem from './OrderItem.js';
+
+import MESSAGES from '../constants/messages.js';
+
+import throwError from '../utils/throwError.js';
+import {
+  hasDuplicatedMenu,
+  hasOnlyBeverages,
+  isTotalAmountOfMenusValid,
+} from '../utils/validators.js';
+
+class Receipt {
+  #orders;
+
+  #orderItems;
+
+  constructor(orders) {
+    this.#validate(orders);
+    this.#orders = orders;
+    this.#orderItems = [];
+  }
+
+  #validate(orders) {
+    if (!isTotalAmountOfMenusValid(orders))
+      throwError(MESSAGES.errors.invalidOrders);
+
+    if (hasDuplicatedMenu(orders)) throwError(MESSAGES.errors.invalidOrders);
+
+    if (hasOnlyBeverages(orders)) throwError(MESSAGES.errors.invalidOrders);
+  }
+
+  generateOrders() {
+    this.#orderItems = this.#orders.map(
+      ({ menu, amount }) => new OrderItem(menu, amount),
+    );
+  }
+
+  get totalPrice() {
+    return this.#orderItems.reduce(
+      (totalPrice, orderItem) => totalPrice + orderItem.totalPrice,
+      0,
+    );
+  }
+
+  get receipt() {
+    const receipt = {};
+
+    this.#orderItems.forEach((orderItem) => {
+      const { category, menu, amount } = orderItem.item;
+
+      if (!Array.isArray(receipt[category])) {
+        receipt[category] = [];
+      }
+
+      receipt[category].push({ menu, amount });
+    });
+
+    return receipt;
+  }
+
+  get orderCategories() {
+    const categories = new Set();
+
+    this.#orderItems.forEach((orderItem) => {
+      const { category } = orderItem.item;
+      categories.add(category);
+    });
+
+    return [...categories];
+  }
+
+  get orderCntByCategory() {
+    const orderCnt = {};
+
+    this.#orderItems.forEach((orderItem) => {
+      const { category, amount } = orderItem.item;
+
+      if (!orderCnt[category]) orderCnt[category] = 0;
+
+      orderCnt[category] += amount;
+    });
+
+    return orderCnt;
+  }
+
+  get menus() {
+    return this.#orderItems.map((orderItem) => {
+      const { menu, amount } = orderItem.item;
+      return { menu, amount };
+    });
+  }
+}
+
+export default Receipt;

리뷰: 구조분해 할당 잘 쓰십니당! 👍
@@ -0,0 +1,11 @@
+const deepFreeze = (object) => {
+  Object.keys(object).forEach((prop) => {
+    if (typeof object[prop] === 'object' && !Object.isFrozen(object[prop])) {
+      deepFreeze(object[prop]);
+    }
+  });
+
+  return Object.freeze(object);
+};
+
+export default deepFreeze;

리뷰: deepFreeze 꼼꼼하십니다~ 👍👍👍
@@ -0,0 +1,36 @@
+import EventCalendar from '../../src/domain/EventCalendar';
+import { CHRISTMAS_D_DAY, GIFT, SPECIAL } from '../../src/domain/Events';
+
+import {
+  EVENT_MONTH,
+  EVENT_NAMES,
+  EVENT_YEAR,
+} from '../../src/constants/event';
+import { MAIN_COURSES } from '../../src/constants/menus';
+
+describe('EventCalendar 테스트', () => {
+  test('getEventBenefit 기능 테스트', () => {
+    // given
+    const YEAR = EVENT_YEAR;
+    const MONTH = EVENT_MONTH;
+    const DATE = 25;
+    const TOTAL_PRICE = 130_000;
+    const ORDER_CATEGORIES = [MAIN_COURSES];
+
+    const RESULT = {
+      [EVENT_NAMES.CHRISTMAS_D_DAY]: CHRISTMAS_D_DAY.getEvent().getBenefit,
+      [EVENT_NAMES.SPECIAL]: SPECIAL.getEvent().getBenefit,
+      [EVENT_NAMES.GIFT]: GIFT.getEvent().getBenefit,
+    };
+
+    // when
+    const eventCalendar = new EventCalendar(YEAR, MONTH, DATE);
+    eventCalendar.setAvailableEvents({
+      totalPrice: TOTAL_PRICE,
+      orderCategories: ORDER_CATEGORIES,
+    });
+
+    //then
+    expect(eventCalendar.availableEvents).toEqual(RESULT);
+  });
+});

리뷰: 실제로 모듈화된 상수의 구조나 값이 바뀌면 테스트의 결과가 바뀌는 걸 경험하고 의문을 품긴 했는데, 독립성을 지키지 않은 행동이었군요! 덕분에 깨달았습니다. 좋은 피드백 감사해요.🙏
@@ -0,0 +1,117 @@
+import { EVENT_NAMES } from '../constants/event.js';
+import { DESSERTS, MAIN_COURSES } from '../constants/menus.js';
+
+export const CHRISTMAS_D_DAY = {
+  getBenefit({ date }) {
+    const baseAmount = 1000;
+    const weightAmount = 100;
+
+    return {
+      amount: 1,
+      price: baseAmount + weightAmount * (date - 1),
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.CHRISTMAS_D_DAY,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isChristmasPeriod }) {
+    return isChristmasPeriod === true;
+  },
+};
+
+export const WEEKDAY = {
+  getBenefit({ orderCntByCategory }) {
+    const discountAmount = 2_023;
+    return {
+      amount: orderCntByCategory[DESSERTS],
+      price: discountAmount,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.WEEKDAY,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isWeekend, orderCategories }) {
+    return isWeekend === false && orderCategories.includes(DESSERTS);
+  },
+};
+
+export const WEEKEND = {
+  getBenefit({ orderCntByCategory }) {
+    const discountAmount = 2_023;
+    return {
+      amount: orderCntByCategory[MAIN_COURSES],
+      price: discountAmount,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.WEEKEND,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isWeekend, orderCategories }) {
+    return isWeekend === true && orderCategories.includes(MAIN_COURSES);
+  },
+};
+
+export const SPECIAL = {
+  getBenefit() {
+    const discountAmount = 1_000;
+    return {
+      amount: 1,
+      price: discountAmount,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.SPECIAL,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ isSpecialDate }) {
+    return isSpecialDate === true;
+  },
+};
+
+export const GIFT = {
+  getBenefit() {
+    return {
+      menu: '샴페인',
+      amount: 1,
+      price: 25_000,
+    };
+  },
+
+  getEvent() {
+    return {
+      name: EVENT_NAMES.GIFT,
+      getBenefit: this.getBenefit,
+    };
+  },
+
+  isEventAvailable({ totalPrice }) {
+    return totalPrice >= 120_000;
+  },
+};
+
+export const ALL_EVENTS = Object.freeze({
+  CHRISTMAS_D_DAY,
+  WEEKDAY,
+  WEEKEND,
+  SPECIAL,
+  GIFT,
+});

리뷰: 좋은 방법이네요! 감사합니다. :)
@@ -0,0 +1,83 @@
+import deepFreeze from '../utils/deepFreeze.js';
+import { EVENT_MONTH } from './event.js';
+
+const MESSAGES = deepFreeze({
+  // InputView
+  inputs: {
+    getVisitDate: `${EVENT_MONTH}월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n`,
+    getOrders:
+      '주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
+  },
+
+  // OutputView
+  outputs: {
+    sayHi: `안녕하세요! 우테코 식당 ${EVENT_MONTH}월 이벤트 플래너입니다.`,
+
+    eventPreview: (date) =>
+      `${EVENT_MONTH}월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!\n`,
+
+    menus: {
+      title: '<주문 메뉴>',
+      printMsg: (menus) =>
+        menus.reduce(
+          (acc, { menu, amount }) => `${acc}${menu} ${amount}개\n`,
+          '',
+        ),
+    },
+
+    originalPrice: {
+      title: '<할인 전 총주문 금액>',
+      printMsg: (price) => `${price.toLocaleString()}원\n`,
+    },
+
+    gift: {
+      title: '<증정 메뉴>',
+      printMsg: (gift) => {
+        if (!gift) return '없음\n';
+
+        return `${gift.menu} ${gift.amount}개\n`;
+      },
+    },
+
+    benefits: {
+      title: '<혜택 내역>',
+      printMsg: (benefits) => {
+        if (!benefits) return '없음\n';
+
+        return Object.entries(benefits).reduce(
+          (acc, [eventName, { amount, price }]) =>
+            `${acc}${eventName}: -${(amount * price).toLocaleString()}원\n`,
+          '',
+        );
+      },
+    },
+
+    totalBenefitMoney: {
+      title: '<총혜택 금액>',
+      printMsg: (money) => {
+        if (!money) return '0원\n';
+
+        return `-${money.toLocaleString()}원\n`;
+      },
+    },
+
+    payment: {
+      title: '<할인 후 예상 결제 금액>',
+      printMsg: (money) => `${money.toLocaleString()}원\n`,
+    },
+
+    badge: {
+      title: '<12월 이벤트 배지>',
+      printMsg: (badge) => `${badge}`,
+    },
+  },
+
+  // Error
+  errors: {
+    prefix: '[ERROR]',
+    invalidDate: '유효하지 않은 날짜입니다. 다시 입력해 주세요.',
+    invalidOrders: '유효하지 않은 주문입니다. 다시 입력해 주세요.',
+  },
+});
+
+export default MESSAGES;

리뷰: 저도 고민했던 부분인데, 다른 달에 이벤트를 개최한다면 상수로 빼는 게 좋을 것 같아 이렇게 했습니다!
@@ -0,0 +1,83 @@
+import deepFreeze from '../utils/deepFreeze.js';
+import { EVENT_MONTH } from './event.js';
+
+const MESSAGES = deepFreeze({
+  // InputView
+  inputs: {
+    getVisitDate: `${EVENT_MONTH}월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n`,
+    getOrders:
+      '주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n',
+  },
+
+  // OutputView
+  outputs: {
+    sayHi: `안녕하세요! 우테코 식당 ${EVENT_MONTH}월 이벤트 플래너입니다.`,
+
+    eventPreview: (date) =>
+      `${EVENT_MONTH}월 ${date}일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!\n`,
+
+    menus: {
+      title: '<주문 메뉴>',
+      printMsg: (menus) =>
+        menus.reduce(
+          (acc, { menu, amount }) => `${acc}${menu} ${amount}개\n`,
+          '',
+        ),
+    },
+
+    originalPrice: {
+      title: '<할인 전 총주문 금액>',
+      printMsg: (price) => `${price.toLocaleString()}원\n`,
+    },
+
+    gift: {
+      title: '<증정 메뉴>',
+      printMsg: (gift) => {
+        if (!gift) return '없음\n';
+
+        return `${gift.menu} ${gift.amount}개\n`;
+      },
+    },
+
+    benefits: {
+      title: '<혜택 내역>',
+      printMsg: (benefits) => {
+        if (!benefits) return '없음\n';
+
+        return Object.entries(benefits).reduce(
+          (acc, [eventName, { amount, price }]) =>
+            `${acc}${eventName}: -${(amount * price).toLocaleString()}원\n`,
+          '',
+        );
+      },
+    },
+
+    totalBenefitMoney: {
+      title: '<총혜택 금액>',
+      printMsg: (money) => {
+        if (!money) return '0원\n';
+
+        return `-${money.toLocaleString()}원\n`;
+      },
+    },
+
+    payment: {
+      title: '<할인 후 예상 결제 금액>',
+      printMsg: (money) => `${money.toLocaleString()}원\n`,
+    },
+
+    badge: {
+      title: '<12월 이벤트 배지>',
+      printMsg: (badge) => `${badge}`,
+    },
+  },
+
+  // Error
+  errors: {
+    prefix: '[ERROR]',
+    invalidDate: '유효하지 않은 날짜입니다. 다시 입력해 주세요.',
+    invalidOrders: '유효하지 않은 주문입니다. 다시 입력해 주세요.',
+  },
+});
+
+export default MESSAGES;

리뷰: 앗, 놓친 부분이네요! 짚어주셔서 감사합니다.🙏
@@ -0,0 +1,63 @@
+import { FRIDAY, SATURDAY, SPECIAL_DATES } from '../constants/calendar.js';
+import { EVENT_PERIOD } from '../constants/event.js';
+import { ALL_EVENTS } from './Events.js';
+
+class EventCalendar {
+  #year;
+
+  #month;
+
+  #date;
+
+  #availableEvents;
+
+  constructor(year, month, date) {
+    this.#year = year;
+    this.#month = month;
+    this.#date = date;
+    this.#availableEvents = {};
+  }
+
+  async setAvailableEvents({ totalPrice, orderCategories }) {
+    const state = this.#getState(totalPrice, orderCategories);
+
+    await Object.values(ALL_EVENTS).forEach((event) => {
+      const { name, getBenefit } = event.getEvent();
+
+      if (event.isEventAvailable(state))
+        this.#availableEvents[name] = getBenefit;
+    });
+  }
+
+  #getState(totalPrice, orderCategories) {
+    return {
+      isWeekend: this.#isWeekend(),
+      isSpecialDate: this.#isSpecialDate(),
+      isChristmasPeriod: this.#isChristmasPeriod(),
+      totalPrice,
+      orderCategories,
+    };
+  }
+
+  #isWeekend() {
+    const dayOfWeek = new Date(
+      `${this.#year}-${this.#month}-${this.#date}`,
+    ).getDay();
+    return dayOfWeek === FRIDAY || dayOfWeek === SATURDAY;
+  }
+
+  #isSpecialDate() {
+    return SPECIAL_DATES.includes(this.#date);
+  }
+
+  #isChristmasPeriod() {
+    const { startDate, endDate } = EVENT_PERIOD;
+    return this.#date >= startDate && this.#date <= endDate;
+  }
+
+  get availableEvents() {
+    return this.#availableEvents;
+  }
+}
+
+export default EventCalendar;

리뷰: 맞아요! 실수를 줄이기 위해서 위와 같은 방식으로 했습니다. :)
@@ -0,0 +1,56 @@
+package christmas.domain;
+
+
+import static christmas.domain.Badge.NONE;
+import static christmas.domain.Badge.SANTA;
+import static christmas.domain.Badge.STAR;
+import static christmas.domain.Badge.TREE;
+
+import java.util.Map;
+
+public class Benefits {
+    private final Map<Event, Integer> benefits;
+
+    public Benefits(Map<Event, Integer> benefits) {
+        this.benefits = benefits;
+    }
+
+    public int calculateTotalBenefitAmount() {
+        return benefits.values()
+                .stream()
+                .mapToInt(amount -> amount)
+                .sum();
+    }
+
+    public Badge toEventBadge(int totalBenefit) {
+        if (isStarBadge(totalBenefit)) {
+            return STAR;
+        }
+        if (isTreeBadge(totalBenefit)) {
+            return TREE;
+        }
+        if (isSantaBadge(totalBenefit)) {
+            return SANTA;
+        }
+        return NONE;
+    }
+
+    private boolean isStarBadge(int totalBenefit) {
+        return totalBenefit >= STAR.getAmount() && totalBenefit < TREE.getAmount();
+    }
+
+    private boolean isTreeBadge(int totalBenefit) {
+        return totalBenefit >= TREE.getAmount() && totalBenefit < SANTA.getAmount();
+    }
+
+    private boolean isSantaBadge(int totalBenefit) {
+        return totalBenefit >= SANTA.getAmount();
+    }
+
+    public Map<Event, Integer> getBenefits() {
+        return benefits;
+    }
+}
+
+
+

리뷰: is{BadgeType}Badge 함수를 사용해서 하나씩 비교하는 함수를 만들게 되면 배지가 생기거나 삭제할때마다 메소드 관리를 계속 해줘야한다는 번거로움이 생길 수 있을 거 같아요!!
@@ -0,0 +1,48 @@
+package christmas.domain;
+
+public enum Menu {
+    MUSHROOM_SOUP("양송이수프", 6_000, "애피타이저"),
+    TAPAS("타파스", 5_500, "애피타이저"),
+    CAESAR_SALAD("시저샐러드", 8_000, "애피타이저"),
+    T_BONE_STEAK("티본스테이크", 55_000, "메인"),
+    BBQ_RIBS("바비큐립", 54_000, "메인"),
+    SEAFOOD_PASTA("해산물파스타", 35_000, "메인"),
+    CHRISTMAS_PASTA("크리스마스파스타", 25_000, "메인"),
+    CHOCOLATE_CAKE("초코케이크", 15_000, "디저트"),
+    ICE_CREAM("아이스크림", 5_000, "디저트"),
+    ZERO_COKE("제로콜라", 3_000, "음료"),
+    RED_WINE("레드와인", 60_000, "음료"),
+    CHAMPAGNE("샴페인", 25_000, "음료"),
+    NONE("없음", 0, "없음");
+
+    private final String menuName;
+    private final int price;
+    private final String type;
+
+    Menu(String menuName, int price, String type) {
+        this.menuName = menuName;
+        this.price = price;
+        this.type = type;
+    }
+
+    public static Menu from(String menuName) {
+        for (Menu menu : values()) {
+            if (menu.getMenuName().equals(menuName)) {
+                return menu;
+            }
+        }
+        return NONE;
+    }
+
+    public String getMenuName() {
+        return menuName;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public String getType() {
+        return type;
+    }
+}

리뷰: 음식 Type의 경우 공통되는 항목 (에피타이저/메인/디저트/음료)에 대해서 별도로 관리해도 좋을거 같아요
@@ -0,0 +1,76 @@
+package christmas.controller;
+
+import christmas.domain.Badge;
+import christmas.domain.Benefits;
+import christmas.domain.Convertor;
+import christmas.domain.Date;
+import christmas.domain.EventManager;
+import christmas.domain.Orders;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+
+public class EventController {
+    public void start() {
+        OutputView.printWelcome();
+        Date date = createDate();
+        Orders orders = createOrders();
+        int totalOrderAmount = createTotalOrderAmount(orders);
+        OutputView.printGift(orders.hasGift());
+        Benefits benefits = createBenefits(orders, date);
+        int totalBenefitAmount = createTotalBenefitAmount(benefits);
+        createPaymentAmount(orders, totalOrderAmount, totalBenefitAmount);
+        createBadge(benefits, totalBenefitAmount);
+    }
+
+    private Date createDate() {
+        while (true) {
+            try {
+                Date date = new Date(InputView.readDate());
+                OutputView.printPreview(date);
+                return date;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e);
+            }
+        }
+    }
+
+    private Orders createOrders() {
+        while (true) {
+            try {
+                Orders orders = Convertor.toOrders(InputView.readOrder());
+                OutputView.printOrders(orders);
+                return orders;
+            } catch (IllegalArgumentException e) {
+                OutputView.printError(e);
+            }
+        }
+    }
+
+    private int createTotalOrderAmount(Orders orders) {
+        int totalOrderAmount = orders.calculateTotalAmount();
+        OutputView.printTotalOrderAmount(totalOrderAmount);
+        return totalOrderAmount;
+    }
+
+    private Benefits createBenefits(Orders orders, Date date) {
+        Benefits benefits = EventManager.toBenefits(orders, date);
+        OutputView.printBenefitDetail(benefits);
+        return benefits;
+    }
+
+    private int createTotalBenefitAmount(Benefits benefits) {
+        int totalBenefitAmount = benefits.calculateTotalBenefitAmount();
+        OutputView.printTotalBenefitAmount(totalBenefitAmount);
+        return totalBenefitAmount;
+    }
+
+    private void createPaymentAmount(Orders orders, int totalOrderAmount, int totalBenefitAmount) {
+        int paymentAmount = orders.calculatePaymentAmount(totalOrderAmount, totalBenefitAmount);
+        OutputView.printPaymentAmount(paymentAmount);
+    }
+
+    private void createBadge(Benefits benefits, int totalBenefitAmount) {
+        Badge badge = benefits.toEventBadge(totalBenefitAmount);
+        OutputView.printEventBadge(badge);
+    }
+}

리뷰: 입력 검증을 후 재입력 받는것을 inputview에서 진행하는것에 대해 어떻게 생각하시나요???
구현 하면서 고민했었는데 어느 방향으로 구현해야 더 나은지 잘 모르겠더라구요