ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: OutputView.printPreview로 출력을 안하고
EventPlanner에서 다시 OutputView를 접근하시는 이유가 있을까요?
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: 맞습니다 ㅠ . 하나의 클래스 내에서 상수 선언을 하면 더 깔끔하더라구요. 조언 감사드립니다 !!
@@ -0,0 +1,58 @@
+package christmas.domain;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Order {
+
+    private static final int MAX_QUANTITY = 20;
+    private static final String INSUFFICIENT_ORDERLINE_ERROR_MESSAGE = "[ERROR] 주문 항목이 비어 있습니다.";
+    private static final String TOO_MANY_ORDERLINE_QUANTITY_ERROR_MESSAGE = "[ERROR] 메뉴는 한 번에 최대 20개까지만 주문할 수 있습니다.";
+    private static final String ONLY_DRINKS_ERROR_MESSAGE = "[ERROR] 음료만 주문할 수 없습니다.";
+
+    private List<OrderLine> orderLines;
+
+    public Order(List<OrderLine> orderLines) {
+        validateAtLeastOneOrderLine(orderLines);
+        validateTotalQuantity(orderLines);
+        validateOnlyDrinks(orderLines);
+        this.orderLines = orderLines;
+    }
+
+    public Money getTotalAmounts() {
+        return orderLines.stream()
+            .map(OrderLine::getAmounts)
+            .reduce(Money::plus)
+            .get();
+    }
+
+    private void validateAtLeastOneOrderLine(List<OrderLine> orderLines) {
+        if (orderLines == null || orderLines.isEmpty()) {
+            throw new IllegalArgumentException(INSUFFICIENT_ORDERLINE_ERROR_MESSAGE);
+        }
+    }
+
+    private void validateTotalQuantity(List<OrderLine> orderLines) {
+        int totalQuantity = orderLines.stream()
+            .mapToInt(OrderLine::getQuantity)
+            .sum();
+
+        if (totalQuantity > MAX_QUANTITY) {
+            throw new IllegalArgumentException(TOO_MANY_ORDERLINE_QUANTITY_ERROR_MESSAGE);
+        }
+    }
+
+    private void validateOnlyDrinks(List<OrderLine> orderLines) {
+        if (orderLines.stream().allMatch(OrderLine::isDrink)) {
+            throw new IllegalArgumentException(ONLY_DRINKS_ERROR_MESSAGE);
+        }
+    }
+
+    public List<OrderLine> getOrderLines() {
+        return Collections.unmodifiableList(orderLines);
+    }
+
+    public Money getAmountToPay(Money discountAmounts) {
+        return getTotalAmounts().minus(discountAmounts);
+    }
+}

리뷰: 이미 validateOnlyDrinks 라는 메서드 내에서 또 다른 private 메서드로 또 빼게 되면 의미의 중복이 생길 것 같아 굳이 빼지는 않았습니다. 말씀해주신 부분도 다시 고려해보겠습니다 :>
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: 다른 분들 코드를 보니 확실히 도메인 패키지가 많이 뚱뚱한 것 같더라구요...저도 분류하는 것이 더 좋다고 생각합니다!!
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: 현재 EventPlanner를 사용하지 않으면 컨트롤러에서 `.run()` 메서드의 라인 수가 너무 길어지더라구요...
해서 OutputView로 여러줄 두는 것 보단 이 출력이 어떤 출력인지 파악하기 쉽도록 EventPlanner를 사용해서 다시 OutputView를 접근하는 형태를 사용했습니다. 
라인 수를 줄이기 위해 임시방편으로 저렇게 두었는데 더 좋은 방법이 무엇인지 잘 모르겠습니다 🥹
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: 검증과 동시에 예외가 발생하면 재귀호출하는 방식으로 되어있네요! 이것도 하나의 트레이드 오프인것 같은데 stackOverflow가 발생할 가능성을 초래하는 방법입니다.!! while을 사용하는게 정답은 아니지만 대신 stackOverflow는 방지할 수는 있죠 어떻게 생각하셔서 이렇게 구현하셨는지 여쭤봐도 될까요?
@@ -0,0 +1,28 @@
+package christmas.domain;
+
+import static java.util.Comparator.comparingInt;
+
+import java.util.Arrays;
+
+public enum EventBadge {
+    NONE(0, "없음"), STAR(5000, "별"), TREE(10000, "트리"), SANTA(20000, "산타");
+
+    private final int money;
+    private String title;
+
+    EventBadge(int money, String title) {
+        this.money = money;
+        this.title = title;
+    }
+
+    public static EventBadge findByMoney(int input) {
+        return Arrays.stream(EventBadge.values())
+            .filter(eventBadge -> eventBadge.money <= input)
+            .max(comparingInt(o -> o.money))
+            .orElse(NONE);
+    }
+
+    public String getTitle() {
+        return title;
+    }
+}

리뷰: 사실 "없음" 이라는 뱃지는 없는 값이라 없음 자체는 View의 담당인 역할 인것 같아요! none이나 ""로 관리했으면 어땟을까요?
@@ -0,0 +1,32 @@
+package christmas.domain;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public enum TotalDiscountType {
+    CHRISTMAS_D_DAY(IntStream.range(1, 26).boxed().toList()),
+    SPECIAL(List.of(3, 10, 17, 24, 25, 31)),
+    NONE(List.of());
+
+    private List<Integer> days;
+
+    TotalDiscountType(List<Integer> days) {
+        this.days = days;
+    }
+
+    public List<Integer> getDays() {
+        return Collections.unmodifiableList(days);
+    }
+
+    public static TotalDiscountType findSpecialTypeByLocalDate(LocalDate localDate) {
+        int day = localDate.getDayOfMonth();
+
+        return Stream.of(TotalDiscountType.SPECIAL)
+            .filter(specialType -> specialType.days.contains(day))
+            .findAny()
+            .orElse(TotalDiscountType.NONE);
+    }
+}

리뷰: 이부분은 개인적으로 저는 2023년의 12월이라는 요구사항이 있었기때문에 저는 Date객체를 사용했습니다! 물론 저도 이 방법을 고려했었는데 이렇게 날짜를 굳이 넣은 이유를 여쭤봐도 될까요?
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: 좋은 처리 방법에 대해 깊게 고민하지 못했습니다 ㅠ... 다른 분께서 언급하신 프록시 패턴, 펑셔널 인터페이스를 활용해 리팩토링 해보려 합니다 !!
@@ -0,0 +1,28 @@
+package christmas.domain;
+
+import static java.util.Comparator.comparingInt;
+
+import java.util.Arrays;
+
+public enum EventBadge {
+    NONE(0, "없음"), STAR(5000, "별"), TREE(10000, "트리"), SANTA(20000, "산타");
+
+    private final int money;
+    private String title;
+
+    EventBadge(int money, String title) {
+        this.money = money;
+        this.title = title;
+    }
+
+    public static EventBadge findByMoney(int input) {
+        return Arrays.stream(EventBadge.values())
+            .filter(eventBadge -> eventBadge.money <= input)
+            .max(comparingInt(o -> o.money))
+            .orElse(NONE);
+    }
+
+    public String getTitle() {
+        return title;
+    }
+}

리뷰: 오...지금 생각해보니 말씀하신 의견이 더 맞는 것 같습니다. 감사합니다. 🥹
@@ -0,0 +1,32 @@
+package christmas.domain;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public enum TotalDiscountType {
+    CHRISTMAS_D_DAY(IntStream.range(1, 26).boxed().toList()),
+    SPECIAL(List.of(3, 10, 17, 24, 25, 31)),
+    NONE(List.of());
+
+    private List<Integer> days;
+
+    TotalDiscountType(List<Integer> days) {
+        this.days = days;
+    }
+
+    public List<Integer> getDays() {
+        return Collections.unmodifiableList(days);
+    }
+
+    public static TotalDiscountType findSpecialTypeByLocalDate(LocalDate localDate) {
+        int day = localDate.getDayOfMonth();
+
+        return Stream.of(TotalDiscountType.SPECIAL)
+            .filter(specialType -> specialType.days.contains(day))
+            .findAny()
+            .orElse(TotalDiscountType.NONE);
+    }
+}

리뷰: 제가 알기론 java의 Calender와 Date는 지양해 LocalDate나 LocalDateTime을 사용해야하는 것으로 알고 있습니다!
저도 단순하게 정수를 사용하는 것보단 LocalDate를 활용하는 편이 더 좋다고 생각합니다.
@@ -0,0 +1,80 @@
+package christmas.controller;
+
+import christmas.domain.BenefitCalculationFactory;
+import christmas.domain.Benefits;
+import christmas.domain.EventPlanner;
+import christmas.domain.Order;
+import christmas.dtos.OrderLineDto;
+import christmas.util.OrderLineMapper;
+import christmas.view.ErrorMessage;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+import java.time.LocalDate;
+import java.util.List;
+
+public class ChristmasController {
+
+    public static void run() {
+        OutputView.printWelcomeMessage();
+        int day = getExpectedVisitDate();
+
+        Order order = getOrder();
+
+        LocalDate orderDate = LocalDate.of(2023, 12, day);
+
+        Benefits benefits = BenefitCalculationFactory.calculateBenefits(order, orderDate);
+
+        OutputView.printBenefitPreviewMessage(orderDate);
+
+        EventPlanner.printPreview(order, benefits);
+    }
+
+    public static Order getOrder() {
+        try {
+            OutputView.printOrderMenuMessage();
+            List<OrderLineDto> dtos = InputView.readMenus();
+            validateDuplicateMenus(dtos);
+
+            Order order = new Order(OrderLineMapper.orderLineDtosToOrderLines(dtos));
+
+            return order;
+        } catch (IllegalArgumentException e) {
+            OutputView.printExceptionMessage(e);
+            return getOrder();
+        }
+    }
+
+
+    public static int getExpectedVisitDate() {
+        try {
+            OutputView.printExpectedVisitDateMessage();
+            int day = InputView.readInteger();
+            validateExpectedVisitDate(day);
+            return day;
+        } catch (IllegalArgumentException e) {
+
+            System.out.println(e.getMessage());
+            System.out.println(e.getClass());
+            OutputView.printExceptionMessage(e);
+            return getExpectedVisitDate();
+        }
+    }
+
+    private static void validateExpectedVisitDate(int day) {
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_DATE_INPUT.getMessage());
+        }
+    }
+
+    private static void validateDuplicateMenus(List<OrderLineDto> dtos) {
+        int originalSize = dtos.size();
+        int distinctSize = (int) dtos.stream()
+            .map(OrderLineDto::getMenuDetail)
+            .distinct()
+            .count();
+
+        if (originalSize != distinctSize) {
+            throw new IllegalArgumentException(ErrorMessage.DUPLICATE_MENU_INPUT.getMessage());
+        }
+    }
+}

리뷰: 해당 부분은 숫자를 상수로 만들면 어떨까요?