@@ -0,0 +1,456 @@
+# 이메일 답장
+
+> 제목: 12월 이벤트 개발 보고
+
+> 보낸 사람: 개발팀 <`dev@woowacourse.io`>
+>
+> 받는 사람: 비즈니스팀 <`biz@woowacourse.io`>
+
+안녕하세요. 개발팀입니다!
+
+12월 이벤트 플래너 개발 요청 사항은 잘 받아 보았습니다.
+요청하신 내용들을 모두 적용해보았는데요. 혹시 누락된 부분이 있다면 바로 말씀해주시면 감사하겠습니다.
+
+#### 방문 날짜 입력
+
+- 방문 날짜는 1 이상 31 이하의 숫자로만 입력받게 했습니다.
+- 단, 사용자의 부주의로 입력 앞뒤로 공백이 생기는 경우가 종종 있는데, 이는 프로그램 내부에서 자동 처리하게 했습니다.
+    - " 12 " (O)
+    - " 1 2" (X)
+- 이외의 모든 잘못된 입력은 요청하신 에러 메시지를 보이게 했습니다.
+    - `[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.` (요청 사항)
+- 에러 메시지가 나온 다음에는 다시 재입력 받을 수 있게 했습니다.
+
+#### 주문 입력
+
+- 모든 주문은 "해산물파스타-2,레드와인-1,초코케이크-1" 와 같은 형식으로만 입력받게 했습니다.
+- 단, 사용자의 부주의로 입력 사이에 공백이 생기는 경우가 종종 있는데, 이는 프로그램 내부에서 자동 처리하게 했습니다.
+    - 띄워쓰기가 포함된 메뉴가 없어서 입력의 모든 공백을 허용하게 했습니다.
+    - "해산물파스타 - 2, 레드와인 - 3" (O)
+    - "해 산 물 파 스 타 - 1 2 , 레드와인-3" (O)
+- 메뉴 형식이 예시와 다른 경우 에러 메시지를 보이게 했습니다.
+    - "해산물파스타 2, 레드와인 3" (X)
+    - "해산물파스타: 2, 레드와인: 3" (X)
+    - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+- 메뉴의 개수를 0 이하의 숫자로 입력하면 에러 메시지를 보이게 했습니다.
+    - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+- 중복 메뉴를 입력한 경우 에러 메시지를 보이게 했습니다.
+    - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+- 메뉴판에 없는 메뉴를 입력한 경우 에러 메시지를 보이게 했습니다.
+    - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+- 메뉴의 개수가 합쳐서 20개를 초과하면 에러 메시지를 보이게 했습니다.
+    - `[ERROR] 메뉴는 한 번에 최대 20개까지만 주문할 수 있습니다. 다시 입력해 주세요.` (추가)
+- 음료만 주문 시, 에러 메시지를 보이게 했습니다.
+    - `[ERROR] 음료만 주문할 수 없습니다. 다시 입력해 주세요.` (추가)
+- 에러 메시지가 나온 다음에는 다시 주문을 재입력 받을 수 있게 했습니다.
+
+#### 이벤트
+
+- 모든 이벤트는 10,000원 이상부터 적용되도록 했습니다.
+- 모든 이벤트는 적용 가능하다면 중복으로 적용되도록 했습니다.
+- 크리스마스 디데이 할인
+    - 기간: 2023.12.01 ~ 2023.12.25
+    - 할인 금액: 2023.12.01 1,000원으로 시작하여, 매일 할인 금액이 100원씩 증가
+    - 할인 방법: 총 주문 금액에서 할인 금액만큼 할인
+- 평일 할인
+    - 기간: 2023.12.01 ~ 2023.12.31 중 매주 일요일 ~ 목요일
+    - 할인 금액: 2,023원
+    - 할인 방법: 디저트 메뉴를 개당 할인 금액만큼 할인
+- 주말 할인
+    - 기간: 2023.12.01 ~ 2023.12.31 중 매주 금요일, 토요일
+    - 할인 금액: 2,023원
+    - 할인 방법: 메인 메뉴를 개당 할인 금액만큼 할인
+- 특별 할인
+    - 기간: 2023.12.01 ~ 2023.12.31 중 매주 일요일, 12월 25일
+    - 할인 금액: 1,000원
+    - 할인 방법: 총주문 금액에서 할인 금액만큼 할인
+- 증정 이벤트
+    - 기간: 2023.12.01 ~ 2023.12.31
+    - 이벤트: 할인 전 총주문 금액이 12만원 이상일 때, 샴페인 1개 증정 (25,000원)
+- 증정 이벤트는 총혜택 금액에는 포함되지만, 실제 할인 후 예상 결제 금액에는 적용되지 않게 했습니다.
+- 나머지 이벤트는 총혜택 금액에도 포함되고 실제 할인에도 적용됩니다.
+
+#### 이벤트 배지
+
+- 5천원 미만: 없음
+- 5천원 이상: 별
+- 1만원 이상: 트리
+- 2만원 이상: 산타
+
+#### 결과 출력
+
+- 입력한 주문 메뉴를 출력합니다.
+- 할인 전 총주문 금액을 출력합니다.
+- 증정 메뉴를 출력합니다. 증정 메뉴가 없으면 "없음"을 출력합니다.
+- 혜택 내역을 출력합니다. 혜택이 없으면 "없음"을 출력합니다.
+- 총혜택 금액을 출력합니다. 혜택이 없으면 0을 출력합니다.
+- 할인 후 예상 결제 금액을 출력합니다.
+- 12월 이벤트 배지를 출력합니다. 받을 배지가 없으면 "없음"을 출력합니다.
+
+개발팀 내부에서 여러 상황을 고려해서 많은 테스트를 진행했습니다.
+하지만 그럼에도 버그가 있거나 새롭게 추가하고 싶은 기능이 있으시다면 바로 말씀해주세요.
+
+특히 새로운 이벤트를 추가하거나, 새로운 메뉴를 추가하는 건 언제든 환영입니다!
+확장성있게 설계를 해두었기 때문에 편하게 요청하셔도 괜찮습니다!
+
+감사합니다 :)
+
+---
+
+# 이벤트 내용
+
+## 이벤트 종류
+
+### 크리스마스 디데이 할인
+
+기간: 2023.12.01 ~ 2023.12.25
+
+할인 금액: 2023.12.01 1,000원으로 시작하여, 매일 할인 금액이 100원씩 증가
+
+할인 방법: 총 주문 금액에서 할인 금액만큼 할인
+
+### 평일 할인
+
+기간: 2023.12.01 ~ 2023.12.31 중 매주 일요일 ~ 목요일
+
+할인 금액: 2,023원
+
+할인 방법: 디저트 메뉴를 개당 할인 금액만큼 할인
+
+### 주말 할인
+
+기간: 2023.12.01 ~ 2023.12.31 중 매주 금요일, 토요일
+
+할인 금액: 2,023원
+
+할인 방법: 메인 메뉴를 개당 할인 금액만큼 할인
+
+### 특별 할인
+
+기간: 2023.12.01 ~ 2023.12.31 중 매주 일요일, 12월 25일
+
+할인 금액: 1,000원
+
+할인 방법: 총주문 금액에서 할인 금액만큼 할인
+
+### 증정 이벤트
+
+기간: 2023.12.01 ~ 2023.12.31
+
+이벤트: 할인 전 총주문 금액이 12만원 이상일 때, 샴페인 1개 증정 (25,000원)
+
+## 이벤트 배지
+
+이벤트로 받은 총혜택 금액에 따라 각기 다른 이벤트 배지를 부여
+(이때 `총혜택 금액 = 할인 금액의 합계 + 증정 메뉴의 가격`으로 계산)
+
+- 5천원 이상: 별
+- 1만원 이상: 트리
+- 2만원 이상: 산타
+
+## 이벤트 주의 사항
+
+- 총주문 금액 10,000원 이상부터 이벤트가 적용
+- 음료만 주문 시, 주문할 수 없음
+- 메뉴는 한 번에 최대 20개까지만 주문할 수 있음 (종류가 아닌 개수)
+
+# 개발 요청 사항
+
+- [x] 방문할 날짜 입력
+    - [x] 1 이상 31 이하의 숫자만 입력받음
+- [x] 주문할 메뉴와 개수 입력
+    - [x] 메뉴판에 있는 메뉴만 입력받음
+    - [x] 정해진 형식의 메뉴만 입력받음
+    - [x] 메뉴의 개수는 1 이상의 숫자만 입력받음
+    - [x] 중복되지 않은 메뉴만 입력받음
+- [x] 주문 메뉴 출력
+    - [x] 출력 순서는 자유롭게 출력
+- [x] 할인 전 총주문 금액 출력
+- [x] 증정 메뉴 출력
+    - [x] 증정 이벤트에 해당하지 않는 경우 `없음` 출력
+- [x] 혜택 내역 출력
+    - [x] 고객에게 적용된 이벤트 내역만 출력
+    - [x] 적용된 이벤트가 없는 경우 `없음` 출력
+    - [x] 이벤트 출력 순서는 자유롭게 출력
+- [x] 총혜택 금액 출력
+    - [x] `총혜택 금액 = 할인 금액의 합계 + 증정 메뉴의 가격`
+- [x] 할인 후 예상 결제 금액 출력
+    - [x] `할인 후 예상 결제 금액 = 할인 전 총주문 금액 - 할인금액`
+- [x] 12월 이벤트 배지 출력
+    - [x] 이벤트 배지가 부여되지 않는 경우 `없음` 출력
+
+# 입력 예외 사항
+
+- [x] 방문할 날짜 입력 예외
+    - [x] 1 이상 31 이하의 숫자가 아닌 경우
+        - `[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.` (요청 사항)
+- [x] 주문할 메뉴와 개수 입력
+    - [x] 메뉴판에 없는 메뉴를 입력
+        - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+    - [x] 메뉴의 개수가 1 이상의 정수가 아닌 경우
+        - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+    - [x] 메뉴의 형식이 정해진 형식을 벗어난 경우
+        - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+    - [x] 중복 메뉴를 입력한 경우
+        - `[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.` (요청 사항)
+    - [x] 메뉴 개수가 20개를 초과한 경우
+        - `[ERROR] 메뉴는 한 번에 최대 20개까지만 주문할 수 있습니다. 다시 입력해 주세요.` (추가)
+    - [x] 음료만 주문한 경우
+        - `[ERROR] 음료만 주문할 수 없습니다. 다시 입력해 주세요.` (추가)
+- 이외 모든 예외 사항에 `[ERROR]` Prefix 사용
+
+# 기능 명세
+
+## Config
+
+- [x] Menu
+    - [x] 메뉴 이름으로 Menu 찾아 반환
+- [x] MenuType
+- [x] ErrorMessage
+
+## Controller
+
+- [x] EventController
+    - [x] 날짜를 입력받아 `Bill`을 생성
+    - [x] 주문을 입력받아 `Bill`에 추가
+    - [x] 위 두 과정 중 예외가 발생할 경우 반복하여 입력받음
+    - [x] 필요한 모든 결과를 출력
+
+## Domain
+
+- [x] Order
+    - [x] 주문 메뉴와 주문 수량을 저장
+    - [x] 1이상의 주문 수량만 사용하도록 검증
+    - [x] 가격(`메뉴 가격 x 주문 수량`)을 반환
+    - [x] 특정 `Order`와 같은 메뉴인지 확인
+    - [x] `OrderDTO` 반환
+    - [x] `List<Order>`의 모든 주문 수량을 누적하여 합산
+        - [x] `MenuType`을 조건으로 사용할 수 있도록 오버로딩
+- [x] Bill
+    - [x] 주문 리스트와 날짜를 저장
+    - [x] 주문을 추가
+        - [x] 중복되는 메뉴가 있는 주문인지 검증
+        - [x] 최대 주문수를 넘지 않는지 검증
+    - [x] 음료만 주문했는지 검증
+    - [x] 주문을 초기화
+    - [x] 전체 주문 금액을 계산
+    - [x] 특정 타입의 메뉴 주문량을 반환
+    - [x] `CheckEventDate`의 메서드 오버로딩
+    - [x] 주문 날짜를 반환
+- [x] Date
+    - [x] 생성을 위한 날짜가 1 ~ 31 범위의 값인지 검증
+    - [x] 요일을 확인
+    - [x] 주말인지 확인 -> 평일 할인, 주말 할인
+    - [x] 달력에 별이 있는 특별한 날인지 확인 -> 특별 할인
+    - [x] 크리스마스 이전인지 확인 -> 크리스마스 디데이 할인
+    - [x] 첫째날로부터 얼마나 지났는지 확인 -> 크리스마스 디데이 할인
+    - [x] 날짜를 반환
+- [x] EventHandler
+    - [x] 증정 메뉴(샴페인) 존재 여부
+    - [x] 혜택 내역
+    - [x] 할인 금액
+    - [x] 총혜택 금액
+- [x] Event
+    - [x] 이벤트 조건에 맞는지 확인
+    - [x] 이벤트 혜택을 반환
+- [x] Badge
+    - [x] 가격에 맞는 이벤트 배지를 반환
+
+## DTO
+
+- [x] BenefitDTO -> 혜택 이름, 혜택 금액을 전달하는 객체
+- [x] OrderDTO -> 메뉴 이름, 주문 개수를 전달하는 객체
+
+## View
+
+- [x] InputView
+    - [x] 날짜 입력
+    - [x] 주문 입력
+- [x] OutputView
+    - [x] 각 제목 출력
+    - [x] 주문 메뉴 출력
+    - [x] 할인 전 총주문 금액 출력
+    - [x] 증정 메뉴 출력
+    - [x] 혜택 내역 출력
+    - [x] 총혜택 금액 출력
+    - [x] 할인 후 예상 결제 금액 출력
+    - [x] 이벤트 배지 출력
+- [x] ViewMessage
+- [x] ViewTitle
+
+## Util
+
+- [x] IntParser
+- [x] OrderParser
+- [x] RetryExecutor
+
+# 테스트 코드
+
+- [x] Badge
+    - [x] `getBadgeNameWithBenefitPrice` 동작 확인
+        - [x] 0 -> 없음
+        - [x] 2500 -> 없음
+        - [x] 5000 -> 별
+        - [x] 9000 -> 별
+        - [x] 10000 -> 트리
+        - [x] 17000 -> 트리
+        - [x] 20000 -> 산타
+        - [x] 50000 -> 산타
+- [x] Bill
+    - [x] `from` 동작 확인
+        - [x] 잘못된 date 값 사용시 예외 발생 -> Date 클래스에서 테스트
+    - [x] `add` 동작 확인
+    - [x] `add` 중복된 메뉴 입력시 예외 발생
+    - [x] `add` 20개 이상 메뉴 입력시 예외 발생
+        - [x] 여러 메뉴의 총 수량이 20개 이상인 경우
+        - [x] 한 메뉴의 수량이 20개 이상인 경우
+    - [x] `clearOrder` 동작 확인
+    - [x] `getTotalPrice` 동작 확인
+    - [x] `getTypeCount` 동작 확인
+        - [x] DESSERT 타입 확인
+        - [x] MAIN 타입 확인
+    - [x] `getDateValue` 동작 확인
+    - [x] 음료만 주문한 경우 예외 발생
+    - [x] 이외의 Date 관련 메서드는 Date 클래스에서 테스트
+- [x] Date
+    - [x] 생성 시 `validate` 동작 확인
+        - [x] 1 -> 동작
+        - [x] 25 -> 동작
+        - [x] 31 -> 동작
+    - [x] 생성 시 `validate` 예외 발생
+        - [x] -5 -> 예외 발생
+        - [x] 0 -> 예외 발생
+        - [x] 32 -> 예외 발생
+        - [x] 75 -> 예외 발생
+    - [x] `isWeekend` 동작 확인
+        - [x] 1 -> true
+        - [x] 8 -> true
+        - [x] 16 -> true
+        - [x] 17 -> false
+        - [x] 25 -> false
+        - [x] 28 -> false
+    - [x] `isSpecialDay` 동작 확인
+        - [x] 3 -> true
+        - [x] 25 -> true
+        - [x] 31 -> true
+        - [x] 13 -> false
+        - [x] 22 -> false
+    - [x] `isNotPassedChristmas` 동작 확인
+        - [x] 1 -> true
+        - [x] 14 -> true
+        - [x] 25 -> true
+        - [x] 26 -> false
+        - [x] 31 -> false
+    - [x] `timePassedSinceFirstDay` 동작 확인
+        - [x] 1 -> 0
+        - [x] 18 -> 17
+        - [x] 25 -> 24
+- [x] Event
+    - [x] `getEventName` 동작 확인
+    - [x] `isDiscount` 동작 확인
+    - [x] `checkCondition` 동작 확인
+        - [x] 크리스마스 디데이 할인 -> `isNotPassedChristmas`에서 테스트
+        - [x] 평일 할인 -> `isWeekend`에서 테스트
+        - [x] 주말 할인 -> `isWeekend`에서 테스트
+        - [x] 특별 할인 -> `isSpecialDay`에서 테스트
+        - [x] 증정 이벤트
+            - [x] 티본스테이크(2) + 아이스크림(2) = 120,000 -> true
+            - [x] 티본스테이크(2) + 시저샐러드(5) = 150,000 -> true
+            - [x] 바비큐립(1) + 양송이수프(1) = 60,000 -> false
+    - [x] `getBenefit` 동작 확인
+        - [x] 크리스마스 디데이 할인
+            - [x] 1 -> 1000
+            - [x] 11 -> 2000
+            - [x] 25 -> 3400
+        - [x] 평일 할인
+            - [x] 양송이수프(1) + 바비큐립(1) -> 0
+            - [x] 타파스(1) + 해산물파스타(1) + 초코케이크(2) -> 4046
+            - [x] 바비큐립(2) + 초코케이크(2) + 아이스크림(3) -> 10115
+        - [x] 주말 할인
+            - [x] 양송이수프(2) + 제로콜라(2) -> 0
+            - [x] 타파스(1) + 티본스테이크(1) -> 2023
+            - [x] 티본스테이크(1) + 바비큐립(2) + 해산물파스타(3) + 제로콜라(6) -> 12138
+        - [x] 특별 할인 -> 항상 1000
+        - [x] 증정 이벤트 -> 항상 25000
+    - [x] 모든 이벤트에서 총주문금액 10000원 이상부터 이벤트 적용
+- [x] EventHandler
+    - [x] `hasChampagneGift` 동작 확인 -> Event `checkCondition`에서 테스트
+    - [x] `getTotalDiscountPrice` 동작 확인
+        - [x] 1일, 티본스테이크(2) + 아이스크림(2) -> 5046
+        - [x] 3일, 티본스테이크(2) + 아이스크림(2) -> 10292
+        - [x] 13일, 해산물파스타(10) + 초코케이크(1) + 아이스크림(2) -> 8269
+        - [x] 25일, 크리스마스파스타(2) + 샴페인(2) -> 4400
+        - [x] 30일, 시저샐러드(5) + 바비큐립(2) + 티본스테이크(1) + 레드와인(2) -> 6069
+    - [x] `getTotalBenefitPrice` 동작 확인
+        - [x] 1일, 티본스테이크(2) + 아이스크림(2) -> 30046
+        - [x] 3일, 티본스테이크(2) + 아이스크림(2) -> 10292
+        - [x] 13일, 해산물파스타(10) + 초코케이크(1) + 아이스크림(2) -> 33269
+        - [x] 25일, 크리스마스파스타(2) + 샴페인(2) -> 4400
+        - [x] 30일, 시저샐러드(5) + 바비큐립(2) + 티본스테이크(1) + 레드와인(2) -> 31069
+    - [x] `getAllBenefit` 동작 확인
+        - [x] 1일, 티본스테이크(2) + 아이스크림(1) -> 크리스마스(1000), 주말(4046)
+        - [x] 3일, 티본스테이크(1) + 아이스크림(4) -> 크리스마스(1200), 평일(8092), 특별(1000)
+        - [x] 13일, 해산물파스타(10) + 초코케이크(1) + 아이스크림(2) -> 크리스마스(2200), 평일(6069), 증정(25000)
+        - [x] 25일, 크리스마스파스타(2) + 샴페인(2) -> 크리스마스(3400), 특별(1000)
+        - [x] 30일, 시저샐러드(5) + 바비큐립(2) + 티본스테이크(1) + 레드와인(2) -> 주말(6069), 증정(25000)
+- [x] Order
+    - [x] `create` 동작 확인
+        - [x] 잘못된 메뉴 이름 사용시 예외 발생 -> Menu 클래스에서 테스트
+        - [x] 잘못된 count 사용시 예외 발생
+    - [x] `getPrice` 동작 확인
+        - [x] 타파스(2) -> 11000
+        - [x] 바비큐립(3) -> 162000
+        - [x] 아이스크림(5) -> 25000
+    - [x] `isSameMenu` 동작 확인
+        - [x] 타파스, 타파스 -> true
+        - [x] 레드와인, 레드와인 -> true
+        - [x] 티본스테이크, 아이스크림 -> false
+        - [x] 초코케이크, 바비큐립 -> false
+    - [x] `accumulateCount` 동작 확인
+        - [x] 티본스테이크(5) -> 5
+        - [x] 양송이수프(3) + 바비큐립(1) + 아이스크림(3) -> 7
+    - [x] `accumulateCount` type 사용하여 동작 확인
+        - [x] DESSERT, 티본스테이크(2) + 제로콜라(4) -> 0
+        - [x] MAIN, 양송이스프(3) + 바비큐립(1) + 해산물파스타(2) + 아이스크림(5) -> 3
+- [x] Menu
+    - [x] `from` 동작 확인
+        - [x] 양송이수프 -> Menu.SOUP
+        - [x] 해산물파스타 -> Menu.SEAFOOD_PASTA
+    - [x] 메뉴판에 없는 메뉴 입력시 예외 확인
+- [x] IntParser
+    - [x] `parseIntOrThrow` 동작 확인
+        - [x] "123" -> 123
+        - [x] "-123" -> -123
+        - [x] "2147483647" -> 2147483647 (최댓값)
+        - [x] "-2147483648" -> -2147483648 (최솟값)
+    - [x] 정수형태가 아닌 문자열 입력시 예외 발생
+        - [x] ABC
+        - [x] 12L
+        - [x] 13.5
+    - [x] Integer 범위를 벗어난 문자열 입력시 예외 발생
+        - [x] 2147483648
+        - [x] -2147483649
+        - [x] 999999999999999
+- [x] OrderParser
+    - [x] `parseOrderOrThrow` 동작 확인
+        - [x] "타파스-1,제로콜라-2,티본스테이크-5" -> 타파스(1) + 제로콜라(2) + 티본스테이크(5)
+        - [x] "타파스 - 1, 제로콜라- 2, 티본 스테이크 -5" -> 타파스(1) + 제로콜라(2) + 티본스테이크(5)
+        - [x] "티본스테이크-1" -> 티본스테이크(1)
+        - [x] "해산물파스타-3 , " -> 해산물파스타(3)
+    - [x] 잘못된 형식의 주문을 입력시 예외 발생
+        - [x] "타파스: 1, 제로콜라: 2"
+        - [x] "타파스-1 제로콜라-2 파스타-3"
+        - [x] "타파스 1 제로콜라 2 파스타 3"
+        - [x] "타파스, 제로콜라, 파스타"
+        - [x] "타파스 -"
+        - [x] "- 3"
+- [x] OutputView
+    - [x] `printHelloMessage` 동작 확인
+    - [x] `printEventPreviewTitle` 동작 확인
+    - [x] `printOrder` 동작 확인
+    - [x] `printTotalPrice` 동작 확인
+    - [x] `printGift` 동작 확인
+    - [x] `printAllBenefit` 동작 확인
+    - [x] `printBenefitPrice` 동작 확인
+    - [x] `printAfterDiscountPrice` 동작 확인
+    - [x] `printBadge` 동작 확인

리뷰: 클래스별로 예외사항 예시나 기능을 꼼꼼히 작성해서 좋았습니다! 프로그램을 조금 요약해서 정리한 정보도 있으면 좋을 것 같습니다!