ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,38 @@
+package christmas.domain;
+
+import static christmas.domain.Badge.NONE;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+class BenefitsTest {
+    Benefits benefits;
+
+    @Test
+    @DisplayName("혜택의 총합 계산")
+    void calculateTotalBenefit() {
+        int totalBenefit = benefits.calculateTotalBenefitAmount();
+        assertThat(totalBenefit).isEqualTo(31246);
+    }
+
+    @ParameterizedTest
+    @DisplayName("총 혜택 금액에 따른 뱃지 지급")
+    @CsvSource(value = {"5500,별", "10500,트리", "21000,산타"})
+    void toEventBadge(int input, String expect) {
+        Badge badge = benefits.toEventBadge(input);
+        if (badge != NONE) {
+            assertThat(badge.getType()).isEqualTo(expect);
+        }
+    }
+
+    @BeforeEach
+    void setUp() {
+        Date date = new Date(3);
+        Orders orders = Convertor.toOrders("티본스테이크-1,바비큐립-1,초코케이크-2,제로콜라-1");
+        benefits = EventManager.toBenefits(orders, date);
+    }
+}
\ No newline at end of file

리뷰: setUp 함수의 경우 가독성(?)을 위해 상단에 위치시키면 더 좋을거같아요
@@ -0,0 +1,70 @@
+# 기능 구현 목록
+
+## view
+
+- [x] **InputView**
+    - 방문 날짜를 입력 받아 정수로 반환한다.
+        - 숫자 이외의 값을 입력시 예외를 발생 시킨다.
+    - 주문 내용을 입력 받아 문자열로 반환한다.
+- [x] **Notice**
+    - InputView와 OutputView에서 사용하는 메시지를 저장한다.
+- [x] **OutputView**
+    - 에러 발생시 에러 메시지를 출력한다.
+    - 인사말을 출력한다.
+    - 혜택 미리보기 내용을 출력한다.
+        - 데이터를 받아 출력 형식에 맞게 가공한다.
+
+## domain
+
+- [x] **Date**
+    - 입력 받은 날짜를 저장한다.
+        - 1-31 이외의 날짜를 받으면 예외를 발생 시킨다.
+    - 25일 전인지 확인한다.
+    - 주말인지 확인한다.
+    - 이벤트 달력상 별이 있는지 확인한다.
+- [x] **Convertor**
+    - 주문 내용이 담긴 문자열을 Order 객체로 구성된 Orders 객체로 변환한다.
+        - 콤마(,) 단위로 Order 객체를 생성한다.
+        - 메뉴와 주문량을 Order 객체에 저장한다.
+    - 예시와 다른 형식의 주문 내용을 받으면 예외를 발생시킨다.
+- [x] **Order**
+    - 입력 받은 메뉴가 메뉴판에 없으면 예외를 발생시킨다.
+    - 메뉴별 주문량이 1개보다 작으면 예외를 발생시킨다.
+    - 주문별 금액을 계산한다.
+    - 메뉴의 종류를 확인한다.
+    - 메뉴 종류별 갯수를 센다.
+- [x] **Orders**
+    - 메뉴의 중복여부를 확인하여 예외를 발생시킨다.
+    - 총 주문량이 20개가 넘으면 예외를 발생시킨다.
+    - 음료만 주문하면 예외를 발생시킨다.
+    - 총 주문금액을 계산한다.
+    - 최종 결제 금액을 계산한다.
+        - 결제 금액이 음수일 경우 0을 반환한다.
+    - 이벤트 참여 가능 여부를 확인한다.
+    - 사은품 증정 여부를 확인한다.
+    - 메뉴 종류별 주문량을 계산한다.
+- [x] **EventManager**
+    - 각 이벤트별 적용여부를 확인하고 혜택 금액을 계산한다.
+    - 혜택 내역을 담은 Benefits 객체를 만든다.
+- [x] **Benefits**
+    - 총 혜택 금액을 계산한다.
+    - 혜택 금액에 따른 뱃지 객체를 만든다.
+- [x] **Menu**
+    - 메뉴 이름, 가격, 종류를 저장한다.
+    - 메뉴 이름을 받아 일치하는 메뉴 객체를 반환한다.
+        - 일치하는 메뉴가 없다면 NONE을 반환한다.
+- [x] **Event**
+    - 이벤트 이름, 초기 적용 금액, 단위당 적용 금액을 저장한다.
+- [x] **Badge**
+    - 뱃지 종류, 기준 금액을 저장한다.
+- [x] **Error**
+    - 에러 메시지 출력에 사용되는 내용을 저장한다.
+
+## controller
+
+- [x] **EventController**
+    - 예외 발생시 해당 부분을 반복시킨다.
+    - view의 내용을 domain에 전달한다.
+    - domain의 내용을 view에 전달한다.
+ 
+  

리뷰: 코드리뷰에 앞서, 객체 기능별로 분리되어 있어 전체 비즈니스 로직 및 각 객체의 역할을 파악하기 편리했어요 👍
@@ -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);
+    }
+}

리뷰: 개행도 하나의 컨벤션이다!

조금 더 기능별로 분리해서 개행으로 구분한다면 더 읽기 편할 것 같아요.
@@ -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;
+    }
+}
+
+
+

리뷰: 만약 뱃지가 100개라면?, 1000개라면?
뱃지의 갯수가 많아지는 상황에서 모든 로직을 하드코딩 해야하는 상황이 생길 것 같아요.

이 부분도, 하드코딩이 아니라, 뱃지의 속성에 따라 구분할 수 있도록 방법을 모색해보시면, 좋을 것 같아요!
@@ -0,0 +1,51 @@
+package christmas.domain;
+
+import static christmas.domain.Error.INVALID_DATE;
+import static java.time.DayOfWeek.FRIDAY;
+import static java.time.DayOfWeek.SATURDAY;
+import static java.time.DayOfWeek.SUNDAY;
+
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+
+public class Date {
+    private static final int MIN_DATE = 1;
+    private static final int MAX_DATE = 31;
+    private static final int THIS_YEAR = 2023;
+    private static final int THIS_MONTH = 12;
+    private static final int CHRISTMAS_DATE = 25;
+    private final int value;
+
+    public Date(int value) {
+        validateRange(value);
+        this.value = value;
+    }
+
+    public boolean isDayBeforeChristmas() {
+        return value <= CHRISTMAS_DATE;
+    }
+
+    public boolean isWeekend() {
+        DayOfWeek dayOfWeek = createDayOfWeek();
+        return dayOfWeek == FRIDAY || dayOfWeek == SATURDAY;
+    }
+
+    public boolean isStarDay() {
+        return createDayOfWeek() == SUNDAY || value == CHRISTMAS_DATE;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    private DayOfWeek createDayOfWeek() {
+        LocalDate localDate = LocalDate.of(THIS_YEAR, THIS_MONTH, value);
+        return localDate.getDayOfWeek();
+    }
+
+    private void validateRange(int value) {
+        if (value > MAX_DATE || value < MIN_DATE) {
+            throw new IllegalArgumentException(INVALID_DATE.getMessage());
+        }
+    }
+}
\ No newline at end of file

리뷰: 해당 이벤트는 1월에도 선물을 증정하면서, `재사용 예정`임을 은연중에 암시했어요.
int 타입 하드코딩이 아니라, LocalDate를 활용해 2023년 12월과 2024년 1월을 끼워넣어서 바로 사용할 수 있도록 설계하면, 더 유연한 설계가 되었을 것이라 생각되어요!
@@ -0,0 +1,33 @@
+package christmas.domain;
+
+import static christmas.domain.Menu.CHAMPAGNE;
+
+public enum Event {
+    CHRISTMAS_D_DAY("크리스마스 디데이 할인", 1_000, 100),
+    WEEKDAY("평일 할인", 0, 2_023),
+    WEEKEND("주말 할인", 0, 2_023),
+    SPECIAL("특별 할인", 1_000, 0),
+    GIFT("증정 이벤트", CHAMPAGNE.getPrice(), 0);
+
+    private final String eventName;
+    private final int initPrice;
+    private final int unitPrice;
+
+    Event(String eventName, int initPrice, int unitPrice) {
+        this.eventName = eventName;
+        this.initPrice = initPrice;
+        this.unitPrice = unitPrice;
+    }
+
+    public String getEventName() {
+        return eventName;
+    }
+
+    public int getInitPrice() {
+        return initPrice;
+    }
+
+    public int getUnitPrice() {
+        return unitPrice;
+    }
+}

리뷰: `initPrice` 직관적으로 변수명만 봤을 때, 어떤 int값을 담는지 모호한 감이 있어요
@@ -0,0 +1,60 @@
+package christmas.domain;
+
+import static christmas.domain.Event.CHRISTMAS_D_DAY;
+import static christmas.domain.Event.GIFT;
+import static christmas.domain.Event.SPECIAL;
+import static christmas.domain.Event.WEEKDAY;
+import static christmas.domain.Event.WEEKEND;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public class EventManager {
+
+    public static Benefits toBenefits(Orders orders, Date date) {
+        Map<Event, Integer> benefits = new EnumMap<>(Event.class);
+        if (orders.isEligible()) {
+            benefits.put(CHRISTMAS_D_DAY, calculateDDayDiscount(date));
+            benefits.put(WEEKDAY, calculateWeekdayDiscount(orders, date));
+            benefits.put(WEEKEND, calculateWeekendDiscount(orders, date));
+            benefits.put(SPECIAL, calculateSpecialDiscount(date));
+            benefits.put(GIFT, calculateGiftAmount(orders));
+        }
+        return new Benefits(benefits);
+    }
+
+    private static int calculateDDayDiscount(Date date) {
+        if (date.isDayBeforeChristmas()) {
+            return CHRISTMAS_D_DAY.getInitPrice() + ((date.getValue() - 1) * CHRISTMAS_D_DAY.getUnitPrice());
+        }
+        return 0;
+    }
+
+    private static int calculateWeekdayDiscount(Orders orders, Date date) {
+        if (!date.isWeekend()) {
+            return orders.countDesserts() * WEEKDAY.getUnitPrice();
+        }
+        return 0;
+    }
+
+    private static int calculateWeekendDiscount(Orders orders, Date date) {
+        if (date.isWeekend()) {
+            return orders.countMains() * WEEKEND.getUnitPrice();
+        }
+        return 0;
+    }
+
+    private static int calculateSpecialDiscount(Date date) {
+        if (date.isStarDay()) {
+            return SPECIAL.getInitPrice();
+        }
+        return 0;
+    }
+
+    private static int calculateGiftAmount(Orders orders) {
+        if (orders.hasGift()) {
+            return GIFT.getInitPrice();
+        }
+        return 0;
+    }
+}

리뷰: 오늘 일수 * 100 + 900 으로 간단하게 표현할 수 있답니다 >_<
@@ -0,0 +1,25 @@
+package christmas.view;
+
+enum Notice {
+    WELCOME("안녕하세요! 우테코 식당 12월 이벤트 플래너입니다."),
+    ASK_DATE_OF_VISIT("12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)"),
+    ASK_MENU_AND_COUNT("주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)"),
+    PREVIEW("12월 %s일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!%n"),
+    ORDER_MENU("\n<주문 메뉴>"),
+    TOTAL_ORDER_AMOUNT("\n<할인 전 총주문 금액>"),
+    GIFT_MENU("\n<증정 메뉴>"),
+    BENEFIT_LIST("\n<혜택 내역>"),
+    TOTAL_BENEFIT_AMOUNT("\n<총혜택 금액>"),
+    TOTAL_PAYMENT_AMOUNT("\n<할인 후 예상 결제 금액>"),
+    EVENT_BADGE("\n<12월 이벤트 배지>");
+
+    private final String message;
+
+    Notice(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

리뷰: 저도 @ddrongy님의 의견에 동의합니다!,그렇다면 1월로 바뀌었을때, (2024년 1월)
System Constraint도 자동으로 바뀌도록(LocalDate..?) 설정하는 방향으로 수정도 필요할 것 같아요!
@@ -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에서 받는다면 치우님 말씀대로 하는게 맞다고 생각합니다! 하지만 날짜가 1 - 31일 중 하나여야 한다는 것에 대한 검증은 VIew가 아니라 Domain 에서 이뤄지기 때문에 결과적으로 View와 Domain 양쪽에서 검증이 이뤄져야 합니다. 따라서 양쪽의 통로 역할을 하는 controller에서 진행하는 것이 맞다고 판단했습니다~
@@ -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);
+    }
+}

리뷰: e라고 사용하는 것은 자바를 사용하는 사람들끼리의 약속인거 같은데 이부분은 한번 대화를 나눠보면 좋을거 같습니다!
@@ -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);
+    }
+}

리뷰: > 전체적으로 create라는 메서드명을 사용했는데 create가 객체 생성의 느낌을 주는거같아서 다른 이름으로 메서드명을 작성해보는건 어떨까요 ?

실제로 해당 메서드가 객체를 생성해서 리턴하는 기능을 하고 있어서 create 라고 명명했는데 혹시 다른 좋은 이름이 있을까요~?
@@ -0,0 +1,24 @@
+package christmas.domain;
+
+public enum Badge {
+    STAR("별", 5_000),
+    TREE("트리", 10_000),
+    SANTA("산타", 20_000),
+    NONE("없음", 0);
+
+    private final String type;
+    private final int amount;
+
+    Badge(String type, int amount) {
+        this.type = type;
+        this.amount = amount;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public int getAmount() {
+        return amount;
+    }
+}

리뷰: 인텔리제이 자동 생성기능으로 만든건데 이 부분에 대해서는 생각해보지 못했네요 ㅜㅜ private로 지정하는게 맞는거 같습니다!
@@ -0,0 +1,37 @@
+import CONSTANTS from '../constants/constants.js';
+import ERROR from '../constants/error.js';
+
+class NumbersValidator {
+  static validateNumbers(numbers) {
+    const validators = [
+      this.#validateEmpty,
+      this.#validateNegative,
+      this.#validateNaN,
+      this.#validateLength,
+      this.#validateDuplicated,
+    ];
+    validators.forEach(validator => validator(numbers));
+  }
+
+  static #validateLength(numbers) {
+    if (numbers.length !== CONSTANTS.number.numberSize) throw new Error(ERROR.numbers.length);
+  }
+
+  static #validateNaN(numbers) {
+    if (Number.isNaN(Number(numbers))) throw new Error(ERROR.numbers.notANumber);
+  }
+
+  static #validateNegative(numbers) {
+    if (Number(numbers) < CONSTANTS.number.zero) throw new Error(ERROR.numbers.negative);
+  }
+
+  static #validateDuplicated(numbers) {
+    if (numbers.length !== new Set(numbers).size) throw new Error(ERROR.numbers.duplicated);
+  }
+
+  static #validateEmpty(numbers) {
+    if (numbers.length === CONSTANTS.number.zero) throw new Error(ERROR.numbers.empty);
+  }
+}
+
+export default NumbersValidator;

리뷰: 각 매서드들을 바로 호출시키지 않고 배열을 만들어서 forEach 메서드로 하나씩 호출시킨 이유가 궁금해요!
@@ -0,0 +1,20 @@
+import { Console } from '@woowacourse/mission-utils';
+import MESSAGE from '../constants/message.js';
+import NumbersValidator from '../validators/NumbersValidator.js';
+import RestartValidator from '../validators/RestartValidator.js';
+
+const InputView = {
+  async readNumbers() {
+    const numbers = await Console.readLineAsync(MESSAGE.read.numbers);
+    NumbersValidator.validateNumbers(numbers);
+    return [...numbers].map(Number);
+  },
+
+  async readRestart() {
+    const restart = await Console.readLineAsync(MESSAGE.read.restart);
+    RestartValidator.validateRestart(restart);
+    return Number(restart);
+  },
+};
+
+export default InputView;

리뷰: 데이터를 받아 출력하는 view의 역할을 생각했을 때 유효성 검사단계가 view에 있는게 맞는지 궁금증이 드는데 이렇게 작성하신 이유가 궁금합니당 :)