ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,91 @@
+package christmas.view;
+
+import christmas.dto.BenefitDTO;
+import christmas.dto.OrderDTO;
+
+import java.util.List;
+
+public class OutputView {
+    private void printMessage(ViewMessage message) {
+        System.out.println(message.getMessage());
+    }
+
+    private void printFormat(ViewMessage message, Object... args) {
+        System.out.printf(message.getMessage(), args);
+        newLine();
+    }
+
+    private void printTitle(ViewTitle title) {
+        System.out.println(title.getTitle());
+    }
+
+    public void printHelloMessage() {
+        printMessage(ViewMessage.HELLO);
+    }
+
+    public void printEventPreviewTitle(int date) {
+        printFormat(ViewMessage.EVENT_PREVIEW_TITLE, date);
+        newLine();
+    }
+
+    public void printOrder(List<OrderDTO> orders) {
+        printTitle(ViewTitle.ORDER_MENU);
+        orders.forEach(it -> printFormat(ViewMessage.ORDER_FORMAT, it.menuName(), it.count()));
+        newLine();
+    }
+
+    public void printTotalPrice(int price) {
+        printTitle(ViewTitle.TOTAL_PRICE);
+        printFormat(ViewMessage.PRICE_FORMAT, price);
+        newLine();
+    }
+
+    public void printGift(boolean hasGift) {
+        printTitle(ViewTitle.GIFT_MENU);
+        if (hasGift) {
+            printMessage(ViewMessage.CHAMPAGNE);
+            newLine();
+            return;
+        }
+        printMessage(ViewMessage.NOTHING);
+        newLine();
+    }
+
+    public void printAllBenefit(List<BenefitDTO> benefits) {
+        printTitle(ViewTitle.BENEFIT_LIST);
+        if (benefits.isEmpty()) {
+            printMessage(ViewMessage.NOTHING);
+            newLine();
+            return;
+        }
+        benefits.forEach(it -> printFormat(ViewMessage.BENEFIT_FORMAT, it.EventName(), it.price()));
+        newLine();
+    }
+
+    public void printBenefitPrice(int price) {
+        printTitle(ViewTitle.BENEFIT_PRICE);
+        printFormat(ViewMessage.PRICE_FORMAT, -price);
+        newLine();
+    }
+
+    public void printAfterDiscountPrice(int price) {
+        printTitle(ViewTitle.AFTER_DISCOUNT_PRICE);
+        printFormat(ViewMessage.PRICE_FORMAT, price);
+        newLine();
+    }
+
+    public void printBadge(String badge) {
+        printTitle(ViewTitle.BADGE);
+        System.out.println(badge);
+    }
+
+    public void printErrorMessage(IllegalArgumentException error) {
+        newLine();
+        System.out.println(error.getMessage());
+        newLine();
+    }
+
+    public void newLine() {
+        System.out.println();
+    }
+}

리뷰: 좋은 지적 감사합니다! `lineSeperator()`에 대해서 공부해봐야겠네요! 😊
@@ -0,0 +1,94 @@
+package christmas.domain;
+
+import christmas.config.Constant;
+import christmas.dto.OrderDTO;
+import christmas.config.ErrorMessage;
+import christmas.config.MenuType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Bill implements CheckEventDate {
+    private final List<Order> orders = new ArrayList<>();
+    private final Date date;
+
+    private Bill(Date date) {
+        this.date = date;
+    }
+
+    public static Bill from(int date) {
+        return new Bill(Date.from(date));
+    }
+
+    public Bill add(Order order) {
+        validateDuplicates(order);
+        orders.add(order);
+        validateMaxOrder();
+        return this;
+    }
+
+    private void validateDuplicates(Order newOrder) {
+        long duplicated = orders.stream()
+                .filter(it -> it.isSameMenu(newOrder))
+                .count();
+        if (duplicated != Constant.FILTER_CONDITION) {
+            throw new IllegalArgumentException(ErrorMessage.WRONG_ORDER.getMessage());
+        }
+    }
+
+    private void validateMaxOrder() {
+        if (Order.accumulateCount(orders) > Constant.MAX_ORDER) {
+            throw new IllegalArgumentException(ErrorMessage.OVER_MAX_ORDER.getMessage());
+        }
+    }
+
+    public void validateOnlyDrink() {
+        if (Order.accumulateCount(orders) == getTypeCount(MenuType.DRINK)) {
+            throw new IllegalArgumentException(ErrorMessage.ONLY_DRINK_ORDER.getMessage());
+        }
+    }
+
+    public void clearOrder() {
+        orders.clear();
+    }
+
+    public int getTotalPrice() {
+        return orders.stream()
+                .map(Order::getPrice)
+                .reduce(Constant.INIT_VALUE, Integer::sum);
+    }
+
+    public int getTypeCount(MenuType type) {
+        return Order.accumulateCount(orders, type);
+    }
+
+    public List<OrderDTO> getAllOrders() {
+        return orders.stream().map(Order::getOrder).toList();
+    }
+
+    // 아래는 Date 관련 method
+
+    @Override
+    public boolean isWeekend() {
+        return date.isWeekend();
+    }
+
+    @Override
+    public boolean isSpecialDay() {
+        return date.isSpecialDay();
+    }
+
+    @Override
+    public boolean isNotPassedChristmas() {
+        return date.isNotPassedChristmas();
+    }
+
+    @Override
+    public int timePassedSinceFirstDay() {
+        return date.timePassedSinceFirstDay();
+    }
+
+    public int getDateValue() {
+        return date.getDateValue();
+    }
+}

리뷰: 그렇게도 할 수 있겠네요! 아예 `Orders`라는 클래스를 만들어 리스트를 관리하는 방법도 좋아보입니다!
리팩토링 할만한 부분들을 짚어주셔서 좋습니다!! 😁
@@ -0,0 +1,47 @@
+package christmas.config;
+
+import java.util.Arrays;
+
+public enum Menu {
+    SOUP("양송이수프", 6000, MenuType.APPETIZER),
+    TAPAS("타파스", 5500, MenuType.APPETIZER),
+    SALAD("시저샐러드", 8000, MenuType.APPETIZER),
+    STEAK("티본스테이크", 55000, MenuType.MAIN),
+    BARBECUE("바비큐립", 54000, MenuType.MAIN),
+    SEAFOOD_PASTA("해산물파스타", 35000, MenuType.MAIN),
+    CHRISTMAS_PASTA("크리스마스파스타", 25000, MenuType.MAIN),
+    CAKE("초코케이크", 15000, MenuType.DESSERT),
+    ICE_CREAM("아이스크림", 5000, MenuType.DESSERT),
+    COLA("제로콜라", 3000, MenuType.DRINK),
+    WINE("레드와인", 60000, MenuType.DRINK),
+    CHAMPAGNE("샴페인", 25000, MenuType.DRINK);
+
+    private final String menuName;
+    private final int price;
+    private final MenuType type;
+
+    Menu(String menuName, int price, MenuType type) {
+        this.menuName = menuName;
+        this.price = price;
+        this.type = type;
+    }
+
+    public static Menu from(String menuName) {
+        return Arrays.stream(Menu.values())
+                .filter(it -> menuName.equals(it.menuName))
+                .findAny()
+                .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.WRONG_ORDER.getMessage()));
+    }
+
+    public String getMenuName() {
+        return menuName;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public MenuType getType() {
+        return type;
+    }
+}

리뷰: 저도 같은 방식으로 구현했는데
Menu의 확장성을 생각해서 여러가지 카테고리에 포함될 수도 있는 점을 생각하여
카테고리는 따로 빼는게 좋다는 의견을 봤습니다!
쿠키님은 어떻게 생각하시나요?
@@ -0,0 +1,18 @@
+package christmas.util;
+
+import java.util.function.Consumer;
+
+public class RetryExecutor {
+    private RetryExecutor() {
+        // 인스턴스 생성 방지
+    }
+
+    public static void execute(Runnable action, Consumer<IllegalArgumentException> onError) {
+        try {
+            action.run();
+        } catch (IllegalArgumentException error) {
+            onError.accept(error);
+            execute(action, onError);
+        }
+    }
+}

리뷰: Retry를 저는 Controller에서 while문으로 하여 지저분한 것이 고민이었는데
이런 방법이 있었네요! 배워 갑니다!!
@@ -0,0 +1,91 @@
+package christmas.view;
+
+import christmas.dto.BenefitDTO;
+import christmas.dto.OrderDTO;
+
+import java.util.List;
+
+public class OutputView {
+    private void printMessage(ViewMessage message) {
+        System.out.println(message.getMessage());
+    }
+
+    private void printFormat(ViewMessage message, Object... args) {
+        System.out.printf(message.getMessage(), args);
+        newLine();
+    }
+
+    private void printTitle(ViewTitle title) {
+        System.out.println(title.getTitle());
+    }
+
+    public void printHelloMessage() {
+        printMessage(ViewMessage.HELLO);
+    }
+
+    public void printEventPreviewTitle(int date) {
+        printFormat(ViewMessage.EVENT_PREVIEW_TITLE, date);
+        newLine();
+    }
+
+    public void printOrder(List<OrderDTO> orders) {
+        printTitle(ViewTitle.ORDER_MENU);
+        orders.forEach(it -> printFormat(ViewMessage.ORDER_FORMAT, it.menuName(), it.count()));
+        newLine();
+    }
+
+    public void printTotalPrice(int price) {
+        printTitle(ViewTitle.TOTAL_PRICE);
+        printFormat(ViewMessage.PRICE_FORMAT, price);
+        newLine();
+    }
+
+    public void printGift(boolean hasGift) {
+        printTitle(ViewTitle.GIFT_MENU);
+        if (hasGift) {
+            printMessage(ViewMessage.CHAMPAGNE);
+            newLine();
+            return;
+        }
+        printMessage(ViewMessage.NOTHING);
+        newLine();
+    }
+
+    public void printAllBenefit(List<BenefitDTO> benefits) {
+        printTitle(ViewTitle.BENEFIT_LIST);
+        if (benefits.isEmpty()) {
+            printMessage(ViewMessage.NOTHING);
+            newLine();
+            return;
+        }
+        benefits.forEach(it -> printFormat(ViewMessage.BENEFIT_FORMAT, it.EventName(), it.price()));
+        newLine();
+    }
+
+    public void printBenefitPrice(int price) {
+        printTitle(ViewTitle.BENEFIT_PRICE);
+        printFormat(ViewMessage.PRICE_FORMAT, -price);
+        newLine();
+    }
+
+    public void printAfterDiscountPrice(int price) {
+        printTitle(ViewTitle.AFTER_DISCOUNT_PRICE);
+        printFormat(ViewMessage.PRICE_FORMAT, price);
+        newLine();
+    }
+
+    public void printBadge(String badge) {
+        printTitle(ViewTitle.BADGE);
+        System.out.println(badge);
+    }
+
+    public void printErrorMessage(IllegalArgumentException error) {
+        newLine();
+        System.out.println(error.getMessage());
+        newLine();
+    }
+
+    public void newLine() {
+        System.out.println();
+    }
+}

리뷰: 저는 자주 사용되는 newLine()을 static으로 선언하면 좋을 것 같다고 말씀드리려 했는데
lineSeperator() 라는게 있었군요! 저도 배워가겠습니다😊
@@ -0,0 +1,12 @@
+package christmas.config;
+
+public class Constant {
+    public static final int INIT_VALUE = 0;
+    public static final int FILTER_CONDITION = 0;
+    public static final int MIN_ORDER = 1;
+    public static final int MAX_ORDER = 20;
+
+    private Constant() {
+        // 인스턴스 생성 방지
+    }
+}

리뷰: 개인적으로 Constant 클래스에 상수들을 모아넣는 것 보다는
각 클래스에 필요한 Constant 클래스를 각각 만들어서 만드는 것이 의미를 더 명확히하고 알아보기 쉬운 것 같아요!
@@ -0,0 +1,12 @@
+package christmas.config;
+
+public class Constant {
+    public static final int INIT_VALUE = 0;
+    public static final int FILTER_CONDITION = 0;
+    public static final int MIN_ORDER = 1;
+    public static final int MAX_ORDER = 20;
+
+    private Constant() {
+        // 인스턴스 생성 방지
+    }
+}

리뷰: 주문 수량에 대한 상수들을 저장하셨는데, 클래스명이 Constant여서 init value 나 filter condition 상수명은 의미가 좀 명확하지 않은 것 같습니다. 타인이 읽기에 용도가 명확하지 않은것 같습니다! 
클래스명을 OrderConstatns 와 같이 하면 좋을것 같네요.

최대 주문 갯수를 언제든 정책변경에 따라 바꾸시려고 빼둔 것이라면 패키지 위치를 고려해보는 것도 좋을 것 같습니다.
@@ -0,0 +1,22 @@
+package christmas.config;
+
+public enum ErrorMessage {
+    WRONG_DATE("유효하지 않은 날짜입니다."),
+    WRONG_ORDER("유효하지 않은 주문입니다."),
+    OVER_MAX_ORDER("메뉴는 한 번에 최대 20개까지만 주문할 수 있습니다."),
+    ONLY_DRINK_ORDER("음료만 주문할 수 없습니다.");
+
+    private static final String PREFIX = "[ERROR]";
+    private static final String SUFFIX = "다시 입력해 주세요.";
+    private static final String DELIMITER = " ";
+
+    private final String message;
+
+    ErrorMessage(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return String.join(DELIMITER, PREFIX, message, SUFFIX);
+    }
+}

리뷰: 저는 `class`로 구현했는데 다른 분들 보고 `enum`으로 구현해보려구요! 열거형으로 구현했을 때만의 장점이 있는 것 같습니다! "에러 모음집에서 이런 에러를 가져오는 구나~" 라고 읽기 더 좋은 것 같아요
@@ -0,0 +1,47 @@
+package christmas.config;
+
+import java.util.Arrays;
+
+public enum Menu {
+    SOUP("양송이수프", 6000, MenuType.APPETIZER),
+    TAPAS("타파스", 5500, MenuType.APPETIZER),
+    SALAD("시저샐러드", 8000, MenuType.APPETIZER),
+    STEAK("티본스테이크", 55000, MenuType.MAIN),
+    BARBECUE("바비큐립", 54000, MenuType.MAIN),
+    SEAFOOD_PASTA("해산물파스타", 35000, MenuType.MAIN),
+    CHRISTMAS_PASTA("크리스마스파스타", 25000, MenuType.MAIN),
+    CAKE("초코케이크", 15000, MenuType.DESSERT),
+    ICE_CREAM("아이스크림", 5000, MenuType.DESSERT),
+    COLA("제로콜라", 3000, MenuType.DRINK),
+    WINE("레드와인", 60000, MenuType.DRINK),
+    CHAMPAGNE("샴페인", 25000, MenuType.DRINK);
+
+    private final String menuName;
+    private final int price;
+    private final MenuType type;
+
+    Menu(String menuName, int price, MenuType type) {
+        this.menuName = menuName;
+        this.price = price;
+        this.type = type;
+    }
+
+    public static Menu from(String menuName) {
+        return Arrays.stream(Menu.values())
+                .filter(it -> menuName.equals(it.menuName))
+                .findAny()
+                .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.WRONG_ORDER.getMessage()));
+    }
+
+    public String getMenuName() {
+        return menuName;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public MenuType getType() {
+        return type;
+    }
+}

리뷰: ```suggestion
    SOUP("양송이수프", 6_000, MenuType.APPETIZER),
```
별거 아니지만 가격은 3자리 숫자 마다 '-' 언더바 넣어주시면 가독성에 좋은것같습니다. 실수도 줄여주고요
@@ -0,0 +1,47 @@
+package christmas.config;
+
+import java.util.Arrays;
+
+public enum Menu {
+    SOUP("양송이수프", 6000, MenuType.APPETIZER),
+    TAPAS("타파스", 5500, MenuType.APPETIZER),
+    SALAD("시저샐러드", 8000, MenuType.APPETIZER),
+    STEAK("티본스테이크", 55000, MenuType.MAIN),
+    BARBECUE("바비큐립", 54000, MenuType.MAIN),
+    SEAFOOD_PASTA("해산물파스타", 35000, MenuType.MAIN),
+    CHRISTMAS_PASTA("크리스마스파스타", 25000, MenuType.MAIN),
+    CAKE("초코케이크", 15000, MenuType.DESSERT),
+    ICE_CREAM("아이스크림", 5000, MenuType.DESSERT),
+    COLA("제로콜라", 3000, MenuType.DRINK),
+    WINE("레드와인", 60000, MenuType.DRINK),
+    CHAMPAGNE("샴페인", 25000, MenuType.DRINK);
+
+    private final String menuName;
+    private final int price;
+    private final MenuType type;
+
+    Menu(String menuName, int price, MenuType type) {
+        this.menuName = menuName;
+        this.price = price;
+        this.type = type;
+    }
+
+    public static Menu from(String menuName) {
+        return Arrays.stream(Menu.values())
+                .filter(it -> menuName.equals(it.menuName))
+                .findAny()
+                .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.WRONG_ORDER.getMessage()));
+    }
+
+    public String getMenuName() {
+        return menuName;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public MenuType getType() {
+        return type;
+    }
+}

리뷰: ```suggestion
    public static Menu getMenuBy(String menuName) {
```
정말 각자 스타일이 달라서 제가 꼭 정답은 아니지만 메서드명 추천드립니다!
@@ -0,0 +1,5 @@
+package christmas.config;
+
+public enum MenuType {
+    APPETIZER, MAIN, DESSERT, DRINK
+}

리뷰: 카테고리 따로 열거해서 구분하신게 나중에 문자로 비교하는 것보다 안정성 있게 구현하신것 같아요
@@ -0,0 +1,26 @@
+package christmas.domain;
+
+import java.util.Arrays;
+
+public enum Badge {
+    SANTA("산타", 20000),
+    TREE("트리", 10000),
+    STAR("별", 5000),
+    NOTHING("없음", 0);
+
+    private final String badgeName;
+    private final int threshold;
+
+    Badge(String badgeName, int threshold) {
+        this.badgeName = badgeName;
+        this.threshold = threshold;
+    }
+
+    public static String getBadgeNameWithBenefitPrice(int benefitPrice) {
+        return Arrays.stream(Badge.values())
+                .filter(it -> it.threshold <= benefitPrice)
+                .map(it -> it.badgeName)
+                .findFirst()
+                .orElse(NOTHING.badgeName);
+    }
+}

리뷰: 저도 이렇게 구현했는데 고민하다보니
뱃지가 가진 개념이 `가격` 인지 `범위`인지 취향 차이라고 생각하는데 저는 `범위`가 조금더 뱃지가 가진 개념이라고 생각이 들더라구요 그래서 안에 기준 가격대신 범위를 넣어보는 고민도 해보시면 좋을 것 같습니다.
@@ -0,0 +1,81 @@
+package christmas.domain;
+
+import christmas.config.ErrorMessage;
+
+import java.util.Arrays;
+
+public class Date implements CheckEventDate {
+    private static final int WEEK_NUM = 7;
+    private static final int WEEK_DIFF = 4;
+    private static final int FIRST_DAY = 1;
+    private static final int LAST_DAY = 31;
+    private static final int CHRISTMAS = 25;
+
+    private final int date;
+
+    private Date(int date) {
+        validate(date);
+        this.date = date;
+    }
+
+    public static Date from(int date) {
+        return new Date(date);
+    }
+
+    private void validate(int date) {
+        if (date < FIRST_DAY || date > LAST_DAY) {
+            throw new IllegalArgumentException(ErrorMessage.WRONG_DATE.getMessage());
+        }
+    }
+
+    private enum DayOfWeek {
+        SUN(0), MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6);
+
+        private final int value;
+
+        DayOfWeek(int value) {
+            this.value = value;
+        }
+
+        public static DayOfWeek from(int value) {
+            return Arrays.stream(DayOfWeek.values())
+                    .filter(it -> value == it.value)
+                    .findAny()
+                    .orElse(null);
+        }
+    }
+
+    /**
+     * 2023년 12월을 기준으로 현재 날짜에 맞는 요일을 반환합니다.
+     * @return DayOfWeek (0: 일요일 ~ 6: 토요일)
+     */
+    private DayOfWeek getDayOfWeek() {
+        int dayValue = (date + WEEK_DIFF) % WEEK_NUM;
+        return DayOfWeek.from(dayValue);
+    }
+
+    @Override
+    public boolean isWeekend() {
+        DayOfWeek dayOfWeek = getDayOfWeek();
+        return dayOfWeek == DayOfWeek.FRI || dayOfWeek == DayOfWeek.SAT;
+    }
+
+    @Override
+    public boolean isSpecialDay() {
+        return getDayOfWeek() == DayOfWeek.SUN || date == CHRISTMAS;
+    }
+
+    @Override
+    public boolean isNotPassedChristmas() {
+        return date <= CHRISTMAS;
+    }
+
+    @Override
+    public int timePassedSinceFirstDay() {
+        return date - FIRST_DAY;
+    }
+
+    public int getDateValue() {
+        return date;
+    }
+}

리뷰: 이 클래스 굉장히 좋은 것 같아요 Date라는 개념도 잘 담겨있고 크리스마스나 시작 요일을 다른 곳에서 받아오는 방식으로 바꾼다면 해당 정보만 갈아끼우면 계속 동작되는 클래스 같아요.
java.time 이라는 자바 패키지를 하나 추천해드릴게요

```
import java.time.LocalDate;
import java.time.DayOfWeek;
import java.time.format.TextStyle;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        // 특정 날짜 설정
        LocalDate date = LocalDate.of(2023, 11, 24); // 예: 2023년 11월 24일

        // 해당 날짜의 요일을 가져옴
        DayOfWeek dayOfWeek = date.getDayOfWeek();

        // 요일을 문자열로 출력 (예: 한국어로 짧은 형식)
        String dayOfWeekInKorean = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.KOREAN);

        System.out.println("요일 (한국어): " + dayOfWeekInKorean);
    }
}
```
@@ -0,0 +1,75 @@
+package christmas.domain;
+
+import christmas.config.Menu;
+import christmas.config.MenuType;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public enum Event {
+    CHRISTMAS(
+            "크리스마스 디데이 할인",
+            true,
+            Bill::isNotPassedChristmas,
+            bill -> bill.timePassedSinceFirstDay() * 100 + 1000
+    ),
+    WEEKDAY(
+            "평일 할인",
+            true,
+            bill -> !bill.isWeekend(),
+            bill -> bill.getTypeCount(MenuType.DESSERT) * 2023
+    ),
+    WEEKEND(
+            "주말 할인",
+            true,
+            Bill::isWeekend,
+            bill -> bill.getTypeCount(MenuType.MAIN) * 2023
+    ),
+    SPECIAL(
+            "특별 할인",
+            true,
+            Bill::isSpecialDay,
+            bill -> 1000
+    ),
+    CHAMPAGNE(
+            "증정 이벤트",
+            false,
+            bill -> bill.getTotalPrice() >= 120000,
+            bill -> Menu.CHAMPAGNE.getPrice()
+    );
+
+    private static final int ALL_EVENT_THRESHOLD = 10000;
+
+    private final String eventName;
+    private final boolean isDiscount;
+    private final Predicate<Bill> condition;
+    private final Function<Bill, Integer> benefit;
+
+    Event(
+            String eventName,
+            boolean isDiscount,
+            Predicate<Bill> condition,
+            Function<Bill, Integer> benefit
+    ) {
+        this.eventName = eventName;
+        this.isDiscount = isDiscount;
+        this.condition = condition;
+        this.benefit = benefit;
+    }
+
+    public String getEventName() {
+        return eventName;
+    }
+
+    public boolean isDiscount() {
+        return isDiscount;
+    }
+
+    public boolean checkCondition(Bill bill) {
+        return condition.test(bill) && bill.getTotalPrice() >= ALL_EVENT_THRESHOLD;
+    }
+
+    public int getBenefit(Bill bill) {
+        return benefit.apply(bill);
+    }
+}

리뷰: 오 할인이 적용 되었는지 불린값을 넣어두신 것이 매우 좋아보입니다! 배워갑니다.
해당 내용을 뱃지에도 적용하시면 좋을 것 같습니다.
@@ -0,0 +1,52 @@
+package christmas.util;
+
+import christmas.dto.OrderDTO;
+import christmas.config.ErrorMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class OrderParser {
+    private static final String SEQUENCE_DELIMITER = ",";
+    private static final String ORDER_DELIMITER = "-";
+    private static final String ORDER_PATTERN = "[^-]+-[0-9]+";
+    private static final String REPLACE_TARGET = " ";
+    private static final String REPLACEMENT = "";
+
+    private OrderParser() {
+        // 인스턴스 생성 방지
+    }
+
+    public static List<OrderDTO> parseOrderOrThrow(String orderSequence) {
+        List<OrderDTO> result = new ArrayList<>();
+        for (String order : splitOrder(trimAll(orderSequence))) {
+            result.add(parseOrderDTO(order));
+        }
+        return result;
+    }
+
+    private static List<String> splitOrder(String orderSequence) {
+        return List.of(orderSequence.split(SEQUENCE_DELIMITER));
+    }
+
+    private static OrderDTO parseOrderDTO(String order) {
+        validateOrderPattern(order);
+        String[] split = order.split(ORDER_DELIMITER);
+        try {
+            return new OrderDTO(split[0], IntParser.parseIntOrThrow(split[1]));
+        } catch (IndexOutOfBoundsException e) {
+            throw new IllegalArgumentException(ErrorMessage.WRONG_ORDER.getMessage());
+        }
+    }
+
+    private static String trimAll(String target) {
+        return target.replaceAll(REPLACE_TARGET, REPLACEMENT);
+    }
+
+    private static void validateOrderPattern(String order) {
+        if (!Pattern.matches(ORDER_PATTERN, order)) {
+            throw new IllegalArgumentException(ErrorMessage.WRONG_ORDER.getMessage());
+        }
+    }
+}

리뷰: ```suggestion
    private static final Pattern ORDER_PATTERN = Pattern.compile("[^-]+-[0-9]+"); // 컴파일된 패턴
```
@@ -0,0 +1,26 @@
+package christmas.controller;
+
+import christmas.domain.Bill;
+import christmas.domain.Order;
+import christmas.domain.ReservationDay;
+
+import java.math.BigDecimal;
+
+public interface EventController {
+
+    void applyEvent(ReservationDay day, Order order, Bill bill);
+
+    void showEventDiscountDetails(Bill bill);
+
+    void presentEvent(Bill bill);
+
+    void dDayDiscountEvent(ReservationDay reservationDay, Bill bill);
+
+    void weekdayDiscountEvent(ReservationDay day, Bill bill);
+
+    void weekendDiscountEvent(ReservationDay day, Bill bill);
+
+    void specialDayDiscountEvent(ReservationDay day, Bill bill);
+
+    void badgeEvent(BigDecimal totalBenefitAmount);
+}

리뷰: EventController를 interface로 만들어서 이벤트에 대한 로직을 관리하는 것보다 이벤트 관련 도메인을 interface로 만들어서 사용해 보는 건 어떨까요? 객체지향을 다룬 오브젝트 서적에서 비슷한 내용이 있어서 링크 남깁니다. https://github.com/eternity-oop/object/tree/master/chapter13/src/main/java/org/eternity/movie/step02