ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,57 @@
+package christmas.domain.menu;
+
+import java.util.Arrays;
+
+import christmas.exception.MenuNotFoundException;
+
+public enum Menu {
+	MUSHROOM_SOUP(MenuType.APPETIZER, "양송이수프", 6_000),
+	TAPAS(MenuType.APPETIZER, "타파스", 5_500),
+	CAESAR_SALAD(MenuType.APPETIZER, "시저샐러드", 8_000),
+	T_BONE_STEAK(MenuType.MAIN, "티본스테이크", 55_000),
+	BARBECUE_RIB(MenuType.MAIN, "바비큐립", 54_000),
+	SEAFOOD_PASTA(MenuType.MAIN, "해산물파스타", 35_000),
+	CHRISTMAS_PASTA(MenuType.MAIN, "크리스마스파스타", 25_000),
+	CHOCOLATE_CAKE(MenuType.DESSERT, "초코케이크", 15_000),
+	ICECREAM(MenuType.DESSERT, "아이스크림", 5_000),
+	ZERO_COKE(MenuType.DRINK, "제로콜라", 3_000),
+	RED_WINE(MenuType.DRINK, "레드와인", 60_000),
+	CHAMPAGNE(MenuType.DRINK, "샴페인", 25_000);
+	private MenuType type;
+	private String name;
+	private int price;
+
+	Menu(MenuType type, String name, int price) {
+		this.type = type;
+		this.name = name;
+		this.price = price;
+	}
+
+	public static Menu from(String input) {
+		return Arrays.stream(Menu.values())
+			.filter(menu -> menu.name.equals(input))
+			.findFirst()
+			.orElseThrow(MenuNotFoundException::new);
+	}
+
+	public MenuType getType() {
+		return this.type;
+	}
+
+	public String getMenuName() {
+		return this.name;
+	}
+
+	public int getPrice() {
+		return this.price;
+	}
+
+	public int getPayment(int quantity) {
+		return (this.price * quantity);
+	}
+
+	public static boolean isMatchTo(String input, MenuType type) {
+		return Arrays.stream(Menu.values())
+			.anyMatch(menu -> menu.name.equals(input) && menu.type.equals(type));
+	}
+}

리뷰: enum 상수들이 세부 카테고리화 될 수 있을 때는 개행을 통해 구분시켜 주면 가독성이 훨씬 좋아집니다!
```suggestion
	CAESAR_SALAD(MenuType.APPETIZER, "시저샐러드", 8_000),
	
	T_BONE_STEAK(MenuType.MAIN, "티본스테이크", 55_000),
```
@@ -0,0 +1,57 @@
+package christmas.domain.menu;
+
+import java.util.Arrays;
+
+import christmas.exception.MenuNotFoundException;
+
+public enum Menu {
+	MUSHROOM_SOUP(MenuType.APPETIZER, "양송이수프", 6_000),
+	TAPAS(MenuType.APPETIZER, "타파스", 5_500),
+	CAESAR_SALAD(MenuType.APPETIZER, "시저샐러드", 8_000),
+	T_BONE_STEAK(MenuType.MAIN, "티본스테이크", 55_000),
+	BARBECUE_RIB(MenuType.MAIN, "바비큐립", 54_000),
+	SEAFOOD_PASTA(MenuType.MAIN, "해산물파스타", 35_000),
+	CHRISTMAS_PASTA(MenuType.MAIN, "크리스마스파스타", 25_000),
+	CHOCOLATE_CAKE(MenuType.DESSERT, "초코케이크", 15_000),
+	ICECREAM(MenuType.DESSERT, "아이스크림", 5_000),
+	ZERO_COKE(MenuType.DRINK, "제로콜라", 3_000),
+	RED_WINE(MenuType.DRINK, "레드와인", 60_000),
+	CHAMPAGNE(MenuType.DRINK, "샴페인", 25_000);
+	private MenuType type;
+	private String name;
+	private int price;
+
+	Menu(MenuType type, String name, int price) {
+		this.type = type;
+		this.name = name;
+		this.price = price;
+	}
+
+	public static Menu from(String input) {
+		return Arrays.stream(Menu.values())
+			.filter(menu -> menu.name.equals(input))
+			.findFirst()
+			.orElseThrow(MenuNotFoundException::new);
+	}
+
+	public MenuType getType() {
+		return this.type;
+	}
+
+	public String getMenuName() {
+		return this.name;
+	}
+
+	public int getPrice() {
+		return this.price;
+	}
+
+	public int getPayment(int quantity) {
+		return (this.price * quantity);
+	}
+
+	public static boolean isMatchTo(String input, MenuType type) {
+		return Arrays.stream(Menu.values())
+			.anyMatch(menu -> menu.name.equals(input) && menu.type.equals(type));
+	}
+}

리뷰: 전달 받는 데이터가 메뉴 이름이라는 것 정도는 최소한으로 정보를 제공해 준다면 해당 메서드의 정의부만 보고도 메서드의 행동을 추론하기 더 쉬워집니다!

```suggestion
	public static Menu from(String name) {
```
@@ -0,0 +1,57 @@
+package christmas.domain.menu;
+
+import java.util.Arrays;
+
+import christmas.exception.MenuNotFoundException;
+
+public enum Menu {
+	MUSHROOM_SOUP(MenuType.APPETIZER, "양송이수프", 6_000),
+	TAPAS(MenuType.APPETIZER, "타파스", 5_500),
+	CAESAR_SALAD(MenuType.APPETIZER, "시저샐러드", 8_000),
+	T_BONE_STEAK(MenuType.MAIN, "티본스테이크", 55_000),
+	BARBECUE_RIB(MenuType.MAIN, "바비큐립", 54_000),
+	SEAFOOD_PASTA(MenuType.MAIN, "해산물파스타", 35_000),
+	CHRISTMAS_PASTA(MenuType.MAIN, "크리스마스파스타", 25_000),
+	CHOCOLATE_CAKE(MenuType.DESSERT, "초코케이크", 15_000),
+	ICECREAM(MenuType.DESSERT, "아이스크림", 5_000),
+	ZERO_COKE(MenuType.DRINK, "제로콜라", 3_000),
+	RED_WINE(MenuType.DRINK, "레드와인", 60_000),
+	CHAMPAGNE(MenuType.DRINK, "샴페인", 25_000);
+	private MenuType type;
+	private String name;
+	private int price;
+
+	Menu(MenuType type, String name, int price) {
+		this.type = type;
+		this.name = name;
+		this.price = price;
+	}
+
+	public static Menu from(String input) {
+		return Arrays.stream(Menu.values())
+			.filter(menu -> menu.name.equals(input))
+			.findFirst()
+			.orElseThrow(MenuNotFoundException::new);
+	}
+
+	public MenuType getType() {
+		return this.type;
+	}
+
+	public String getMenuName() {
+		return this.name;
+	}
+
+	public int getPrice() {
+		return this.price;
+	}
+
+	public int getPayment(int quantity) {
+		return (this.price * quantity);
+	}
+
+	public static boolean isMatchTo(String input, MenuType type) {
+		return Arrays.stream(Menu.values())
+			.anyMatch(menu -> menu.name.equals(input) && menu.type.equals(type));
+	}
+}

리뷰: 메뉴가 어떤 타입인지 판단하는 것은 메뉴 클래스의 책임이라고 생각해요.
getter를 만드는 대신 `boolean isTypeOf(MenuType expectedType)` 과 같은 메서드를 구현해 보는 건 어떨까요?
해당 클래스의 캡슐화가 더 잘 제공될 것 같습니다.
@@ -0,0 +1,36 @@
+package christmas.domain.event;
+
+import java.time.LocalDate;
+
+import christmas.domain.menu.Menu;
+import christmas.domain.order.OrderSheet;
+import christmas.dto.result.EventResult;
+import christmas.dto.result.PresentEventResult;
+
+public class PresentEvent extends Event{
+	private final Menu present = Menu.CHAMPAGNE;
+	private static final int presentQuantity = 1;
+	private static final int requiredPayment = 120_000;
+
+	public PresentEvent(EventType eventType, LocalDate startDate, LocalDate endDate) {
+		super(eventType, startDate, endDate);
+	}
+
+	@Override
+	protected boolean isNotSatisfiedBy(OrderSheet orderSheet) {
+		return isDayOfWeekNotInDuration(orderSheet.getVisitDate()) || isLessThanRequiredPayment(orderSheet.getTotalPayment());
+	}
+
+	@Override
+	public EventResult getEventBenefits(OrderSheet orderSheet) {
+		if (isNotSatisfiedBy(orderSheet)) {
+			return null;
+		}
+
+		return new PresentEventResult(eventType.getName(), present.getPrice(), Menu.CHAMPAGNE, presentQuantity);
+	}
+
+	private boolean isLessThanRequiredPayment(int totalPayment) {
+		return totalPayment < requiredPayment;
+	}
+}

리뷰: 한 줄에서 코드가 너무 길어진다면 적절히 개행을 해 주어 가독성을 높여줄 수 있는데요
보통 논리연산자로 코드가 연결되어 있다면 논리연산자 앞에서 개행을 시켜주는 것이 일반적이라 합니다!
[네이버 캠퍼스 핵데이 컨벤션](https://naver.github.io/hackday-conventions-java/)
```suggestion
		return isDayOfWeekNotInDuration(orderSheet.getVisitDate()) 
		        || isLessThanRequiredPayment(orderSheet.getTotalPayment());
```
@@ -0,0 +1,28 @@
+package christmas.dto.order;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.YearMonth;
+
+import christmas.exception.IllegalDateException;
+
+public class VisitDate {
+	private final YearMonth visitYearMonth = YearMonth.of(2023, 12);
+	private final LocalDate visitDate;
+
+	public VisitDate(int input) {
+		this.visitDate = convertBy(input);
+	}
+
+	public LocalDate getVisitDate() {
+		return this.visitDate;
+	}
+
+	private LocalDate convertBy(int input) {
+		try {
+			return visitYearMonth.atDay(input);
+		} catch (DateTimeException e) {
+			throw new IllegalDateException();
+		}
+	}
+}

리뷰: 해당 클래스가 날짜의 정보를 가지고 있는 만큼 이 클래스는 DTO의 역할만 수행하기 보단, 해당 날짜가 주말인지 등의 정보를 제공하는 메서드들이 구현되어 있는 것이 더 좋을 것 같습니다.
코드의 응집도가 높아질 수 있을 것 같아요
@@ -0,0 +1,36 @@
+package christmas.domain.event;
+
+import java.time.LocalDate;
+
+import christmas.domain.menu.Menu;
+import christmas.domain.order.OrderSheet;
+import christmas.dto.result.EventResult;
+import christmas.dto.result.PresentEventResult;
+
+public class PresentEvent extends Event{
+	private final Menu present = Menu.CHAMPAGNE;
+	private static final int presentQuantity = 1;
+	private static final int requiredPayment = 120_000;
+
+	public PresentEvent(EventType eventType, LocalDate startDate, LocalDate endDate) {
+		super(eventType, startDate, endDate);
+	}
+
+	@Override
+	protected boolean isNotSatisfiedBy(OrderSheet orderSheet) {
+		return isDayOfWeekNotInDuration(orderSheet.getVisitDate()) || isLessThanRequiredPayment(orderSheet.getTotalPayment());
+	}
+
+	@Override
+	public EventResult getEventBenefits(OrderSheet orderSheet) {
+		if (isNotSatisfiedBy(orderSheet)) {
+			return null;
+		}
+
+		return new PresentEventResult(eventType.getName(), present.getPrice(), Menu.CHAMPAGNE, presentQuantity);
+	}
+
+	private boolean isLessThanRequiredPayment(int totalPayment) {
+		return totalPayment < requiredPayment;
+	}
+}

리뷰: 훨씬 깔끔해진 것 같네요 :) 감사합니다!
@@ -0,0 +1,57 @@
+package christmas.domain.menu;
+
+import java.util.Arrays;
+
+import christmas.exception.MenuNotFoundException;
+
+public enum Menu {
+	MUSHROOM_SOUP(MenuType.APPETIZER, "양송이수프", 6_000),
+	TAPAS(MenuType.APPETIZER, "타파스", 5_500),
+	CAESAR_SALAD(MenuType.APPETIZER, "시저샐러드", 8_000),
+	T_BONE_STEAK(MenuType.MAIN, "티본스테이크", 55_000),
+	BARBECUE_RIB(MenuType.MAIN, "바비큐립", 54_000),
+	SEAFOOD_PASTA(MenuType.MAIN, "해산물파스타", 35_000),
+	CHRISTMAS_PASTA(MenuType.MAIN, "크리스마스파스타", 25_000),
+	CHOCOLATE_CAKE(MenuType.DESSERT, "초코케이크", 15_000),
+	ICECREAM(MenuType.DESSERT, "아이스크림", 5_000),
+	ZERO_COKE(MenuType.DRINK, "제로콜라", 3_000),
+	RED_WINE(MenuType.DRINK, "레드와인", 60_000),
+	CHAMPAGNE(MenuType.DRINK, "샴페인", 25_000);
+	private MenuType type;
+	private String name;
+	private int price;
+
+	Menu(MenuType type, String name, int price) {
+		this.type = type;
+		this.name = name;
+		this.price = price;
+	}
+
+	public static Menu from(String input) {
+		return Arrays.stream(Menu.values())
+			.filter(menu -> menu.name.equals(input))
+			.findFirst()
+			.orElseThrow(MenuNotFoundException::new);
+	}
+
+	public MenuType getType() {
+		return this.type;
+	}
+
+	public String getMenuName() {
+		return this.name;
+	}
+
+	public int getPrice() {
+		return this.price;
+	}
+
+	public int getPayment(int quantity) {
+		return (this.price * quantity);
+	}
+
+	public static boolean isMatchTo(String input, MenuType type) {
+		return Arrays.stream(Menu.values())
+			.anyMatch(menu -> menu.name.equals(input) && menu.type.equals(type));
+	}
+}

리뷰: 확실히 위와 같이 구현하는게 캡슐화 측면에서 더 좋을 것 같네요  꼼꼼한 리뷰 감사합니다 😄
@@ -0,0 +1,28 @@
+package christmas.dto.order;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.YearMonth;
+
+import christmas.exception.IllegalDateException;
+
+public class VisitDate {
+	private final YearMonth visitYearMonth = YearMonth.of(2023, 12);
+	private final LocalDate visitDate;
+
+	public VisitDate(int input) {
+		this.visitDate = convertBy(input);
+	}
+
+	public LocalDate getVisitDate() {
+		return this.visitDate;
+	}
+
+	private LocalDate convertBy(int input) {
+		try {
+			return visitYearMonth.atDay(input);
+		} catch (DateTimeException e) {
+			throw new IllegalDateException();
+		}
+	}
+}

리뷰: VisitDate 내부에서 주말인지 확인하는 메서드가 있었다면 Event 객체 내부에서 getter를 사용하지 않아도 되었겠네요 👍
@@ -0,0 +1,42 @@
+package christmas.view;
+
+import static christmas.controller.Parser.parseDate;
+import static christmas.controller.Parser.parseMenus;
+
+import camp.nextstep.edu.missionutils.Console;
+import christmas.exception.ErrorMessage;
+import christmas.exception.PromotionException;
+import java.util.List;
+
+public class InputView {
+    private static final String DATE_PATTERN = "[\\d]{1,2}";
+    private static final String MENUS_PATTERN = "(([가-힣]+)-([\\d]{1,2}),)*([가-힣]+)-([\\d]{1,2})";
+
+    public int readVisitDate() {
+        System.out.println("12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)");
+        String date = Console.readLine();
+        validateDateInput(date);
+        return parseDate(date);
+    }
+
+    private void validateDateInput(String input) {
+        if (input.matches(DATE_PATTERN)) {
+            return;
+        }
+        throw new PromotionException(ErrorMessage.INVALID_DATE_MESSAGE);
+    }
+
+    public List<String> readMenus() {
+        System.out.println("주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)");
+        String menus = Console.readLine().trim();
+        validateMenuInput(menus);
+        return parseMenus(menus, ",");
+    }
+
+    private void validateMenuInput(String menus) {
+        if (menus.matches(MENUS_PATTERN)) {
+            return;
+        }
+        throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+    }
+}

리뷰: 이거 할려다가 포기했는데, 정말 대단하십니다...
@@ -0,0 +1,21 @@
+package christmas.controller;
+
+import christmas.dto.MenuCount;
+import java.util.Arrays;
+import java.util.List;
+
+public class Parser {
+
+    public static int parseDate(String input) {
+        return Integer.parseInt(input);
+    }
+
+    public static List<String> parseMenus(String input, String delimiter) {
+        return Arrays.stream(input.split(delimiter)).toList();
+    }
+
+    public static MenuCount parseMenu(String input, String delimiter) {
+        String[] split = input.split(delimiter);
+        return new MenuCount(split[0], Integer.parseInt(split[1]));
+    }
+}

리뷰: 저는 Parsing 자체를 InputView에서 했는데, 이걸 Parser로 분리하신 이유가 있으실까요?
@@ -0,0 +1,85 @@
+package christmas.domain;
+
+import christmas.domain.menu.Category;
+import christmas.domain.menu.Menu;
+import christmas.exception.ErrorMessage;
+import christmas.exception.PromotionException;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class Menus {
+    public static final int MIN_MENU_COUNT = 1;
+    public static final int MAX_ORDER_COUNT = 20;
+    private final Map<Menu, Integer> menus = new EnumMap<>(Menu.class);
+
+    public Map<Menu, Integer> getMenus() {
+        return menus;
+    }
+
+    public void add(String menu, int count) {
+        validate(menu, count);
+        menus.put(Menu.fromDescription(menu), count);
+    }
+
+    private void validate(String menu, int count) {
+        validateMenuExists(menu);
+        validateMenuNotDuplicated(menu);
+        validateCount(count);
+        validateTotalCount(count);
+    }
+
+    public boolean isAllInCategory(Category category) {
+        return menus.keySet().stream()
+                .map(Menu::category)
+                .allMatch(c -> c.equals(category));
+    }
+
+    public int countByCategory(Category category) {
+        return menus.keySet().stream()
+                .filter(key -> category.equals(key.category()))
+                .mapToInt(menus::get)
+                .sum();
+    }
+
+    public int totalCount() {
+        return menus.values().stream()
+                .mapToInt(Integer::intValue)
+                .sum();
+    }
+
+    public int totalAmount() {
+        return menus.entrySet().stream()
+                .mapToInt(this::itemPrice)
+                .sum();
+    }
+
+    private int itemPrice(Entry<Menu, Integer> item) {
+        return item.getKey().price() * item.getValue();
+    }
+
+    private void validateMenuExists(String menu) {
+        if (Menu.exists(menu)) {
+            return;
+        }
+        throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+    }
+
+    private void validateCount(int count) {
+        if (count < MIN_MENU_COUNT) {
+            throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private void validateMenuNotDuplicated(String menu) {
+        if (menus.containsKey(Menu.fromDescription(menu))) {
+            throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    private void validateTotalCount(int count) {
+        if (totalCount() + count > MAX_ORDER_COUNT) {
+            throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+        }
+    }
+}

리뷰: 검증 과정을 각 메서드별로 나누신 것이 인상깊습니다!
@@ -0,0 +1,74 @@
+package christmas.domain;
+
+import christmas.domain.event.Event;
+import christmas.domain.event.Giveaway;
+import christmas.domain.menu.Category;
+import christmas.domain.menu.Menu;
+import christmas.dto.MenuCount;
+import christmas.exception.ErrorMessage;
+import christmas.exception.PromotionException;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class Order {
+    private final Menus menus;
+    private final Date date;
+
+    public Order(Date date, Menus menus) {
+        validateCategory(menus);
+        this.date = date;
+        this.menus = menus;
+    }
+
+    public int getDate() {
+        return date.getDate();
+    }
+
+    public Map<Menu, Integer> getOrderMenu() {
+        return menus.getMenus();
+    }
+
+    private void validateCategory(Menus menus) {
+        if (menus.isAllInCategory(Category.BEVERAGE)) {
+            throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+        }
+    }
+
+    public int totalAmount() {
+        return menus.totalAmount();
+    }
+
+    public Optional<MenuCount> giveGiveaway() {
+        Giveaway giveaway = (Giveaway) Event.GIVEAWAY.policy();
+        return giveaway.giveGiveaway(date, menus);
+    }
+
+    public Map<Event, Integer> benefitDetails() {
+        return Arrays.stream(Event.values())
+                .map(event -> new SimpleEntry<>(event, event.benefitAmount(date, menus)))
+                .filter(entry -> entry.getValue() < 0)
+                .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (x, y) -> y, LinkedHashMap::new));
+    }
+
+    public int totalBenefit() {
+        return Arrays.stream(Event.values())
+                .mapToInt(event -> event.benefitAmount(date, menus))
+                .sum();
+    }
+
+    public int totalDiscount() {
+        return Arrays.stream(Event.values())
+                .filter(event -> event != Event.GIVEAWAY)
+                .mapToInt(event -> event.benefitAmount(date, menus))
+                .sum();
+    }
+
+    public int finalAmount() {
+        return totalAmount() + totalDiscount();
+    }
+}

리뷰: 해당 검증 과정은 Menus에서 하는게 어떨까요? Menu 자체가 생성되지 않도록 하는 편이 개인적으로 더 좋다고 생각합니다.
@@ -0,0 +1,33 @@
+package christmas.domain.event;
+
+import christmas.domain.Date;
+import christmas.domain.Menus;
+import christmas.domain.menu.Menu;
+import christmas.dto.MenuCount;
+import java.util.Optional;
+
+public class Giveaway implements EventPolicy {
+    private static final int MIN_TOTAL_AMOUNT = 120_000; //shadowing
+    private static final Menu giveaway = Menu.CHAMPAGNE;
+    private static final int numGiveaway = 1;
+
+    @Override
+    public boolean canBeApplied(Date date, Menus menus) {
+        return menus.totalAmount() >= MIN_TOTAL_AMOUNT;
+    }
+
+    @Override
+    public int amount(Date date, Menus menus) {
+        if (canBeApplied(date, menus)) {
+            return -1 * numGiveaway * giveaway.price();
+        }
+        return NONE;
+    }
+
+    public Optional<MenuCount> giveGiveaway(Date date, Menus menus) {
+        if (canBeApplied(date, menus)) {
+            return Optional.of(new MenuCount(giveaway.description(), numGiveaway));
+        }
+        return Optional.empty();
+    }
+}

리뷰: `//shadowing` 주석의 의미는 무엇일까요?
@@ -0,0 +1,42 @@
+package christmas.view;
+
+import static christmas.controller.Parser.parseDate;
+import static christmas.controller.Parser.parseMenus;
+
+import camp.nextstep.edu.missionutils.Console;
+import christmas.exception.ErrorMessage;
+import christmas.exception.PromotionException;
+import java.util.List;
+
+public class InputView {
+    private static final String DATE_PATTERN = "[\\d]{1,2}";
+    private static final String MENUS_PATTERN = "(([가-힣]+)-([\\d]{1,2}),)*([가-힣]+)-([\\d]{1,2})";
+
+    public int readVisitDate() {
+        System.out.println("12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)");
+        String date = Console.readLine();
+        validateDateInput(date);
+        return parseDate(date);
+    }
+
+    private void validateDateInput(String input) {
+        if (input.matches(DATE_PATTERN)) {
+            return;
+        }
+        throw new PromotionException(ErrorMessage.INVALID_DATE_MESSAGE);
+    }
+
+    public List<String> readMenus() {
+        System.out.println("주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)");
+        String menus = Console.readLine().trim();
+        validateMenuInput(menus);
+        return parseMenus(menus, ",");
+    }
+
+    private void validateMenuInput(String menus) {
+        if (menus.matches(MENUS_PATTERN)) {
+            return;
+        }
+        throw new PromotionException(ErrorMessage.INVALID_ORDER_MESSAGE);
+    }
+}

리뷰: `private static final DATE_REQUEST_MESSAGE = "12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)"`
이렇게 사용하는 것이 더 좋다고 생각합니다!
@@ -0,0 +1,74 @@
+package christmas.domain;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import christmas.exception.ErrorMessage;
+import christmas.exception.PromotionException;
+import java.time.DayOfWeek;
+import java.util.List;
+import java.util.stream.IntStream;
+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;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class DateTest {
+
+    @DisplayName("날짜 도메인 생성 테스트")
+    @Test
+    void create() {
+        IntStream.range(1, 32)
+                .forEach(date -> assertDoesNotThrow(() -> new Date(date)));
+    }
+
+    @DisplayName("날짜 도메인_범위 밖의 날짜면 예외 발생")
+    @ParameterizedTest
+    @ValueSource(ints = {0, 32})
+    void createOutRange(int date) {
+        assertThatThrownBy(() -> new Date(date))
+                .isInstanceOf(PromotionException.class)
+                .hasMessage(ErrorMessage.INVALID_DATE_MESSAGE.message());
+    }
+
+    @DisplayName("날짜 -> 요일 테스트")
+    @ParameterizedTest
+    @CsvSource({"1,FRIDAY", "25,MONDAY", "31,SUNDAY"})
+    void dayOfWeek(int date, String expected) {
+        DayOfWeek dayOfWeek = new Date(date).dayOfWeek();
+
+        assertThat(dayOfWeek.name()).isEqualTo(expected);
+    }
+
+    @DisplayName("특정날들에 해당 날짜가 포함되는지 테스트")
+    @ParameterizedTest
+    @CsvSource({"1,true", "25,false"})
+    void isIncluded(int dateSource, boolean expected) {
+        Date date = new Date(dateSource);
+        List<Integer> dates = List.of(1, 2);
+
+        assertThat(date.isIncluded(dates)).isEqualTo(expected);
+    }
+
+    @DisplayName("특정 기간에 해당날짜가 포함되는지 테스트")
+    @ParameterizedTest
+    @CsvSource({"1,true", "26,false"})
+    void isInRange(int dateSource, boolean expected) {
+        Date date = new Date(dateSource);
+
+        assertThat(date.isInRange(1, 25))
+                .isEqualTo(expected);
+    }
+
+    @DisplayName("해당 날짜가 특정날로부터 몇번째 날인지 구하는 기능 테스트")
+    @ParameterizedTest
+    @CsvSource({"1,1,0", "31,25,6"})
+    void isInRange(int dateSource, int from, int expectedDay) {
+        Date date = new Date(dateSource);
+
+        assertThat(date.dayFromDate(from))
+                .isEqualTo(expectedDay);
+    }
+}

리뷰: 전반적으로 테스트를 참 깔끔하게 작성하셨네요! 잘 배우고 갑니다!