ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview badge 기능에 대한 테스트 파일
+ * @module badgeTest
+ */
+import { Console } from "@woowacourse/mission-utils";
+import badge from "../../src/domain/badge.js";
+
+/**
+ * Console 모듈에서 print 메서드의 spyOn을 생성하여 반환하는 함수
+ * @returns {jest.SpyInstance} Console.print의 spyOn
+ */
+const getLogSpy = () => {
+  const logSpy = jest.spyOn(Console, "print");
+  logSpy.mockClear();
+
+  return logSpy;
+};
+/**
+ * badge 기능에 대한 테스트 수트
+ */
+describe("badge 기능 테스트", () => {
+  let logSpy;
+  /**
+   * 각 테스트 케이스 실행 전에 logSpy를 초기화하는 함수
+   */
+  beforeEach(() => {
+    logSpy = getLogSpy();
+  });
+
+  test("테스트 케이스 1: 할인 금액이 0일 때 없음이 출력되어야 함.", () => {
+    // given
+    const totalDiscountPrice = 0;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "없음"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 2: 할인 금액이 2만원이 넘을 때 산타가 출력되어야 함", () => {
+    // given
+    const totalDiscountPrice = -20001;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "산타"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 3: 할인 금액이 1만원 이상 2만원 미만일 때 트리가 출력되어야 함", () => {
+    // given
+    const totalDiscountPrice = -19999;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "트리"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+  
+  test("테스트 케이스 4: 할인 금액이 5천원 이상 1만원 미만일 때 별이 출력되어야 함.", () => {
+    // given
+    const totalDiscountPrice = -9999;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "별"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 5: 할인 금액이 0원 이상일 때 없음이 출력되이어야 함.", () => {
+    // given
+    const totalDiscountPrice = 1000;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "없음"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+});
\ No newline at end of file

리뷰: 단위테스트를 하기 쉽게 코드를 작성하셨다는게 보여지는 테스트 코드네요.
저같은 경우엔 이런식의 테스트 작성이 불가능했어서 많이 배우고 갑니다~
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview badge 기능에 대한 테스트 파일
+ * @module badgeTest
+ */
+import { Console } from "@woowacourse/mission-utils";
+import badge from "../../src/domain/badge.js";
+
+/**
+ * Console 모듈에서 print 메서드의 spyOn을 생성하여 반환하는 함수
+ * @returns {jest.SpyInstance} Console.print의 spyOn
+ */
+const getLogSpy = () => {
+  const logSpy = jest.spyOn(Console, "print");
+  logSpy.mockClear();
+
+  return logSpy;
+};
+/**
+ * badge 기능에 대한 테스트 수트
+ */
+describe("badge 기능 테스트", () => {
+  let logSpy;
+  /**
+   * 각 테스트 케이스 실행 전에 logSpy를 초기화하는 함수
+   */
+  beforeEach(() => {
+    logSpy = getLogSpy();
+  });
+
+  test("테스트 케이스 1: 할인 금액이 0일 때 없음이 출력되어야 함.", () => {
+    // given
+    const totalDiscountPrice = 0;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "없음"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 2: 할인 금액이 2만원이 넘을 때 산타가 출력되어야 함", () => {
+    // given
+    const totalDiscountPrice = -20001;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "산타"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 3: 할인 금액이 1만원 이상 2만원 미만일 때 트리가 출력되어야 함", () => {
+    // given
+    const totalDiscountPrice = -19999;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "트리"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+  
+  test("테스트 케이스 4: 할인 금액이 5천원 이상 1만원 미만일 때 별이 출력되어야 함.", () => {
+    // given
+    const totalDiscountPrice = -9999;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "별"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 5: 할인 금액이 0원 이상일 때 없음이 출력되이어야 함.", () => {
+    // given
+    const totalDiscountPrice = 1000;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "없음"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+});
\ No newline at end of file

리뷰: 단위 테스트 원칙에서 "모킹을 피할 수 있으면 최대한 피하는 것이 좋다."고 언급하고 있는데요. 

Console.print에 출력이 잘 되는지 확인하는 것 보단 도메인의 '단위 테스트'인 만큼 Badge에서 예상했던 이벤트 뱃지를 잘 반환 하는지 확인하면 더 좋을거 같아 코멘트 남겨봅니다..!!

또한, Badge에 UI 로직이 결합되있는걸 살펴볼 수 있었는데, 3주차 공통 피드백에 명시된 UI 로직과 비즈니스 로직 분리를 고려해보시면 좋을거 같습니다.

https://github.com/mawrkus/js-unit-testing-guide#-dont-mock-everything
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview badge 기능에 대한 테스트 파일
+ * @module badgeTest
+ */
+import { Console } from "@woowacourse/mission-utils";
+import badge from "../../src/domain/badge.js";
+
+/**
+ * Console 모듈에서 print 메서드의 spyOn을 생성하여 반환하는 함수
+ * @returns {jest.SpyInstance} Console.print의 spyOn
+ */
+const getLogSpy = () => {
+  const logSpy = jest.spyOn(Console, "print");
+  logSpy.mockClear();
+
+  return logSpy;
+};
+/**
+ * badge 기능에 대한 테스트 수트
+ */
+describe("badge 기능 테스트", () => {
+  let logSpy;
+  /**
+   * 각 테스트 케이스 실행 전에 logSpy를 초기화하는 함수
+   */
+  beforeEach(() => {
+    logSpy = getLogSpy();
+  });
+
+  test("테스트 케이스 1: 할인 금액이 0일 때 없음이 출력되어야 함.", () => {
+    // given
+    const totalDiscountPrice = 0;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "없음"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 2: 할인 금액이 2만원이 넘을 때 산타가 출력되어야 함", () => {
+    // given
+    const totalDiscountPrice = -20001;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "산타"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 3: 할인 금액이 1만원 이상 2만원 미만일 때 트리가 출력되어야 함", () => {
+    // given
+    const totalDiscountPrice = -19999;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "트리"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+  
+  test("테스트 케이스 4: 할인 금액이 5천원 이상 1만원 미만일 때 별이 출력되어야 함.", () => {
+    // given
+    const totalDiscountPrice = -9999;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "별"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+
+  test("테스트 케이스 5: 할인 금액이 0원 이상일 때 없음이 출력되이어야 함.", () => {
+    // given
+    const totalDiscountPrice = 1000;
+
+    // when
+    badge(totalDiscountPrice);
+
+    // then
+    const expected = "없음"
+    expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(expected));
+  });
+});
\ No newline at end of file

리뷰: 테스트 케이스가 깔끔해서 잘 읽히네요 👍
@@ -0,0 +1,111 @@
+/**
+ * @fileoverview Discount 기능에 대한 테스트 파일
+ * @module DiscountTest
+ */
+import Discount from "../../src/domain/Discount.js";
+import orderMenu from "../../src/domain/orderMenu.js";
+import preDiscountAmount from "../../src/domain/preDiscountAmount";
+
+/**
+ * 주어진 음식에 대한 메뉴와 총 가격을 반환하는 함수
+ * @param {string} food - 주문할 음식의 목록
+ * @returns {{menu: Object, totalPrice: number}} 주문한 음식의 메뉴와 총 가격
+ */
+const getMenuAndTotalPrice = (food) => {
+  const menu = orderMenu(food);
+  const totalPrice = preDiscountAmount(menu.menuNames, menu.quantities);
+
+  return { menu, totalPrice };
+};
+
+/**
+ * Discount 기능에 대한 테스트 수트
+ */
+describe("Discount 기능 테스트", () => {
+  let discount;
+  /**
+   * 모든 테스트 케이스 실행 전에 Discount 객체를 초기화하는 함수
+   */
+  beforeAll(() => {
+    discount = new Discount();
+  });
+
+  /**
+   * 여러 테스트 케이스에 대한 테스트를 수행하는 함수
+   * @param {string} description - 테스트 케이스에 대한 설명
+   * @param {string} date - 할인을 받을 날짜
+   * @param {string} food - 주문한 음식의 목록
+   * @param {Array} expected - 예상되는 할인 목록
+   */
+  test.each([
+    [
+      "크리스마스일 때 평일 할인(디저트 1개)), 특별 할인",
+      "25",
+      "해산물파스타-2,레드와인-1,초코케이크-1",
+      [
+        { type: "크리스마스 디데이 할인: ", amount: -3400 },
+        { type: "평일 할인: ", amount: -2023 },
+        { type: "특별 할인: ", amount: -1000 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "토요일일 때 주말 할인(메인 2개)",
+      "16",
+      "타파스-1,바비큐립-1,크리스마스파스타-1,아이스크림-2,제로콜라-1,샴페인-1",
+      [
+        { type: "크리스마스 디데이 할인: ", amount: -2500 },
+        { type: "주말 할인: ", amount: -4046 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "크리스마스 이후 평일 할인(디저트 3개)",
+      "28",
+      "시저샐러드-1,티본스테이크-1,해산물파스타-1,초코케이크-1,아이스크림-2,제로콜라-1,레드와인-1",
+      [
+        { type: "평일 할인: ", amount: -6069 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "크리스마스 이후 주말 할인(메인 5개)",
+      "30",
+      "양송이수프-1,시저샐러드-4,타파스-1,티본스테이크-2,바비큐립-1,해산물파스타-1,크리스마스파스타-1,아이스크림-4,초코케이크-1,제로콜라-1,레드와인-1,샴페인-1",
+      [
+        { type: "주말 할인: ", amount: -10115 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "크리스마스 이후 평일 할인(디저트 4개)와 특별 할인",
+      "31",
+      "양송이수프-1,시저샐러드-1,타파스-1,티본스테이크-2,크리스마스파스타-1,아이스크림-2,초코케이크-2,제로콜라-1,레드와인-1",
+      [
+        { type: "평일 할인: ", amount: -8092 },
+        { type: "특별 할인: ", amount: -1000 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "1만원 이하 금액이라 할인 혜택이 적용되지 않음",
+      "16",
+      "양송이수프-1,제로콜라-1",
+      [],
+    ],
+    [
+      "1만원 이상 12만원 이하 금액이라 샴페인 증정이 되지 않음",
+      "24",
+      "타파스-1,티본스테이크-1,해산물파스타-1,아이스크림-2,제로콜라-2",
+      [
+        { type: "크리스마스 디데이 할인: ", amount: -3300 },
+        { type: "평일 할인: ", amount: -4046 },
+        { type: "특별 할인: ", amount: -1000 },
+      ],
+    ],
+  ])("테스트 케이스 %#: %s", (_, date, food, expected) => {
+    const { menu, totalPrice } = getMenuAndTotalPrice(food);
+    const result = discount.discountPrice(menu, date, totalPrice);
+    expect(result).toEqual(expected);
+  });
+});

리뷰: jsDoc 사용을 잘 하시네요 👍
@@ -0,0 +1,111 @@
+/**
+ * @fileoverview Discount 기능에 대한 테스트 파일
+ * @module DiscountTest
+ */
+import Discount from "../../src/domain/Discount.js";
+import orderMenu from "../../src/domain/orderMenu.js";
+import preDiscountAmount from "../../src/domain/preDiscountAmount";
+
+/**
+ * 주어진 음식에 대한 메뉴와 총 가격을 반환하는 함수
+ * @param {string} food - 주문할 음식의 목록
+ * @returns {{menu: Object, totalPrice: number}} 주문한 음식의 메뉴와 총 가격
+ */
+const getMenuAndTotalPrice = (food) => {
+  const menu = orderMenu(food);
+  const totalPrice = preDiscountAmount(menu.menuNames, menu.quantities);
+
+  return { menu, totalPrice };
+};
+
+/**
+ * Discount 기능에 대한 테스트 수트
+ */
+describe("Discount 기능 테스트", () => {
+  let discount;
+  /**
+   * 모든 테스트 케이스 실행 전에 Discount 객체를 초기화하는 함수
+   */
+  beforeAll(() => {
+    discount = new Discount();
+  });
+
+  /**
+   * 여러 테스트 케이스에 대한 테스트를 수행하는 함수
+   * @param {string} description - 테스트 케이스에 대한 설명
+   * @param {string} date - 할인을 받을 날짜
+   * @param {string} food - 주문한 음식의 목록
+   * @param {Array} expected - 예상되는 할인 목록
+   */
+  test.each([
+    [
+      "크리스마스일 때 평일 할인(디저트 1개)), 특별 할인",
+      "25",
+      "해산물파스타-2,레드와인-1,초코케이크-1",
+      [
+        { type: "크리스마스 디데이 할인: ", amount: -3400 },
+        { type: "평일 할인: ", amount: -2023 },
+        { type: "특별 할인: ", amount: -1000 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "토요일일 때 주말 할인(메인 2개)",
+      "16",
+      "타파스-1,바비큐립-1,크리스마스파스타-1,아이스크림-2,제로콜라-1,샴페인-1",
+      [
+        { type: "크리스마스 디데이 할인: ", amount: -2500 },
+        { type: "주말 할인: ", amount: -4046 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "크리스마스 이후 평일 할인(디저트 3개)",
+      "28",
+      "시저샐러드-1,티본스테이크-1,해산물파스타-1,초코케이크-1,아이스크림-2,제로콜라-1,레드와인-1",
+      [
+        { type: "평일 할인: ", amount: -6069 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "크리스마스 이후 주말 할인(메인 5개)",
+      "30",
+      "양송이수프-1,시저샐러드-4,타파스-1,티본스테이크-2,바비큐립-1,해산물파스타-1,크리스마스파스타-1,아이스크림-4,초코케이크-1,제로콜라-1,레드와인-1,샴페인-1",
+      [
+        { type: "주말 할인: ", amount: -10115 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "크리스마스 이후 평일 할인(디저트 4개)와 특별 할인",
+      "31",
+      "양송이수프-1,시저샐러드-1,타파스-1,티본스테이크-2,크리스마스파스타-1,아이스크림-2,초코케이크-2,제로콜라-1,레드와인-1",
+      [
+        { type: "평일 할인: ", amount: -8092 },
+        { type: "특별 할인: ", amount: -1000 },
+        { type: "증정 이벤트: ", amount: -25000 },
+      ],
+    ],
+    [
+      "1만원 이하 금액이라 할인 혜택이 적용되지 않음",
+      "16",
+      "양송이수프-1,제로콜라-1",
+      [],
+    ],
+    [
+      "1만원 이상 12만원 이하 금액이라 샴페인 증정이 되지 않음",
+      "24",
+      "타파스-1,티본스테이크-1,해산물파스타-1,아이스크림-2,제로콜라-2",
+      [
+        { type: "크리스마스 디데이 할인: ", amount: -3300 },
+        { type: "평일 할인: ", amount: -4046 },
+        { type: "특별 할인: ", amount: -1000 },
+      ],
+    ],
+  ])("테스트 케이스 %#: %s", (_, date, food, expected) => {
+    const { menu, totalPrice } = getMenuAndTotalPrice(food);
+    const result = discount.discountPrice(menu, date, totalPrice);
+    expect(result).toEqual(expected);
+  });
+});

리뷰: beforeAll을 사용해서 DRY 원칙을 준수하시는것이 인상적이네요 👍
@@ -0,0 +1,22 @@
+const MENU = Object.freeze([
+  { menu: "양송이수프", price: 6000 },
+  { menu: "타파스", price: 5500 },
+  { menu: "시저샐러드", price: 8000 },
+  { menu: "티본스테이크", price: 55000 },
+  { menu: "바비큐립", price: 54000 },
+  { menu: "해산물파스타", price: 35000 },
+  { menu: "크리스마스파스타", price: 25000 },
+  { menu: "초코케이크", price: 15000 },
+  { menu: "아이스크림", price: 5000 },
+  { menu: "제로콜라", price: 3000 },
+  { menu: "레드와인", price: 60000 },
+  { menu: "샴페인", price: 25000 },
+]);
+
+const MAIN_MENU = Object.freeze(["티본스테이크", "바비큐립", "해산물파스타", "크리스마스파스타"]);
+
+const DESSERT_MENU = Object.freeze(["초코케이크", "아이스크림"]);
+
+const DRINK_MENU = Object.freeze(["제로콜라", "레드와인", "샴페인"]);
+
+export { MENU, MAIN_MENU, DESSERT_MENU, DRINK_MENU };

리뷰: 배열과 value값으로 설정해서 활용할수있군요!
배워갑니다!
@@ -0,0 +1,41 @@
+const PROMPT = Object.freeze({
+  start: "안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.",
+  select_date:
+    "12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)\n",
+  select_menu:
+    "주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)\n",
+  month: "12월 ",
+  preview: "일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!",
+  order_menu: "\n<주문 메뉴>",
+  pre_discount_total: "\n<할인 전 총주문 금액>",
+  free_menu: "\n<증정 메뉴>",
+  benefit_detail: "\n<혜택 내역>",
+  total_benefit_detail: "\n<총혜택 금액>",
+  discounted_amount: "\n<할인 후 예상 결제 금액>",
+  event_badge: "\n<12월 이벤트 배지>",
+  champagne: "샴페인 1개",
+  none: "없음",
+  won: "원",
+  piece: "개",
+});
+
+const SYMBOLS = Object.freeze({
+  comma: ",",
+  blank: "",
+});
+
+const PROMPT_IN = Object.freeze({
+  d_day_discount: "크리스마스 디데이 할인: ",
+  weekday_discount: "평일 할인: ",
+  weekend_discount: "주말 할인: ",
+  specials_discount: "특별 할인: ",
+  gift_event: "증정 이벤트: ",
+});
+
+const BADGE = Object.freeze([
+  { limit: 20000, badge: "산타" },
+  { limit: 10000, badge: "트리" },
+  { limit: 5000, badge: "별" },
+]);
+
+export { PROMPT, SYMBOLS, PROMPT_IN, BADGE };

리뷰: 개, 원  등 디테일하게 상수처리해서 활용하는법 배워갑니다!
@@ -1,7 +1,105 @@
-export default OutputView = {
-    printMenu() {
-        Console.print("<주문 메뉴>");
-        // ...
-    }
-    // ...
-}
+import { Console } from "@woowacourse/mission-utils";
+import { PROMPT } from "./constants/prompt.js";
+import messageFormat from "./constants/messageFormat.js";
+
+const { start, pre_discount_total, free_menu, total_benefit_detail, discounted_amount, event_badge, } = PROMPT;
+
+const { preview, menu, preDiscount, free, benefit, totalBenefit, discountedAmount, badge, } = messageFormat;
+
+const OutputView = {
+  /**
+   * 콘솔에 메시지 출력.
+   * @param {string} message - 출력할 메시지
+   */
+  print(message) {
+    Console.print(message);
+  },
+
+  /**
+   * 시작 메시지 출력.
+   */
+  start() {
+    this.print(start);
+  },
+
+  /**
+   * 에러 메시지를 출력.
+   * @param {string} message - 출력할 에러 메시지
+   */
+  errorPrint(message) {
+    this.print(message);
+  },
+
+  /**
+   * 날짜 미리보기를 출력.
+   * @param {string} date - 날짜 정보
+   */
+  preView(date) {
+    this.print(preview(date));
+  },
+
+  /**
+   * 주문한 메뉴를 출력.
+   * @param {string[]} menuNames - 주문한 메뉴 이름 배열
+   * @param {string[]} quantities - 주문한 수량 배열
+   */
+  menu(menuNames, quantities) {
+    this.print(menu(menuNames, quantities));
+  },
+
+  /**
+   * 할인 전 총 금액 출력.
+   * @param {number} totalPrice - 할인 전 총 금액
+   */
+  preDiscount(totalPrice) {
+    this.print(pre_discount_total);
+    this.print(preDiscount(totalPrice));
+  },
+
+  /**
+   * 무료 메뉴 정보 출력
+   * @param {number} totalPrice - 할인 전 총 금액
+   */
+  free(totalPrice) {
+    this.print(free_menu);
+    this.print(free(totalPrice));
+  },
+
+  /**
+   * 할인 혜택 정보 출력
+   * @param {Object} discount - 할인 정보 객체
+   */
+  benefit(discount) {
+    this.print(benefit(discount));
+  },
+
+  /**
+   * 총 할인 혜택 정보 출력
+   * @param {number} totalDiscount - 총 할인 금액
+   */
+  totalBenefit(totalDiscount) {
+    this.print(total_benefit_detail);
+    this.print(totalBenefit(totalDiscount));
+  },
+
+  /**
+   * 할인된 최종 금액 출력
+   * @param {number} totalPrice - 할인 전 총 금액
+   * @param {number} totalDiscount - 총 할인 금액
+   */
+  discountedAmount(totalPrice, totalDiscount) {
+    this.print(discounted_amount);
+    this.print(discountedAmount(totalPrice, totalDiscount));
+  },
+
+  /**
+   * 이벤트 뱃지 정보 출력
+   * @param {number} selectedBadge - 총 할인 금액
+   */
+  badge(selectedBadge) {
+    this.print(event_badge);
+    this.print(badge(selectedBadge));
+  },
+};
+
+export default OutputView;

리뷰: prompt에서 쓰는 것들만, messageFormat에서 쓰는 것들만 갖고 와서 활용한 것으로 보이는데 이런 활용은 생각지 못했기에 인상에 남는 방식이라고 생각합니다. 
그런데 prompt와 messageFormat 파일 자체는 constants 폴더 안에 있어서 상수처럼 보이는데 변수로 만들어서 해당 폴더 안에 두신 이유가 궁금합니다. 다소 역할이 헷갈릴 수 있지 않나 싶어 질문드립니다.
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview orderMenu 기능에 대한 테스트 파일
+ * @module OrderMenuTest
+ */
+import orderMenu from "../../src/domain/orderMenu.js";
+
+/**
+ * orderMenu 함수에 대한 테스트 수트
+ */
+describe("orderMenu 기능 테스트", () => {
+  test("테스트 케이스 1: 음식 문자열 올바르게 파싱되어야 함.", () => {
+    // given
+    const food = "해산물파스타-2,레드와인-1,초코케이크-1";
+    
+    // when
+    const result = orderMenu(food);
+
+    // then
+    const expectedResult = {
+      menuNames: ["해산물파스타", "레드와인", "초코케이크"],
+      quantities: ["2", "1", "1"],
+    };
+    expect(result).toEqual(expectedResult);
+  });
+  test("테스트 케이스 2: 다양한 음식을 처리할 수 있어야 함.", () => {
+    // given
+    const food = "타파스-3,바비큐립-2,크리스마스파스타-1,레드와인-1,아이스크림-1";
+
+    // when
+    const result = orderMenu(food);
+
+    // then
+    const expectedResult = {
+      menuNames: ["타파스", "바비큐립", "크리스마스파스타", "레드와인", "아이스크림"],
+      quantities: ["3", "2", "1", "1", "1"],
+    };
+    expect(result).toEqual(expectedResult);
+  });
+});
\ No newline at end of file

리뷰: 이미 아래 테스트가 어떤 역할을 수행할지 충분히 가독성 좋게 작성하셔서 굳이 주석이 필요할까?? 라는 생각이 들었습니다!!
@@ -0,0 +1,104 @@
+import InputView from "../InputView.js";
+import dateIsValid from "../utils/dateIsValid.js";
+import foodIsValid from "../utils/foodIsValid.js";
+import orderMenu from "../domain/orderMenu.js";
+import OutputView from "../OutputView.js";
+import preDiscountAmount from "../domain/preDiscountAmount.js";
+import Discount from "../domain/Discount.js";
+import totalDiscount from "../domain/totalDiscount.js";
+import badge from "../domain/badge.js";
+
+/**
+ * 크리스마스 할인 이벤트 로직 제어하는 컨트롤러 클래스
+ * @class
+ */
+class ChristmasController {
+  /**
+   * 할인 객체
+   * @type {Discount}
+   * @private
+   */
+  #discount = new Discount();
+
+  /**
+   * ChristmasController 인스턴스 생성
+   * @constructor
+   */
+  constructor() {
+    /**
+     * 애플리케이션 시작 메시지 출력
+     */
+    OutputView.start();
+  }
+
+  /**
+   * 사용자로부터 날짜를 입력 받는 메서드
+   * @async
+   * @returns {Promise<string>} 입력받은 날짜
+   */
+  async dateInput() {
+    return this.#handleDateInput();
+  }
+
+  /**
+   * 날짜 입력 처리를 담당하는 메서드
+   * @async
+   * @private
+   * @returns {Promise<string>} 입력받은 날짜
+   * @throws {InputError} 입력이 유효하지 않은 경우 발생하는 예외
+   */
+  async #handleDateInput() {
+    try {
+      const date = await InputView.date();
+      dateIsValid(date);
+      return date;
+    } catch (error) {
+      OutputView.errorPrint(error.message);
+      return this.#handleDateInput();
+    }
+  }
+
+  /**
+   * 사용자로부터 음식을 입력받는 메서드
+   * @async
+   * @returns {Promise<string>} 입력받은 음식
+   */
+  async foodInput() {
+    return this.#handleFoodInput();
+  }
+
+  /**
+   * 음식 입력 처리를 담당하는 메서드
+   * @async
+   * @private
+   * @returns {Promise<string>} 입력받은 음식
+   * @throws {InputError} 입력이 유효하지 않은 경우 발생하는 예외
+   */
+  async #handleFoodInput() {
+    try {
+      const food = await InputView.food();
+      foodIsValid(food);
+      return food;
+    } catch (error) {
+      OutputView.errorPrint(error.message);
+      return this.#handleFoodInput();
+    }
+  }
+
+  /**
+   * 크리스마스 이벤트 로직을 처리하는 메서드
+   * @param {string} date - 날짜
+   * @param {string} food - 음식 주문
+   * @returns {void}
+   */
+  handleChristmasLogic(date, food) {
+    OutputView.preView(date);
+    const menu = orderMenu(food);
+    const totalPrice = preDiscountAmount(menu.menuNames, menu.quantities);
+    const discount = this.#discount.discountPrice(menu, date, totalPrice);
+    const totalDiscountPrice = totalDiscount(totalPrice, discount);
+    badge(totalDiscountPrice);
+  }
+}
+
+export default ChristmasController;

리뷰: 이곳에 쓰인 date와 food는 #handleDateInput()에서 리턴한 date와 #handleFoodInput()에서 리턴한 food로 보입니다. 필드에 놓고 저장한 후 그것을 사용한 것이 아니라 바로 사용한 것은 필드의 수를 줄이기 위해서 인지, 다른 이유가 있는지 여쭙고 싶습니다!
@@ -0,0 +1,39 @@
+import { NUMBERS } from "../constants/numbers.js";
+import { PROMPT } from "../constants/prompt.js";
+import OutputView from "../OutputView.js";
+
+const { zero } = NUMBERS;
+const { order_menu } = PROMPT;
+
+/**
+ * 입력받은 음식 정보를 가공하여 주문 목록을 반환함.
+ * 
+ * @param {string} food - 음식 정보 문자열
+ * @returns {{ menuNames: Array<string>, quantities: Array<number> }} - 추출된 음식 이름과 수량
+ * @property {Array} menuNames - 음식 목록
+ * @property {Array} quantities - 수량 목록
+ */
+const orderMenu = (food) => {
+  const menuNames = food.match(/[^\d,-]+/g);
+  const quantities = food.match(/\d+/g);
+
+  orderMenuOutput(menuNames, quantities);
+  return { menuNames, quantities };
+};
+
+/**
+ * 주문 목록을 출력합니다.
+ * 
+ * @param {Array} menuNames - 음식 목록
+ * @param {Array} quantities - 수량 목록
+ * @returns {void}
+ */
+const orderMenuOutput = (menuNames, quantities) => {
+  OutputView.print(order_menu);
+  menuNames.map((name, index) => {
+    const quantity = quantities[index];
+    OutputView.menu(name, quantity);
+  });
+};
+
+export default orderMenu;

리뷰: 새로운 배열을 반환하는 map 이 아닌 배열을 순회하고 로직 수행만하는 forEach가 더 적합해보입니다!!