ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,99 @@
+## 우아한 테크코스 프리코스 크리스마스 프로모션
+
+### 진행방식
+- 시작 메시지 출력
+- 방문 날짜 입력
+- 주문 메뉴와 개수 입력
+- 입력 요일 이벤트 메시지 출력
+- 이벤트 혜택 출력
+  - 주문 메뉴
+  - 할인 전 총 주문 금액
+  - 증정 메뉴 여부
+  - 혜택 내역 여부
+  - 할인 후 예상 결제 금액
+  - 12월 이벤트 배지 여부
+
+### 주요 구현 사항
+- [x] 입력 뷰 구현
+- [x] 출력 뷰 구현
+
+- [x] 날짜 입력 및 저장 객체 구현
+- [x] 날짜 입력 시 날짜관련 해당 이벤트 항목 저장
+  - [x] 크리스마스 디데이 총 금액 할인 항목
+  - [x] 평일/주말 구분 할인 타입 항복
+  - [x] 날짜나 달력의 별에 따라 스페셜 할인 항목
+- [x] 날짜 관련 이벤트 반환 DTO 반환
+
+- [x] 주문 메뉴와 개수 입력 및 저장 객체 구현
+  - [x] 각 메뉴를 기본 금액과 메뉴 명으로 Enum 으로 관리
+  - [x] 주문한 메뉴 입력에 대한 유효성 검사 진행 후 저장 객체에 저장
+- [x] 주문 메뉴 내역 반환 DTO 반환
+
+- [x] 날짜 이벤트와 주문 메뉴 DTO 를 통한 이벤트 혜택 적용
+- [x] 이벤트 적용 DTO 반환 및 출력
+  - [x] 할인 전 금액을 출력
+  - [x] 할인 전 금액에 따른 증정 여부 출력
+    - 증정 여부가 없다면 '없음' 출력
+  - [x] 혜택 내역 출력
+    - 적용된 혜택 없다면 '없음' 출력
+  - [x] 혜택 금액 출력
+  - [x] 증정 혜택 금액을 제외한 할인 후 예상 금액 출력
+  - [x] 총 혜택 금액에 따른 이벤트 배지 부여 여부 출력
+    - 이벤트 배지가 없다면 '없음' 출력
+
+- 추가 구현 사항
+  - 이벤트 결과 도메인의 정보를 출력 문자열으로 변경하여 Builder DTO 생성/반환
+  - 잘못된 입력을 통한 과정은 예외 메시지 출력 및 재입력
+  - 총 주문 메뉴 금액이 10,000원 이하일 경우 모든 할인 금액 0으로 지정
+
+### 예외 처리
+- 날짜 입력 예외
+  - [x] 잘못된 날짜 입력의 경우
+  - [x] 날짜가 정수형으로 변경이 안될 때
+- 메뉴 입력 예외
+  - [x] 메뉴 입력 시 정규표현식 패턴이 다를 경우 (TextProcessor 처리)
+  - [x] 중복 메뉴명 입력의 경우 (TextProcessor 처리)
+  - [x] 없는 메뉴명 입력의 경우
+  - [x] 주문 메뉴의 개수가 1보다 작을 때
+  - [x] 주문 메뉴가 20개를 넘을 때
+  - [x] 음료만 주문 시 주문 불가능
+
+### 클래스 구조
+- InputView : 화면 입력을 담당하는 뷰
+  - InputViewMessage : 입력을 위한 메시지를 관리하는 Enum
+- OutputView : 화면 출력을 담당하는 뷰
+  - OutputViewMessage : 출력을 위한 메시지를 관리하는 Enum
+- PromotionRun : 뷰와 컨트롤러를 연결하는 클래스로 요청 전달
+- PromotionController : 비즈니스 로직을 실행하기 위해 요청을 전달하는 컨트롤러
+- PromotionService : 프로모션 유효성 검증을 거친 객체 생성과 비즈니스 로직을 실행할 서비스
+- TextProcessor : 입력받은 문자열을 특정 타입으로 반환하기 위한 서비스
+- OrderMenus : 주문 메뉴와 개수 저장하는 클래스
+  - MenuItem : 메뉴 이름과 가격, 메뉴 종류를 저장하는 Enum
+  - MenuCategory : 메뉴 종류를 구분하는 Enum
+- ReservationDateEvent : 날짜 관련 이벤트 저장 클래스
+  - ReservationDate : 입력된 날짜를 저장하기 위한 클래스
+  - ChristmasDiscount : 크리스마스 D-day 할인 관리 클래스
+  - WeekDiscountType : 주말 할인, 평일 할인을 구분하는 Enum
+  - SpecialDayDiscount : 특별 할인 일자가 저장되어 있는 Enum
+- EventResult : 적용 중인 모든 이벤트를 저장하는 클래스
+  - DiscountDetail : 주문 금액과 할인, 예상 금액 등을 보관한 클래스
+  - GiftEvent : 증정 상품 여부를 관리하는 Enum
+  - RewardBadge : 이벤트 배지를 관리하는 Enum
+- PromotionException : 지정된 Enum 예외 메시지를 통해 값를 담아 IllegalArgumentException 생성
+  - ExceptionMessage : 예외 발생 메시지를 보관한 Enum Class
+- PromotionConverter : Service 에서 Domain to DTO 컨버터
+- EventResultTextFactory : EventResultDTO 반환을 위한 문자열 조작 서비스
+  - EventResultText : EventResultDTO 에 필요한 문자열 Enum
+- 테스트 : 클래스별 메소드 단위 테스트 진행
+
+### 이번에 프로젝트를 진행하면서 고민한 요소
+- 해당 기능을 구현하기 위한 구조 설계
+  - 이후 필요한 클래스들을 추가하며 설계 변경
+- 동일한 입력과 출력을 하는 뷰는 싱글톤으로 구현
+- 입력에 대한 유효성 검사와 객체 생성에 대한 유효성 검사 구분에 대한 고민
+- 이벤트 적용 시 이벤트 내역과 주문 내역을 사용해 값을 저장하는 방법을 Service 에서 진행할지 객체 내부에서 진행할지 고민
+- 출력을 위한 DTO 생성에 대한 고민
+  - DTO 룰 출력하고 해당 DTO 통해 다시 이벤트 내역 확인하는데 있어 DTO 값을 사용하는 방법
+  - EventResultDTO 뷰에 출력할 문자열을 가지고 있을 DTO 생성 방법 고민 후 Builder, EventResultTextFactory 사용
+- 잘못된 입력에 대한 메서드 재실행 로직은 지난주 [zangsu님](https://github.com/zangsu) 코드리뷰로 배운 Supplier 반복 사용
+- 테스트 코드 mock 객체에 대한 사용방법에 대한 고민
\ No newline at end of file

리뷰: 아 좋은 생각이네요. 이후 관리에는 생각하지 않았던 것 같습니다
@@ -1,7 +1,20 @@
 package christmas;
 
+
+import camp.nextstep.edu.missionutils.Console;
+import christmas.run.PromotionRun;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+
 public class Application {
     public static void main(String[] args) {
-        // TODO: 프로그램 구현
+        try {
+            InputView inputView = SingletonView.getInputView();
+            OutputView outputView = SingletonView.getOutputView();
+            PromotionRun promotionRun = new PromotionRun(inputView, outputView);
+            promotionRun.runPromotion();
+        } finally {
+            Console.close();
+        }
     }
 }

리뷰: 한번만 생성되면 되고 새로운 객체를 생성할 필요가 없겠다 싶어 적용했는데 컨트롤러, 서비스 모두 적용해야하나 싶다가 뷰만 생성하게 되었습니다..
@@ -0,0 +1,20 @@
+package christmas.exception;
+
+public enum ExceptionMessage {
+    INVALID_INPUT_DATE("유효하지 않은 날짜입니다. 다시 입력해 주세요."),
+    INVALID_INPUT_ORDER("유효하지 않은 주문입니다. 다시 입력해 주세요."),
+    INVALID_MENU_ITEM("해당 메뉴가 존재하지 않습니다."),
+    INVALID_WEEK_DISCOUNT_TYPE("잘못된 주간 할인 타입입니다."),
+    ;
+
+    private static final String PREFIX = "[ERROR] ";
+    private final String errorMessage;
+
+    ExceptionMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+
+    public String getErrorMessage() {
+        return PREFIX + errorMessage;
+    }
+}

리뷰: 아 해당 예외는 이미 처리된 결과 DTO 들을 가지고 발생되는 예외라서 정상적이라면 터지지 않는 예외 입니다. 일단 제 생각으로는 무슨 짓을 해도 입력에 대해 해당 예외는 터지지는 않을 거라 생각하는데 해당 기능에서만 터지는 예외를 구분하기 위해서 사용했습니다!
@@ -0,0 +1,33 @@
+package christmas.model.event;
+
+import christmas.model.date.ReservationDate;
+
+public class ChristmasDiscount {
+    private static final int FIRST_DAY = 1;
+    private static final int CHRISTMAS_DAY = 25;
+    private static final int BASIC_DISCOUNT = 1000;
+    private static final int PLUS_DISCOUNT = 100;
+    private static final int NONE_DISCOUNT = 0;
+
+    private final int discount;
+
+    public ChristmasDiscount(ReservationDate date) {
+        this.discount = initDiscountAmount(date);
+    }
+
+    private int initDiscountAmount(ReservationDate date) {
+        if (date.day() > CHRISTMAS_DAY) {
+            return NONE_DISCOUNT;
+        }
+
+        return BASIC_DISCOUNT + getPlusDiscount(date.day());
+    }
+
+    private int getPlusDiscount(int day) {
+        return (day - FIRST_DAY) * PLUS_DISCOUNT;
+    }
+
+    public int getDiscount() {
+        return discount;
+    }
+}

리뷰: 예약 일자 객체는 예약된 일자에 대해서만 값을 가지고 있어 할인을 적용하기 위한 유효성 검사를 해당 할인 객체가 수행해야 한다고 생각해서 넣었는데 고민이 되네요..
@@ -0,0 +1,62 @@
+package christmas.model.event;
+
+import christmas.model.event.dto.ReservationDateEventDTO;
+
+public class DiscountDetail {
+    private final int totalPriceBeforeDiscount;
+    private final int totalPriceAfterDiscount;
+    private final WeekDiscountType weekDiscountType;
+    private final int christmasDiscount;
+    private final int weekTypeDiscount;
+    private final int specialDiscount;
+
+    protected DiscountDetail(ReservationDateEventDTO dateEventDTO, int totalPriceBeforeDiscount, int weekTypeDiscount) {
+        this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+        this.weekDiscountType = WeekDiscountType.checkWeekType(dateEventDTO.getDateWeek());
+        this.christmasDiscount = dateEventDTO.getChristmasDiscount();
+        this.specialDiscount = dateEventDTO.getSpecialDiscount();
+        this.weekTypeDiscount = weekTypeDiscount;
+        this.totalPriceAfterDiscount = initTotalPriceAfterDiscount();
+    }
+
+    protected DiscountDetail(ReservationDateEventDTO dateEventDTO, int totalPriceBeforeDiscount) {
+        this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+        this.weekDiscountType = WeekDiscountType.checkWeekType(dateEventDTO.getDateWeek());
+        this.christmasDiscount = 0;
+        this.specialDiscount = 0;
+        this.weekTypeDiscount = 0;
+        this.totalPriceAfterDiscount = initTotalPriceAfterDiscount();
+    }
+
+    private int initTotalPriceAfterDiscount() {
+        return totalPriceBeforeDiscount - getTotalDiscount();
+    }
+
+    public int getTotalPriceBeforeDiscount() {
+        return totalPriceBeforeDiscount;
+    }
+
+    public int getTotalPriceAfterDiscount() {
+        return totalPriceAfterDiscount;
+    }
+
+    public int getChristmasDiscount() {
+        return christmasDiscount;
+    }
+
+    public int getSpecialDiscount() {
+        return specialDiscount;
+    }
+
+    public int getWeekTypeDiscount() {
+        return weekTypeDiscount;
+    }
+
+    public WeekDiscountType getWeekDiscountType() {
+        return weekDiscountType;
+    }
+
+    public int getTotalDiscount() {
+        return christmasDiscount + weekTypeDiscount + specialDiscount;
+    }
+}

리뷰: 객체가 가지고 있는 상태의 객체를 표현하려 하다보니 이렇게 되었는데 아직도 메서드로 메시지를 전달하는 방법은 아직 낯선 것 같습니다.. 중간중간 인지하려해도 놓치는 부분이 많네요..
@@ -0,0 +1,62 @@
+package christmas.model.event;
+
+import christmas.model.event.dto.ReservationDateEventDTO;
+
+public class DiscountDetail {
+    private final int totalPriceBeforeDiscount;
+    private final int totalPriceAfterDiscount;
+    private final WeekDiscountType weekDiscountType;
+    private final int christmasDiscount;
+    private final int weekTypeDiscount;
+    private final int specialDiscount;
+
+    protected DiscountDetail(ReservationDateEventDTO dateEventDTO, int totalPriceBeforeDiscount, int weekTypeDiscount) {
+        this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+        this.weekDiscountType = WeekDiscountType.checkWeekType(dateEventDTO.getDateWeek());
+        this.christmasDiscount = dateEventDTO.getChristmasDiscount();
+        this.specialDiscount = dateEventDTO.getSpecialDiscount();
+        this.weekTypeDiscount = weekTypeDiscount;
+        this.totalPriceAfterDiscount = initTotalPriceAfterDiscount();
+    }
+
+    protected DiscountDetail(ReservationDateEventDTO dateEventDTO, int totalPriceBeforeDiscount) {
+        this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+        this.weekDiscountType = WeekDiscountType.checkWeekType(dateEventDTO.getDateWeek());
+        this.christmasDiscount = 0;
+        this.specialDiscount = 0;
+        this.weekTypeDiscount = 0;
+        this.totalPriceAfterDiscount = initTotalPriceAfterDiscount();
+    }
+
+    private int initTotalPriceAfterDiscount() {
+        return totalPriceBeforeDiscount - getTotalDiscount();
+    }
+
+    public int getTotalPriceBeforeDiscount() {
+        return totalPriceBeforeDiscount;
+    }
+
+    public int getTotalPriceAfterDiscount() {
+        return totalPriceAfterDiscount;
+    }
+
+    public int getChristmasDiscount() {
+        return christmasDiscount;
+    }
+
+    public int getSpecialDiscount() {
+        return specialDiscount;
+    }
+
+    public int getWeekTypeDiscount() {
+        return weekTypeDiscount;
+    }
+
+    public WeekDiscountType getWeekDiscountType() {
+        return weekDiscountType;
+    }
+
+    public int getTotalDiscount() {
+        return christmasDiscount + weekTypeDiscount + specialDiscount;
+    }
+}

리뷰: 여러 객체를 생성하고 static으로 객체를 생성하는 방법보다는 저는 개인적으로 해당 기능은 할인이 없을 경우의 객체를 오버로딩을 통해 생성하는게 좋다고 생각됩니다
@@ -0,0 +1,28 @@
+package christmas.model.event;
+
+public enum SpecialDayDiscount {
+    THIRD_DAY(3, 1000),
+    TENTH_DAY(10, 1000),
+    SEVENTEENTH_DAY(17, 1000),
+    TWENTY_FOURTH_DAY(24, 1000),
+    TWENTY_FIFTH_DAY(25, 1000),
+    THIRTY_FIRST_DAY(31, 1000),
+    OTHER_DAY(0, 0),
+    ;
+
+    private final int day;
+    private final int discountPrice;
+
+    SpecialDayDiscount(int day, int discountPrice) {
+        this.day = day;
+        this.discountPrice = discountPrice;
+    }
+
+    public int getDay() {
+        return day;
+    }
+
+    public int getDiscountPrice() {
+        return discountPrice;
+    }
+}

리뷰: 좋은 생각입니다! enum에 자료구조를 넣을 생각을 못했던 것 같아요
@@ -0,0 +1,79 @@
+package christmas.model.menu;
+
+import christmas.exception.ExceptionMessage;
+import christmas.exception.PromotionException;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class OrderMenus {
+    private static final int MIN_ORDER_MENU_NUMBER = 1;
+    private static final int MAX_ORDER_MENUS_NUMBER = 20;
+
+    private final Map<MenuItem, Integer> orderMenus;
+
+    public OrderMenus(Map<String, Integer> orderMenus) {
+        this.orderMenus = validateOrderMenusByNames(orderMenus);
+        validateOrderMenus();
+    }
+
+    private Map<MenuItem, Integer> validateOrderMenusByNames(Map<String, Integer> orderMenus) {
+        return generateOrderMenuValues(orderMenus)
+                .collect(Collectors.toUnmodifiableMap(
+                        entry -> MenuItem.findMenuItemByName(entry.getKey(), ExceptionMessage.INVALID_INPUT_ORDER),
+                        Map.Entry::getValue
+                ));
+    }
+
+    private void validateOrderMenus() {
+        validateOrderNumbers();
+        validateTotalOrderNumbers();
+        validateOrderAllBeverage();
+    }
+
+    private void validateOrderNumbers() {
+        generateOrderMenuValues(orderMenus)
+                .filter(menu ->
+                        menu.getValue() < MIN_ORDER_MENU_NUMBER)
+                .findAny()
+                .ifPresent(menuName -> throwInvalidOrderException());
+    }
+
+    private void validateTotalOrderNumbers() {
+        int orderNumber = generateOrderMenuValues(orderMenus)
+                .mapToInt(Map.Entry::getValue)
+                .sum();
+
+        if (orderNumber > MAX_ORDER_MENUS_NUMBER) {
+            throwInvalidOrderException();
+        }
+    }
+
+    private void validateOrderAllBeverage() {
+        boolean allBeverage = generateOrderMenuValues(orderMenus)
+                .allMatch(orderMenu ->
+                        orderMenu.getKey().getMenuCategory() == MenuCategory.BEVERAGE);
+
+        if (allBeverage) {
+            throwInvalidOrderException();
+        }
+    }
+
+    private <K, V> Stream<Map.Entry<K, V>> generateOrderMenuValues(Map<K, V> orderMenus) {
+        return orderMenus.entrySet()
+                .stream();
+    }
+
+    private void throwInvalidOrderException() {
+        throw new PromotionException(ExceptionMessage.INVALID_INPUT_ORDER);
+    }
+
+    public Map<String, Integer> getOrderMenus() {
+        Map<String, Integer> menuNames = new HashMap<>();
+        orderMenus.forEach((menuItem, quantity) ->
+                menuNames.put(menuItem.getItemName(), quantity));
+
+        return Collections.unmodifiableMap(menuNames);
+    }
+}

리뷰: 개수만을 가지고 있는 객체다 보니 클래스가 많아져 무거워질 것 같아 사용하지 않았는데 뭐가 더 나은지는 고민이 되네요..
@@ -0,0 +1,76 @@
+package christmas.run;
+
+import christmas.controller.PromotionController;
+import christmas.exception.PromotionException;
+import christmas.model.event.dto.EventResultDTO;
+import christmas.model.event.dto.ReservationDateEventDTO;
+import christmas.model.menu.dto.OrderMenusDTO;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+
+import java.util.function.Supplier;
+
+public class PromotionRun {
+    private final PromotionController controller = new PromotionController();
+    private final InputView inputView;
+    private final OutputView outputView;
+
+    public PromotionRun(InputView inputView, OutputView outputView) {
+        this.inputView = inputView;
+        this.outputView = outputView;
+    }
+
+    public void runPromotion() {
+        ReservationDateEventDTO dateEventDto = inputCorrectReservationDate();
+        OrderMenusDTO orderMenusDto = inputCorrectOrderMenus();
+        displayEventPreview(dateEventDto);
+
+        EventResultDTO responseDto = applyEventsAndGenerateResult(dateEventDto, orderMenusDto);
+        outputEventResult(responseDto);
+    }
+
+    private ReservationDateEventDTO inputCorrectReservationDate() {
+        outputView.displayStartPromotion();
+
+        return getCorrectResult(this::generateDateEvent);
+    }
+
+    private OrderMenusDTO inputCorrectOrderMenus() {
+        return getCorrectResult(this::receiveMenus);
+    }
+
+    private ReservationDateEventDTO generateDateEvent() {
+        String inputDate = inputView.inputDate();
+
+        return controller.generateEvent(inputDate);
+    }
+
+    private OrderMenusDTO receiveMenus() {
+        String inputMenus = inputView.inputOrderMenus();
+
+        return controller.receiveOrderMenus(inputMenus);
+    }
+
+    private void displayEventPreview(ReservationDateEventDTO dateEventDto) {
+        outputView.displayPreviewEvent(dateEventDto.getDay());
+    }
+
+    private EventResultDTO applyEventsAndGenerateResult(ReservationDateEventDTO dateEventDTO,
+                                                        OrderMenusDTO orderMenusDto) {
+        return controller.applyEvents(dateEventDTO, orderMenusDto);
+    }
+
+    private void outputEventResult(EventResultDTO responseDto) {
+        outputView.outputEventResult(responseDto);
+    }
+
+    private <T> T getCorrectResult(Supplier<T> supplier) {
+        while (true) {
+            try {
+                return supplier.get();
+            } catch (PromotionException e) {
+                outputView.displayException(e);
+            }
+        }
+    }
+}

리뷰: 해당 부분은 저도 이전 코드리뷰를 통해 배운 구현 방법인데 좋은 방법인 것 같습니다.
@@ -0,0 +1,27 @@
+package christmas.util;
+
+enum EventResultText {
+    ORDER_OUTPUT_REGEX("%s %d%s"),
+    EMPTY_TEXT(""),
+    SPACE(" "),
+    MENU_NUMBER("개"),
+    MENU_PRICE_UNIT("원"),
+    CHRISTMAS_D_DAY_DISCOUNT("크리스마스 디데이 할인"),
+    SPECIAL_DISCOUNT("특별 할인"),
+    GIFT_EVENT("증정 이벤트"),
+    NONE_BENEFIT("없음"),
+    DISCOUNT_PRICE("-"),
+    BENEFIT_SEPARATOR(": "),
+    NEW_LINE("\n"),
+    ;
+
+    private final String text;
+
+    EventResultText(String text) {
+        this.text = text;
+    }
+
+    public String getText() {
+        return text;
+    }
+}

리뷰: 결과 문자열을 출력해주기 위한 상수들을 묶어 둔 Enum 입니다. 다른 곳에서 사용되지는 않을 것 같아 default로 enum을 사용했어요
@@ -0,0 +1,62 @@
+package christmas.model.event;
+
+import christmas.model.event.dto.ReservationDateEventDTO;
+
+public class DiscountDetail {
+    private final int totalPriceBeforeDiscount;
+    private final int totalPriceAfterDiscount;
+    private final WeekDiscountType weekDiscountType;
+    private final int christmasDiscount;
+    private final int weekTypeDiscount;
+    private final int specialDiscount;
+
+    protected DiscountDetail(ReservationDateEventDTO dateEventDTO, int totalPriceBeforeDiscount, int weekTypeDiscount) {
+        this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+        this.weekDiscountType = WeekDiscountType.checkWeekType(dateEventDTO.getDateWeek());
+        this.christmasDiscount = dateEventDTO.getChristmasDiscount();
+        this.specialDiscount = dateEventDTO.getSpecialDiscount();
+        this.weekTypeDiscount = weekTypeDiscount;
+        this.totalPriceAfterDiscount = initTotalPriceAfterDiscount();
+    }
+
+    protected DiscountDetail(ReservationDateEventDTO dateEventDTO, int totalPriceBeforeDiscount) {
+        this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+        this.weekDiscountType = WeekDiscountType.checkWeekType(dateEventDTO.getDateWeek());
+        this.christmasDiscount = 0;
+        this.specialDiscount = 0;
+        this.weekTypeDiscount = 0;
+        this.totalPriceAfterDiscount = initTotalPriceAfterDiscount();
+    }
+
+    private int initTotalPriceAfterDiscount() {
+        return totalPriceBeforeDiscount - getTotalDiscount();
+    }
+
+    public int getTotalPriceBeforeDiscount() {
+        return totalPriceBeforeDiscount;
+    }
+
+    public int getTotalPriceAfterDiscount() {
+        return totalPriceAfterDiscount;
+    }
+
+    public int getChristmasDiscount() {
+        return christmasDiscount;
+    }
+
+    public int getSpecialDiscount() {
+        return specialDiscount;
+    }
+
+    public int getWeekTypeDiscount() {
+        return weekTypeDiscount;
+    }
+
+    public WeekDiscountType getWeekDiscountType() {
+        return weekDiscountType;
+    }
+
+    public int getTotalDiscount() {
+        return christmasDiscount + weekTypeDiscount + specialDiscount;
+    }
+}

리뷰: 필드로 가지고 있는게 여러번 호출 될 경우 데이터만 가지고 오는 것이 좋다 생각해서 해당 방식으로 사용하는 것이 좋다 생각했는데 몇몇 필드는 메서드로 해당 필드만을 가지고 반환할 수 있을 것 같네요
@@ -0,0 +1,105 @@
+package christmas.model.event.dto;
+
+public class EventResultDTO {
+    private final String orderMenus;
+    private final String totalPriceBeforeDiscount;
+    private final String giftMenu;
+    private final String benefitHistory;
+    private final String totalBenefit;
+    private final String totalPriceAfterDiscount;
+    private final String rewardBadge;
+
+    private EventResultDTO(Builder builder) {
+        this.orderMenus = builder.orderMenus;
+        this.totalPriceBeforeDiscount = builder.totalPriceBeforeDiscount;
+        this.giftMenu = builder.giftMenu;
+        this.benefitHistory = builder.benefitHistory;
+        this.totalBenefit = builder.totalBenefit;
+        this.totalPriceAfterDiscount = builder.totalPriceAfterDiscount;
+        this.rewardBadge = builder.rewardBadge;
+    }
+
+    public static class Builder {
+        private String orderMenus;
+        private String totalPriceBeforeDiscount;
+        private String giftMenu;
+        private String benefitHistory;
+        private String totalBenefit;
+        private String totalPriceAfterDiscount;
+        private String rewardBadge;
+
+        public Builder() {
+        }
+
+        public Builder orderMenus(String orderMenus) {
+            this.orderMenus = orderMenus;
+            return this;
+        }
+
+        public Builder totalPriceBeforeDiscount(String totalPriceBeforeDiscount) {
+            this.totalPriceBeforeDiscount = totalPriceBeforeDiscount;
+            return this;
+        }
+
+        public Builder giftMenu(String giftMenu) {
+            this.giftMenu = giftMenu;
+            return this;
+        }
+
+        public Builder benefitHistory(String benefitHistory) {
+            this.benefitHistory = benefitHistory;
+            return this;
+        }
+
+        public Builder totalBenefit(String totalBenefit) {
+            this.totalBenefit = totalBenefit;
+            return this;
+        }
+
+        public Builder totalPriceAfterDiscount(String totalPriceAfterDiscount) {
+            this.totalPriceAfterDiscount = totalPriceAfterDiscount;
+            return this;
+        }
+
+        public Builder rewardBadge(String rewardBadge) {
+            this.rewardBadge = rewardBadge;
+            return this;
+        }
+
+        public EventResultDTO build() {
+            return new EventResultDTO(this);
+        }
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public String getOrderMenus() {
+        return orderMenus;
+    }
+
+    public String getTotalPriceBeforeDiscount() {
+        return totalPriceBeforeDiscount;
+    }
+
+    public String getGiftMenu() {
+        return giftMenu;
+    }
+
+    public String getBenefitHistory() {
+        return benefitHistory;
+    }
+
+    public String getTotalBenefit() {
+        return totalBenefit;
+    }
+
+    public String getTotalPriceAfterDiscount() {
+        return totalPriceAfterDiscount;
+    }
+
+    public String getRewardBadge() {
+        return rewardBadge;
+    }
+}

리뷰: 빌더 클래스를 사용하여 인스턴스를 생성하면 null 인 값이 올 수 도 있을텐데 전부 다 필요해이는 변수들인데 생성자를 이용하지 않고 빌더클래스를 활용하신 이유가 궁금합니다!
@@ -0,0 +1,55 @@
+package christmas.model.menu;
+
+import christmas.exception.ExceptionMessage;
+import christmas.exception.PromotionException;
+
+import java.util.Arrays;
+
+public enum MenuItem {
+    APPETIZER_MUSHROOM_SOUP("양송이수프", 6_000, MenuCategory.APPETIZER),
+    APPETIZER_TAPAS("타파스", 5_500, MenuCategory.APPETIZER),
+    APPETIZER_CAESAR_SALAD("시저샐러드", 8_000, MenuCategory.APPETIZER),
+
+    MAIN_T_BONE_STEAK("티본스테이크", 55_000, MenuCategory.MAIN_COURSE),
+    MAIN_BBQ_RIB("바비큐립", 54_000, MenuCategory.MAIN_COURSE),
+    MAIN_SEAFOOD_PASTA("해산물파스타", 35_000, MenuCategory.MAIN_COURSE),
+    MAIN_CHRISTMAS_PASTA("크리스마스파스타", 25_000, MenuCategory.MAIN_COURSE),
+
+    DESSERT_CHOCO_CAKE("초코케이크", 15_000, MenuCategory.DESSERT),
+    DESSERT_ICE_CREAM("아이스크림", 5_000, MenuCategory.DESSERT),
+
+    BEVERAGE_ZERO_COLA("제로콜라", 3_000, MenuCategory.BEVERAGE),
+    BEVERAGE_RED_WINE("레드와인", 60_000, MenuCategory.BEVERAGE),
+    BEVERAGE_CHAMPAGNE("샴페인", 25_000, MenuCategory.BEVERAGE),
+    ;
+
+    private final String itemName;
+    private final int itemPrice;
+    private final MenuCategory menuCategory;
+
+    MenuItem(String itemName, int itemPrice, MenuCategory menuCategory) {
+        this.itemName = itemName;
+        this.itemPrice = itemPrice;
+        this.menuCategory = menuCategory;
+    }
+
+    public static MenuItem findMenuItemByName(String itemName, ExceptionMessage message) {
+        return Arrays.stream(MenuItem.values())
+                .filter(menu -> menu.getItemName().equals(itemName))
+                .findFirst()
+                .orElseThrow(() ->
+                        new PromotionException(message));
+    }
+
+    public String getItemName() {
+        return itemName;
+    }
+
+    public int getItemPrice() {
+        return itemPrice;
+    }
+
+    public MenuCategory getMenuCategory() {
+        return menuCategory;
+    }
+}

리뷰: 메뉴 아이템 CRUD API 를 만들때 enum 으로 하게 되면 추가에 대해 확장성이 떨어지는것이 아닌가 생각이듭니다!