ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,67 @@
+import CalculateTotalOrderAmount from '../../model/calculate_total_order_amount.js';
+import CalculateEventDetails from '../../model/calculate_event_details.js';
+import PrintEventResult from '../../view/print_event_result.js';
+
+class EventManage {
+  #visitDate;
+  #orderMenu;
+
+  constructor(visitDate, orderMenu) {
+    this.#visitDate = Number(visitDate);
+    this.#orderMenu = orderMenu;
+  }
+
+  totalOrderAmount() {
+    const TOTAL_AMOUNT = this.#calculateTotalOrderAmount();
+    if (TOTAL_AMOUNT < 10000) {
+      return this.#printEventResult(TOTAL_AMOUNT, 0, null);
+    }
+    return this.#eventBenefit(TOTAL_AMOUNT);
+  }
+
+  #eventBenefit(TOTAL_AMOUNT) {
+    const EVENT_DETAILS = this.#calculateEventDetails();
+    EVENT_DETAILS.giveaway = TOTAL_AMOUNT >= 120000 ? 25000 : 0;
+    const BENEFIT_AMOUNT = Object.values(EVENT_DETAILS).reduce((acc, cur) => acc + cur, 0);
+    this.#printEventResult(TOTAL_AMOUNT, BENEFIT_AMOUNT, EVENT_DETAILS);
+  }
+
+  #calculateTotalOrderAmount() {
+    const calculateTotalOrderAmount = new CalculateTotalOrderAmount();
+    return calculateTotalOrderAmount.result(this.#orderMenu);
+  }
+
+  #calculateEventDetails() {
+    const calculateEventDetails = new CalculateEventDetails();
+    return calculateEventDetails.result(this.#visitDate, this.#orderMenu);
+  }
+
+  #eventBadgeResult(BENEFIT_AMOUNT) {
+    if (BENEFIT_AMOUNT < 5000) {
+      return '없음';
+    }
+    if (BENEFIT_AMOUNT < 10000) {
+      return '별';
+    }
+    if (BENEFIT_AMOUNT < 20000) {
+      return '트리';
+    }
+    return '산타';
+  }
+
+  #printEventResult(TOTAL_AMOUNT, BENEFIT_AMOUNT, EVENT_DETAILS) {
+    const GIVEAWAY_MENU = TOTAL_AMOUNT >= 120000 ? '샴페인 1개' : '없음';
+    const EVENT_DETAILS_RESULT = EVENT_DETAILS === null ? '없음' : EVENT_DETAILS;
+    const BENEFIT_AMOUNT_RESULT = BENEFIT_AMOUNT === 0 ? '없음' : BENEFIT_AMOUNT;
+    const DISCOUNT_AMOUNT = TOTAL_AMOUNT >= 120000 ? BENEFIT_AMOUNT - 25000 : BENEFIT_AMOUNT;
+    const EXPECT_PAYMENT_AMOUNT = TOTAL_AMOUNT - DISCOUNT_AMOUNT;
+    const BADGE = this.#eventBadgeResult(BENEFIT_AMOUNT);
+
+    const AMOUNT_RESULT = { TOTAL_AMOUNT, BENEFIT_AMOUNT_RESULT, EXPECT_PAYMENT_AMOUNT };
+    const BENEFIT_RESULT = { GIVEAWAY_MENU, EVENT_DETAILS_RESULT, BADGE };
+    const printEventResult = new PrintEventResult();
+    printEventResult.print(AMOUNT_RESULT, BENEFIT_RESULT);
+  }
+}
+
+export default EventManage;

리뷰: 10,000도 상수로 관리해주면 더욱 좋을 것 같습니다. 의미를 파악하기도 수월하고, 만약 조건이 변경되는 상황이 있는 경우 수정하기 수월하다고 생각합니다.
@@ -0,0 +1,67 @@
+import CalculateTotalOrderAmount from '../../model/calculate_total_order_amount.js';
+import CalculateEventDetails from '../../model/calculate_event_details.js';
+import PrintEventResult from '../../view/print_event_result.js';
+
+class EventManage {
+  #visitDate;
+  #orderMenu;
+
+  constructor(visitDate, orderMenu) {
+    this.#visitDate = Number(visitDate);
+    this.#orderMenu = orderMenu;
+  }
+
+  totalOrderAmount() {
+    const TOTAL_AMOUNT = this.#calculateTotalOrderAmount();
+    if (TOTAL_AMOUNT < 10000) {
+      return this.#printEventResult(TOTAL_AMOUNT, 0, null);
+    }
+    return this.#eventBenefit(TOTAL_AMOUNT);
+  }
+
+  #eventBenefit(TOTAL_AMOUNT) {
+    const EVENT_DETAILS = this.#calculateEventDetails();
+    EVENT_DETAILS.giveaway = TOTAL_AMOUNT >= 120000 ? 25000 : 0;
+    const BENEFIT_AMOUNT = Object.values(EVENT_DETAILS).reduce((acc, cur) => acc + cur, 0);
+    this.#printEventResult(TOTAL_AMOUNT, BENEFIT_AMOUNT, EVENT_DETAILS);
+  }
+
+  #calculateTotalOrderAmount() {
+    const calculateTotalOrderAmount = new CalculateTotalOrderAmount();
+    return calculateTotalOrderAmount.result(this.#orderMenu);
+  }
+
+  #calculateEventDetails() {
+    const calculateEventDetails = new CalculateEventDetails();
+    return calculateEventDetails.result(this.#visitDate, this.#orderMenu);
+  }
+
+  #eventBadgeResult(BENEFIT_AMOUNT) {
+    if (BENEFIT_AMOUNT < 5000) {
+      return '없음';
+    }
+    if (BENEFIT_AMOUNT < 10000) {
+      return '별';
+    }
+    if (BENEFIT_AMOUNT < 20000) {
+      return '트리';
+    }
+    return '산타';
+  }
+
+  #printEventResult(TOTAL_AMOUNT, BENEFIT_AMOUNT, EVENT_DETAILS) {
+    const GIVEAWAY_MENU = TOTAL_AMOUNT >= 120000 ? '샴페인 1개' : '없음';
+    const EVENT_DETAILS_RESULT = EVENT_DETAILS === null ? '없음' : EVENT_DETAILS;
+    const BENEFIT_AMOUNT_RESULT = BENEFIT_AMOUNT === 0 ? '없음' : BENEFIT_AMOUNT;
+    const DISCOUNT_AMOUNT = TOTAL_AMOUNT >= 120000 ? BENEFIT_AMOUNT - 25000 : BENEFIT_AMOUNT;
+    const EXPECT_PAYMENT_AMOUNT = TOTAL_AMOUNT - DISCOUNT_AMOUNT;
+    const BADGE = this.#eventBadgeResult(BENEFIT_AMOUNT);
+
+    const AMOUNT_RESULT = { TOTAL_AMOUNT, BENEFIT_AMOUNT_RESULT, EXPECT_PAYMENT_AMOUNT };
+    const BENEFIT_RESULT = { GIVEAWAY_MENU, EVENT_DETAILS_RESULT, BADGE };
+    const printEventResult = new PrintEventResult();
+    printEventResult.print(AMOUNT_RESULT, BENEFIT_RESULT);
+  }
+}
+
+export default EventManage;

리뷰: 사소하지만, 조건을 역순으로 적었다면 더 이해하기 수월할 것 같습니다.
`20000 이상: 산타 / 10000 이상: 트리 / 5000 이상: 별 / 나머지: 없음`
@@ -0,0 +1,28 @@
+const EVENT_NAMES = ['크리스마스 디데이 할인', '평일 할인', '주말 할인', '특별 할인', '증정 이벤트'];
+
+const WEEKDAY_DISCOUNT_DATE = [
+  3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17,
+  18, 19, 20, 21, 24, 25, 26, 27, 28, 31,
+];
+
+const WEEKEND_DISCOUNT_DATE = [
+  1, 2, 8, 9, 15, 16, 22, 23, 29, 30,
+];
+
+const SPECIAL_DISCOUNT_DATE = [
+  3, 10, 17, 24, 25, 31,
+];
+
+Object.freeze(
+  EVENT_NAMES,
+  WEEKDAY_DISCOUNT_DATE,
+  WEEKEND_DISCOUNT_DATE,
+  SPECIAL_DISCOUNT_DATE,
+);
+
+export {
+  EVENT_NAMES,
+  WEEKDAY_DISCOUNT_DATE,
+  WEEKEND_DISCOUNT_DATE,
+  SPECIAL_DISCOUNT_DATE,
+};

리뷰: 여러 개의 인자를 넘겨주고 freeze 메서드를 호출하면 프리징되지 않을 것 같습니다.
@@ -0,0 +1,33 @@
+const MENU_NAMES = [
+  '양송이수프', '타파스', '시저샐러드',
+  '티본스테이크', '바비큐립', '해산물파스타', '크리스마스파스타',
+  '초코케이크', '아이스크림',
+  '제로콜라', '레드와인', '샴페인',
+];
+
+const MENUS = {
+  Epitizer: {
+    양송이수프: 6000,
+    타파스: 5500,
+    시저샐러드: 8000,
+  },
+  Main: {
+    티본스테이크: 55000,
+    바비큐립: 54000,
+    해산물파스타: 35000,
+    크리스마스파스타: 25000,
+  },
+  Dessert: {
+    초코케이크: 15000,
+    아이스크림: 5000,
+  },
+  Drink: {
+    제로콜라: 3000,
+    레드와인: 60000,
+    샴페인: 25000,
+  },
+};
+
+Object.freeze(MENU_NAMES, MENUS);
+
+export { MENU_NAMES, MENUS };

리뷰: 중첩된 객체의 경우, 외부를 프리징 해주어도 내부는 프리징 되지 않습니다. deep freeze 키워드로 학습해보는 것을 추천드립니다.
@@ -0,0 +1,31 @@
+package dto;
+
+import domain.car.Car;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static util.Splitter.BLANK;
+
+public class CarDto {
+    private String name;
+    private List<String> whiteSpaceList;
+
+    public CarDto(Car car) {
+        assert car != null;
+        this.name = car.getName();
+        this.whiteSpaceList = IntStream.range(0, car.getPosition())
+                .mapToObj(num -> BLANK)
+                .collect(Collectors.toList());
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List<String> getWhiteSpaceList() {
+        return whiteSpaceList;
+    }
+
+}

리뷰: carDto의 position은 생성자에서 whiteSpaceList 만드는 걸 제외하곤 쓰는데가 없는 것 같은데, carDto에서 빼는 건 어떻게 생각하시나요?
@@ -0,0 +1,79 @@
+package view;
+
+import domain.car.Car;
+import domain.game.RacingGame;
+import domain.game.Track;
+import domain.random.RandomGenerator;
+import domain.strategy.RandomMovingStrategy;
+import dto.CarDto;
+import dto.RacingGameRequestDto;
+import spark.Route;
+import util.Splitter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+import static java.util.stream.Collectors.toList;
+import static util.RenderUtil.render;
+import static util.Splitter.COMMA;
+
+public class WebRacingHandler {
+    public Route handleGetName() {
+        return (req, res) -> {
+            HashMap<String, Object> model = new HashMap<>();
+            String names = req.queryParams("names");
+
+            List<String> splitNames = Splitter.splitBlank(names);
+            model.put("names", splitNames);
+            model.put("nameStrings", String.join(COMMA, splitNames));
+            return render(model, "/game.html");
+        };
+    }
+
+    public Route handleResult() {
+        return (req, res) -> {
+            final HashMap<String, Object> model = new HashMap<>();
+            final RacingGameRequestDto racingGameRequestDto = new RacingGameRequestDto(Integer.parseInt(req.queryParams("turn")), req.queryParams("names"));
+
+            final RacingGame racingGame = generateRacingGame(racingGameRequestDto);
+
+            finishGame(racingGame);
+            Track track = racingGame.getTrack();
+
+            List<CarDto> carDtos = mapToCarDto(track);
+            List<String> nameOfWinners = mapToName(racingGame);
+
+
+            model.put("cars", carDtos);
+            model.put("winners", nameOfWinners);
+            return render(model, "/result.html");
+        };
+    }
+
+    private void finishGame(RacingGame racingGame) {
+        while (!racingGame.isDone()) {
+            racingGame.proceedOneTurn();
+        }
+    }
+
+    private RacingGame generateRacingGame(RacingGameRequestDto racingGameRequestDto) {
+        final Random generate = RandomGenerator.generate();
+        final RandomMovingStrategy movingStrategy = new RandomMovingStrategy(generate);
+        return RacingGame.newInstance(racingGameRequestDto.getNames(), racingGameRequestDto.getTurn(), movingStrategy);
+    }
+
+    private List<String> mapToName(RacingGame racingGame) {
+        return racingGame.getWinner()
+                .stream()
+                .map(Car::getName)
+                .collect(toList());
+    }
+
+    private List<CarDto> mapToCarDto(Track track) {
+        return track.getCars()
+                .stream()
+                .map(CarDto::new)
+                .collect(toList());
+    }
+}

리뷰: 게임시작에 필요한 input과 결과값인 result를 가져오는 로직을 메소드로 분리하면 더 깔끔해 질거 같아요
@@ -0,0 +1,79 @@
+package view;
+
+import domain.car.Car;
+import domain.game.RacingGame;
+import domain.game.Track;
+import domain.random.RandomGenerator;
+import domain.strategy.RandomMovingStrategy;
+import dto.CarDto;
+import dto.RacingGameRequestDto;
+import spark.Route;
+import util.Splitter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+import static java.util.stream.Collectors.toList;
+import static util.RenderUtil.render;
+import static util.Splitter.COMMA;
+
+public class WebRacingHandler {
+    public Route handleGetName() {
+        return (req, res) -> {
+            HashMap<String, Object> model = new HashMap<>();
+            String names = req.queryParams("names");
+
+            List<String> splitNames = Splitter.splitBlank(names);
+            model.put("names", splitNames);
+            model.put("nameStrings", String.join(COMMA, splitNames));
+            return render(model, "/game.html");
+        };
+    }
+
+    public Route handleResult() {
+        return (req, res) -> {
+            final HashMap<String, Object> model = new HashMap<>();
+            final RacingGameRequestDto racingGameRequestDto = new RacingGameRequestDto(Integer.parseInt(req.queryParams("turn")), req.queryParams("names"));
+
+            final RacingGame racingGame = generateRacingGame(racingGameRequestDto);
+
+            finishGame(racingGame);
+            Track track = racingGame.getTrack();
+
+            List<CarDto> carDtos = mapToCarDto(track);
+            List<String> nameOfWinners = mapToName(racingGame);
+
+
+            model.put("cars", carDtos);
+            model.put("winners", nameOfWinners);
+            return render(model, "/result.html");
+        };
+    }
+
+    private void finishGame(RacingGame racingGame) {
+        while (!racingGame.isDone()) {
+            racingGame.proceedOneTurn();
+        }
+    }
+
+    private RacingGame generateRacingGame(RacingGameRequestDto racingGameRequestDto) {
+        final Random generate = RandomGenerator.generate();
+        final RandomMovingStrategy movingStrategy = new RandomMovingStrategy(generate);
+        return RacingGame.newInstance(racingGameRequestDto.getNames(), racingGameRequestDto.getTurn(), movingStrategy);
+    }
+
+    private List<String> mapToName(RacingGame racingGame) {
+        return racingGame.getWinner()
+                .stream()
+                .map(Car::getName)
+                .collect(toList());
+    }
+
+    private List<CarDto> mapToCarDto(Track track) {
+        return track.getCars()
+                .stream()
+                .map(CarDto::new)
+                .collect(toList());
+    }
+}

리뷰: 메소드를 분해해도 좋을것 같습니다 👍 !
@@ -0,0 +1,25 @@
+import view.WebRacingHandler;
+
+import java.util.HashMap;
+
+import static spark.Spark.*;
+import static util.RenderUtil.render;
+
+public class WebMain {
+    public static final int PORT = 8080;
+    private static WebRacingHandler webRacingHandler = new WebRacingHandler();
+
+    public static void main(String[] args) {
+        port(PORT);
+
+        get("/", (req, res) -> render(new HashMap<>(), "/index.html"));
+
+        post("/name", webRacingHandler.handleGetName());
+
+        post("/result", webRacingHandler.handleResult());
+    }
+
+
+
+
+}
\ No newline at end of file

리뷰: 헛, 별건아닌데 `post`가 요구사항이었습니다 😄
@@ -0,0 +1,79 @@
+package view;
+
+import domain.car.Car;
+import domain.game.RacingGame;
+import domain.game.Track;
+import domain.random.RandomGenerator;
+import domain.strategy.RandomMovingStrategy;
+import dto.CarDto;
+import dto.RacingGameRequestDto;
+import spark.Route;
+import util.Splitter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+import static java.util.stream.Collectors.toList;
+import static util.RenderUtil.render;
+import static util.Splitter.COMMA;
+
+public class WebRacingHandler {
+    public Route handleGetName() {
+        return (req, res) -> {
+            HashMap<String, Object> model = new HashMap<>();
+            String names = req.queryParams("names");
+
+            List<String> splitNames = Splitter.splitBlank(names);
+            model.put("names", splitNames);
+            model.put("nameStrings", String.join(COMMA, splitNames));
+            return render(model, "/game.html");
+        };
+    }
+
+    public Route handleResult() {
+        return (req, res) -> {
+            final HashMap<String, Object> model = new HashMap<>();
+            final RacingGameRequestDto racingGameRequestDto = new RacingGameRequestDto(Integer.parseInt(req.queryParams("turn")), req.queryParams("names"));
+
+            final RacingGame racingGame = generateRacingGame(racingGameRequestDto);
+
+            finishGame(racingGame);
+            Track track = racingGame.getTrack();
+
+            List<CarDto> carDtos = mapToCarDto(track);
+            List<String> nameOfWinners = mapToName(racingGame);
+
+
+            model.put("cars", carDtos);
+            model.put("winners", nameOfWinners);
+            return render(model, "/result.html");
+        };
+    }
+
+    private void finishGame(RacingGame racingGame) {
+        while (!racingGame.isDone()) {
+            racingGame.proceedOneTurn();
+        }
+    }
+
+    private RacingGame generateRacingGame(RacingGameRequestDto racingGameRequestDto) {
+        final Random generate = RandomGenerator.generate();
+        final RandomMovingStrategy movingStrategy = new RandomMovingStrategy(generate);
+        return RacingGame.newInstance(racingGameRequestDto.getNames(), racingGameRequestDto.getTurn(), movingStrategy);
+    }
+
+    private List<String> mapToName(RacingGame racingGame) {
+        return racingGame.getWinner()
+                .stream()
+                .map(Car::getName)
+                .collect(toList());
+    }
+
+    private List<CarDto> mapToCarDto(Track track) {
+        return track.getCars()
+                .stream()
+                .map(CarDto::new)
+                .collect(toList());
+    }
+}

리뷰: 요건 반환값이 따로 있는이유가 있을까요 ?
`getTracks()`는 메소드가 따로있어서요!
@@ -0,0 +1,79 @@
+package view;
+
+import domain.car.Car;
+import domain.game.RacingGame;
+import domain.game.Track;
+import domain.random.RandomGenerator;
+import domain.strategy.RandomMovingStrategy;
+import dto.CarDto;
+import dto.RacingGameRequestDto;
+import spark.Route;
+import util.Splitter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+import static java.util.stream.Collectors.toList;
+import static util.RenderUtil.render;
+import static util.Splitter.COMMA;
+
+public class WebRacingHandler {
+    public Route handleGetName() {
+        return (req, res) -> {
+            HashMap<String, Object> model = new HashMap<>();
+            String names = req.queryParams("names");
+
+            List<String> splitNames = Splitter.splitBlank(names);
+            model.put("names", splitNames);
+            model.put("nameStrings", String.join(COMMA, splitNames));
+            return render(model, "/game.html");
+        };
+    }
+
+    public Route handleResult() {
+        return (req, res) -> {
+            final HashMap<String, Object> model = new HashMap<>();
+            final RacingGameRequestDto racingGameRequestDto = new RacingGameRequestDto(Integer.parseInt(req.queryParams("turn")), req.queryParams("names"));
+
+            final RacingGame racingGame = generateRacingGame(racingGameRequestDto);
+
+            finishGame(racingGame);
+            Track track = racingGame.getTrack();
+
+            List<CarDto> carDtos = mapToCarDto(track);
+            List<String> nameOfWinners = mapToName(racingGame);
+
+
+            model.put("cars", carDtos);
+            model.put("winners", nameOfWinners);
+            return render(model, "/result.html");
+        };
+    }
+
+    private void finishGame(RacingGame racingGame) {
+        while (!racingGame.isDone()) {
+            racingGame.proceedOneTurn();
+        }
+    }
+
+    private RacingGame generateRacingGame(RacingGameRequestDto racingGameRequestDto) {
+        final Random generate = RandomGenerator.generate();
+        final RandomMovingStrategy movingStrategy = new RandomMovingStrategy(generate);
+        return RacingGame.newInstance(racingGameRequestDto.getNames(), racingGameRequestDto.getTurn(), movingStrategy);
+    }
+
+    private List<String> mapToName(RacingGame racingGame) {
+        return racingGame.getWinner()
+                .stream()
+                .map(Car::getName)
+                .collect(toList());
+    }
+
+    private List<CarDto> mapToCarDto(Track track) {
+        return track.getCars()
+                .stream()
+                .map(CarDto::new)
+                .collect(toList());
+    }
+}

리뷰: 해당 영역을 분리하면 좋을 것 같아요!
@@ -0,0 +1,55 @@
+package christmas.controller;
+
+import christmas.domain.date.Date;
+import christmas.domain.badge.Badge;
+import christmas.domain.reward.Reward;
+import christmas.domain.order.Bill;
+import christmas.dto.RewardDto;
+import christmas.factory.ControllerFactory;
+import christmas.view.OutputView;
+
+public class GameController {
+    private final DateController dateController = ControllerFactory.getDateController();
+    private final OrderController orderController = ControllerFactory.getOrderController();
+    private final EventController eventController = ControllerFactory.getEventController();
+    private final BadgeController badgeController = ControllerFactory.getBadgeController();
+
+    public void run() {
+        printWelcomeMessage();
+
+        Date date = dateController.acceptVisitDate();
+        Bill bill = orderController.acceptOrder();
+        printOrderResult(date, bill);
+
+        Reward reward = eventController.confirmReward(date, bill);
+        RewardDto rewardDto = reward.toDto();
+
+        printRewardResult(rewardDto);
+        printFinalCheckoutPrice(bill, rewardDto);
+
+        printBadgeResult(badgeController.grantBadge(rewardDto));
+    }
+
+    public void printWelcomeMessage() {
+        OutputView.printWelcomeMessage();
+    }
+
+    public void printOrderResult(Date date, Bill bill) {
+        OutputView.printPreviewMessage(date);
+        OutputView.printOrderMessage(bill);
+    }
+
+    public void printFinalCheckoutPrice(Bill bill, RewardDto rewardDto) {
+
+        int checkoutPrice = bill.totalPrice() - rewardDto.getTotalDiscountReward();
+        OutputView.printFinalCheckoutPriceMessage(checkoutPrice);
+    }
+
+    public void printRewardResult(RewardDto rewardDto) {
+        OutputView.printRewardsMessage(rewardDto);
+    }
+
+    public void printBadgeResult(Badge badge) {
+        OutputView.printBadgeMessage(badge);
+    }
+}

리뷰: 취향 차이 라고 생각하긴 하지만, 객체를 생성해주는 라인들을 하나로 모으고 출력print메서드들을 전부 모아주는것도 하나의 방법일 것 같아요

        printWelcomeMessage();

        Date date = dateController.acceptVisitDate();
        Bill bill = orderController.acceptOrder();
        Reward reward = eventController.confirmReward(date, bill);
        RewardDto rewardDto = reward.toDto();

        printRewardResult(rewardDto);
        printOrderResult(date, bill);
        printFinalCheckoutPrice(bill, rewardDto);
        printBadgeResult(badgeController.grantBadge(rewardDto));

이런식으로요
@@ -0,0 +1,26 @@
+package christmas.domain.category;
+
+import christmas.domain.menu.Menu;
+import christmas.domain.menu.MenuItem;
+
+public enum Appetizer implements MenuItem {
+    MUSHROOM_SOUP("양송이수프", 6000),
+    TAPAS("타파스", 5500),
+    CAESAR_SALAD("시저샐러드", 8000);
+
+    private Menu menu;
+
+    Appetizer(String name, int price) {
+        this.menu = new Menu(name, price);
+    }
+    @Override
+    public Menu get(){
+        return menu;
+    }
+
+    @Override
+    public String getName() {
+        return menu.name();
+    }
+
+}

리뷰: 인터페이스를 사용하셨군요!! 이렇게 되면 유지 보수에 더 좋을거라는 생각이 드네요 멋져요
@@ -0,0 +1,32 @@
+package christmas.domain.date;
+
+import christmas.exception.DateException;
+import christmas.exception.message.DateExceptionMessage;
+import christmas.util.Calendar;
+
+import static christmas.constant.DateConstant.START_DAY;
+import static christmas.constant.DateConstant.END_DAY;
+
+public record Date(int day, DayOfWeek dayOfWeek) {
+    public static Date of(int day) {
+        validate(day);
+
+        DayOfWeek dayOfWeek = getDayOfWeek(day);
+        return new Date(day, dayOfWeek);
+    }
+
+    private static void validate(int day) {
+        validateDayRange(day);
+    }
+
+    private static void validateDayRange(int day) {
+        if (day < START_DAY || day > END_DAY) {
+            throw new DateException(DateExceptionMessage.INVALID_DAY);
+        }
+    }
+
+    private static DayOfWeek getDayOfWeek(int day) {
+        return Calendar.calculateDayOfWeek(day);
+    }
+
+}

리뷰: 저도 리뷰하다가 배운 내용인데 localDate.of() 를 사용하는 방법도 있어서 추천 드려요!!
@@ -0,0 +1,37 @@
+package christmas.domain.event.discount;
+
+import christmas.domain.date.Date;
+import christmas.domain.reward.DiscountEventReward;
+import christmas.lib.event.DiscountEvent;
+
+import static christmas.constant.EventConstant.CHRISTMAS_DAY;
+import static christmas.constant.EventConstant.D_DAY_DISCOUNT_UNIT;
+import static christmas.constant.EventConstant.CHRISTMAS_EVENT_MESSAGE;
+import static christmas.constant.EventConstant.D_DAY_START_PRICE;
+
+
+public class ChristmasDiscountEvent extends DiscountEvent<Date> {
+    private final Integer D_DAY = CHRISTMAS_DAY;
+    private final Integer START_PRICE = D_DAY_START_PRICE;
+    private final Integer DISCOUNT_UNIT = D_DAY_DISCOUNT_UNIT;
+    private final String EVENT_NAME = CHRISTMAS_EVENT_MESSAGE;
+
+    @Override
+    public boolean checkCondition(Date date) {
+        if (date.day() <= D_DAY) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public DiscountEventReward provideReward(Date date) {
+        int currentDay = date.day();
+        int discountPrice = calculateDiscountPrice(currentDay);
+        return new DiscountEventReward(EVENT_NAME, discountPrice);
+    }
+
+    private Integer calculateDiscountPrice(int currentDay) {
+        return START_PRICE + (currentDay - 1) * DISCOUNT_UNIT;
+    }
+}

리뷰: static import문들이 위에 위치하는 것이 자바 컨벤션에 부합하는 것으로 알고 있어요