✔ 크리스마스 프로모션
미션 제출 방법
- 미션 요구사항을 파악해 기능을 구현한 후 GitHub을 통해 add, commit, push
- 우아한테크코스 지원 플랫폼에 GitHub ID, 비공개 저장소 주소, 과제 진행 소감을 프리코스 과제로 제출
3주차 공통 피드백 정리
1) 함수 라인은 15라인으로 제한한 후 15라인이 넘어간다면 함수 분리를 위한 고민을 한다
2) 발생할 수 있는 예외 상황에 대해 고민한다
3) 비즈니스 로직과 UI 로직을 분리한다
4) 연관성이 있는 상수는 static final 대신 enum을 활용한다
5) final 키워드를 사용해 값의 변경을 막는다
6) 객체의 상태 접근을 제한한다
7) 객체는 객체스럽게 사용한다
8) 필드의 수를 줄이기 위해 노력한다
9) 성공하는 케이스 뿐만 아니라 예외에 대한 케이스도 테스트한다
10) 테스트 코드도 코드이므로 리팩토링을 통해 개선해 나가야 한다
11) 테스트를 위한 코드는 구현 코드에서 분리되어야 한다
12) 단위 테스트하기 어려운 코드를 외부로 분리하는 시도를 하여 단위 테스트가 가능하도록 리팩토링하자
13) private 함수를 테스트하고 싶다면 클래스 분리를 고려하자
+) getter를 사용하는 대신 객체에 메시지를 보내자
+) 순수 값 프로퍼티를 가져와야 할 때는 Unmodifiable Collection을 사용해 외부에서 변경하지 못하도록 하게 하자
진행 방식
1) 과제 진행 요구 사항 파악하기
1. 미션 저장소를 비공개 저장소로 Fork & Clone해 시작하기
2. 기능을 구현하기 전에 구현할 기능 목록을 정리해 추가하기
3. 커밋 단위는 기능 목록 단위로 추가 (커밋 메시지 컨벤션 가이드를 참고해 커밋 메시지를 작성)
2) 프로그래밍 요구 사항 파악하기
1. JDK 17 버전에서 실행
2. 프로그램 실행의 시작점은 Application의 main()
3. Java 코드 컨벤션 가이드를 준수하여 프로그래밍
4. build.gradle 파일은 변경할 수 없음
5. ApplicationTest의 모든 테스트가 성공해야 함
6. 파일, 패키지 이름을 수정하거나 이동하지 않음
7. Randoms의 pickNumberInRange() 및 Console의 readLine() API를 사용하여 구현
8. indent depth를 3이 넘지 않도록 구현
9. 3항 연산자를 쓰지 않음
10. 함수가 한 가지 일만 하도록 최대한 작게 만들어라
11. 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인하라 (각 함수별로 테스트를 작성)
12. 함수의 길이가 15라인을 넘어가지 않도록 구현하라
13. else 예약어를 쓰지 마라
14. 도메인 로직에 단위 테스트를 구현해라 (단, UI 로직은 제외)
15. 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현하라. (입출력 클래스를 구현)
3) 기능 요구 사항 파악하기
[프로그램 설명 - 크리스마스 프로모션]
1. 식당에 방문할 날짜와 메뉴를 미리 선택하여 이벤트 플래너에게 전달
2. 주문 메뉴 확인
3. 이벤트 안내 제공
[기능 요구 사항 설명]
1. 식당에 방문할 날짜와 메뉴를 선택하여 입력
2. '주문 메뉴' 확인 제공
3. '할인 전 총주문 금액' 미리보기 제공
4. '증정 메뉴' 미리보기 제공
5. '혜택 내역' 미리보기 제공
6. '총혜택 금액' 미리보기 제공
7. '할인 후 예상 결제 금액' 미리보기 제공
8. '12월 이벤트 배지' 미리보기 제공
[입출력 요구 사항 설명]
안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.
12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)
3
주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)
티본스테이크-1,바비큐립-1,초코케이크-2,제로콜라-1
12월 3일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!
<주문 메뉴>
티본스테이크 1개
바비큐립 1개
초코케이크 2개
제로콜라 1개
<할인 전 총주문 금액>
142,000원
<증정 메뉴>
샴페인 1개
<혜택 내역>
크리스마스 디데이 할인: -1,200원
평일 할인: -4,046원
특별 할인: -1,000원
증정 이벤트: -25,000원
<총혜택 금액>
-31,246원
<할인 후 예상 결제 금액>
135,754원
<12월 이벤트 배지>
산타
구현할 기능 목록 정리
1) 프로그램이 시작되면 식당 예상 방문 날짜를 입력받기 위해
'안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.
12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!) ' 출력
2) 식당 예상 방문 날짜 저장
2023년 12월은 1일부터 31일까지 있으므로 1일부터 31일까지의 숫자만 입력 받아 저장
[예외]
숫자가 아닐 때
1 이상부터 31 이하의 숫자가 아닐 때
→ 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키고
'[ERROR]'로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받음
3) 주문 메뉴와 개수를 입력받기 위해
' 주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)' 출력
4) 주문 메뉴와 개수 저장
주문 메뉴와 그에 따른 메뉴의 개수 저장
[예외]
쉼표(,)로 구분되지 않을 때)
메뉴 형식이 예시와 다를 때
메뉴판에 없는 메뉴일 때
중복 메뉴를 입력했을 때
메뉴의 개수가 숫자가 아닐 때
메뉴의 개수가 1보다 작을 때
음료만 주문할 때
메뉴를 20개보다 많이 주문할 때
→ 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키고
'[ERROR]'로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받음
5) '주문 메뉴' 출력
'메뉴이름 _개' 형식으로 순서 상관없이 자유롭게 출력
6) '할인 전 총주문 금액' 출력
할인 전 총주문 금액을 천 단위 쉼표를 넣어 출력
7) '증정 메뉴' 출력
[증정 메뉴가 있을 경우]
할인 전 총주문 금액이 12만원 이상일 때, '샴페인 1개' 출력
[증정 메뉴가 없을 경우]
증정 이벤트에 해당하지 않는 경우, '없음' 출력
총주문 금액이 10,000원 이상이 아닐 경우, '없음' 출력
8) '혜택 내역' 출력
[혜택 내역이 있을 경우]
1일부터 25일 사이라면 '크리스마스 디데이 할인' 출력 (1,000원으로 시작하여 날마다 할인 금액 100원 증가)
일요일 ~ 목요일이라면 '평일 할인' 출력 (디저트 메뉴를 메뉴 1개당 2,023원 할인)
금요일, 토요일이라면 '주말 할인' 출력 (메인 메뉴를 메뉴 1개당 2,023원 할인)
별이 있는 일요일, 크리스마스 당일이라면 '특별 할인' 출력 (총주문 금액에서 1,000원 할인)
증정 메뉴를 받았다면 '증정 이벤트' 출력 (할인 전 총주문 금액이 12만원 이상일 때, 샴페인 1개 증정)
천 단위 쉼표를 넣어 출력
[혜택 내역이 없을 경우]
적용된 이벤트가 하나도 없다면, '없음' 출력
총주문 금액이 10,000원 이상이 아닐 경우, '없음' 출력
9) '총혜택 금액' 출력
[혜택 금액이 있을 경우]
'할인 금액의 합계 + 증정 메뉴의 가격'을 천 단위 쉼표를 넣어 출력
[혜택 금액이 없을 경우]
총주문 금액이 10,000원 이상이 아닐 경우, '0원' 출력
10) '할인 후 예상 결제 금액' 출력
'할인 전 총주문 금액 - 할인 금액'을 천 단위 쉼표를 넣어 출력
11) '12월 이벤트 배지' 출력
[이벤트 배지가 있을 경우]
총혜택 금액이 5천 원 이상일 경우, '별' 출력
총혜택 금액이 1만 원 이상일 경우, '트리' 출력
총혜택 금액이 2만 원 이상일 경우, '산타' 출력
[이벤트 배지가 없을 경우]
총혜택 금액이 5천 원보다 적을 경우, '없음' 출력
총주문 금액이 10,000원 이상이 아닐 경우, '없음' 출력
기능 구현
1) MVC 패턴을 적용해서 설계해보자
Model : 애플리케이션의 정보, 데이터 등의 가공을 책임지는 컴포넌트
View : 사용자 인터페이스 요소로 데이터를 기반으로 사용자들이 볼 수 있는 화면
Controller : 데이터와 사용자 인터페이스 요소를 잇는 다리 역할로, 이벤트들을 처리
[필요한 Model]
1. 메뉴 관련 객체 : 카테고리, 메뉴
2. 고객 관련 객체 : 방문 날짜, 주문, 고객
3. 이벤트 관련 객체 : 이벤트 내역, 할인, 뱃지
4. 결제 관련 객체 : 청구서
[필요한 View]
1. 사용자 입력 화면
2. 사용자 출력 화면
[필요한 Controller]
1. 메인 컨트롤러
2. 방문 날짜 컨트롤러
3. 주문 컨트롤러
4. 고객 컨트롤러
5. 청구서 컨트롤러
[커스텀 예외 처리 exception]
1. 날짜 입력 예외처리
2. 주문 입력 예외처리
[그 외 utils]
1. 규칙 값 (Rule)
2. 출력 메시지 값 (Message)
3. 입력값 형변환 (InputParser)
model
|- menu
|- Category.java : 카테고리 객체 Domain
|- Menu.java : 메뉴 객체 Domain (Category를 포함)
|- customer
|- VisitDate.java : 방문 날짜 객체 Domain
|- Order.java : 주문 객체 Domain (Menu를 포함)
|- Customer.java : 고객 객체 Domain (VisitDate, Order를 포함)
|- event
|- Event.java : 이벤트 내역 객체 Domain
|- Discount.java : 할인 객체 Domain (Event를 포함)
|- DiscountMachine.java : 이벤트 내역에 맞는 할인 객체 생성 Service
|- Badge.java : 뱃지 객체 Domain
|- bill
|- Bill.java : 청구서 객체 Domain (Discount, Badge를 포함)
view
|- InputView.java : 사용자 입력 View
|- OutputView.java : 사용자 출력 View
controller
|- MainController.java : 메인 시작 Controller
|- customer
|- VisitDateController.java : 방문 날짜 Controller
|- OrderController.java : 주문 Controller
|- CustomerController.java : 고객 Controller
|- bill
|- BillController.java : 청구서 Controller
exception
|- InvalidDayException.java : 날짜 입력 커스텀 예외처리 exception
|- InvalidOrderException.java : 주문 입력 커스텀 예외처림 exception
utils
|- constant
|- Rule.java : 규칙 값 Constant
|- Message.java : 출력 메시지 값 Constant
InputParser.java : 입력값 형변환 Util
Application.java
2) TDD를 적용해가면서 구현해보자
1. 테스트 코드 작성
2. 테스트 코드를 통과하기 위한 최소한의 코드 작성
3. 테스트 코드 통과를 유지하면서 리팩토링
+) 테스트 코드를 짜기 힘들 경우 코드 먼저 짜고서라도 테스트 코드 작성하기
3) 리팩토링
클린 코드에 맞춰 리팩토링을 하려고 노력했다.
1. 의미 있는 메소드명, 변수명, 클래스명을 사용하자
2. 중복을 최대한 제거하자
3. 한 메소드에서는 하나의 일만 하도록 하자
4. 상수 대신 열거형을 사용하자
5. 인터페이스 사용과 의존성 주입을 통해 결합도를 낮추자
6. final 사용을 통해 변수를 불변하도록 하자
7. 생성자 대신 정적 팩토리 메서드를 사용해보자
8. 접근 권한을 최소화하자
9. 가독성이 떨어지지 않는다면 스트림을 사용해보자
10. 각 함수별로 테스트를 작성하여 정상 동작함을 확인하자
이 외에도 다양한 곳을 리팩토링하려고 했다.
회고
이번 미션을 통해 이전 미션에서 Enum의 사용과 함수 작게 만들기에 대한 중요성을 명확히 이해할 수 있었습니다.
프리코스 이전까지의 개발에서 저는 단순히 열거형을 상수를 모아두는 용도로만 사용했었습니다. 하지만 프리코스를 진행하며 다양한 Enum에 대한 자료를 찾아보고 우아한 기술블로그에 소개된 'Java Enum 활용기' 등을 살펴보며 Enum을 통해 데이터들 간의 연관관계를 표현하는데 유용하다는 것을 깨달았습니다. 이번 미션에도 이를 좀 더 적용하여 Badge, Event, Menu와 같이 하나의 주제에 대해서 연관이 있을 경우 이를 추출하여 관리함으로써 가독성을 높일 수 있었습니다. 또한 추후 이벤트나 뱃지 등에서 변경 사항이 생길 경우, 여러 코드를 살펴보기 보다는 해당 Enum 클래스에 추가 또는 수정을 통해 시간을 최소화하여 작업할 수 있다는 것도 실감하게 되었습니다.
다음으로 함수를 작게 만드는 것이 이해도를 높이는데 차원이 다르다는 것을 알게 되었습니다. 요구사항이 많아지다보니 나 혼자 만이 아니라 다른 개발자와 함께 협업을 하며 작업을 할 경우 내 코드를 이해할 수 있을까? 라는 의문을 가졌습니다. 이에 나만 이해하고 끝내는 코드가 아니라 누군가에게 보여줄 때 쉽게 이해할 수 있는 코드를 작성하기 위해 노력했습니다. 이를 위해 함수명을 명확하게 작성하고 함수가 하나의 일만 하도록 작게 만들어 코드의 이해도를 높이기 위해 노력했습니다.
그 외에도 이전 미션에서 처음 시도해봤던 값 객체를 적극적으로 적용하여 데이터의 불변성을 유지하고 코드의 가독성을 높이기 위해 필드를 클래스로 감싸 캡슐화하도록 했습니다. 특히 이번 미션에서는 고객에게 받은 날짜와 주문에 대한 검증이 많다고 느꼈습니다. 캡슐화를 통해 각자의 클래스를 만드니 입력 받은 문자에 대해 검증을 할 때에도 자신의 클래스 내에서 자신에 대해서만 검증을 할 수 있어 이해도도 높아지고 한 클래스에 대한 코드의 길이도 줄여볼 수 있었습니다. 또한 커스텀 예외 처리 클래스를 작성하고 컨트롤러의 분리시켜보며, SOLID 원칙을 준수한 객체지향적인 개발을 하기 위해 노력하고자 했습니다.
4주간의 프리코스 미션을 진행하며 다양한 관점에서 저를 돌아볼 수 있었습니다.
먼저, SOLID 원칙이나 객체지향적인 개발에 대해서 몸소 느낄 수 있었습니다. 공부를 하면서 단순히 개념만 알고 있었던 것을 직접 코드에 적용해보고 계속해서 리팩토링해나가면서, 유지보수 하기 쉽고 유연하며 확장성을 높일 수 있는 객체지향의 장점을 느낄 수 있었습니다. 또한 프리코스 이전에 작성해왔던 나의 코드에 대해 프로그램이 실행되는 것이 끝이 아니라, 추후에 내가 다시 코드를 돌아보거나 어떤 개발자가 보더라도 이해하기 쉽도록 다시 리팩토링하고자 하는 마음을 가지게 되었습니다.
'Community > 우테코 프리코스' 카테고리의 다른 글
[우테코] 웹 백엔드 프리코스 3주차 미션 (0) | 2023.11.02 |
---|---|
[우테코] 웹 백엔드 프리코스 2주차 미션 (0) | 2023.10.26 |
[우테코] 웹 백엔드 프리코스 1주차 미션 (0) | 2023.10.19 |
[우테코] 2024 우아한테크코스 입학설명회 (0) | 2023.10.06 |