본문 바로가기
활동/우테코 프리코스

우테코 프리코스 4: java-christmas

by lucid_07 2023. 11. 18.
반응형

 

우테코 프리코스 4: java-christmas를 구현하며 생각해본 내용을 정리하였습니다.

https://github.com/dearmysolitude/java-christmas-6-dearmysolitude

 

이 단계에서는 문제 해결을 위해 1단계부터 지금까지 피드백 받은 내용을 적용하고 문제 해결에 초점을 맞추어 구현하였습니다. MVC 모델에 대해서는 정식으로 적용하지는 않았으므로 감안하고 보시면 감사하겠습니다.

  • 주어진 입력 라이브러리만 사용할 것
  • Output View 와 Input View를 나누어서 구현할 것
  • 단위 테스트 코드를 적용할 것
  • 메서드는 하나의 기능만 하도록 구현할 것
  • 3항 연산자와 else 예약어를 사용하지 말 것
  • 클래스에서 getter로 필드값을 가져와서 처리하지 말고, 되도록이면 클래스 내에서 조작할 것
  • 테스트하기 쉽도록 메서드를 작성하고 리팩터링 하는 것을 고려할 것
  • 실행 가능한 가장 작은 단위로 구현하여 패치해 나갈 것, 가장 중요한 기능부터 순차적으로 해결한다(추상적인, 대규모 문제를 해결할 때의 전략).

 

기능 요구사항

12월 이벤트를 맞이하여 날짜와 메뉴를 입력하면 그에 따른 이벤트 사항을 출력하는 프로그램입니다.

상세 요구 기능 1: 이벤트

1. 크리스마스 디데이 할인
   - 12 월 1 일~ 25 일
   - 1000원으로 시작하여 크리스마스가 다가올수록 할인 금액이 100원씩 증가
   - 총 주문 금액에서 해당 금액만큼 할인
2. 평일 할인(일 ~ 목): 디저트 메뉴 1개 당 -2023
3. 주말 할인(금, 토): 메인 메뉴 1개 당 -2023
4. 특별 할인: 달력에 별이 있다면 총 금액에서 -1000
5. 증정 이벤트: 할인 전 총 금액 12만원 이상일 때 샴페인 1개 증정
6. 디데이 할인을 제외하면 12월 전일 적용

상세 요구 기능 2: 배지 부여

- 총 혜택 금액에 따라 배지 부여
  - 5000원 이상: 별
  - 10000원 이상: 트리
  - 20000원 이상: 산타

상세 요구 기능 3: 주의 사항

- 총 주문 금액 10000원 이상부터 적용
- 음료만 주문 시 주문 불가
- 메뉴는 한 번에 최대 20 개만(음료 포함)

 

해결 전략

  • 우선 의도적으로 작동하는 프로그램을 작성하여 리팩터링 하는 것을 목표로 시작하였다.
  • 문제가 복잡하므로 데이터를 어떻게 주고받을지 먼저 생각하여 과정이 진행되더라도 산만하지 않고 일관되게 구성할 수 있었다. 다음은 처음 생각했던 내용들.
    • 프로그램 자체가 날짜와 메뉴를 입력받아 할인 내역을 추출하고 선물, 뱃지를 추가하여 출력하는 간단한 구성이므로, 크게 기능 구성은 입력, 출력, 할인(선물, 뱃지)이라고 생각하였다.
    • 가장 먼저 할인에 대한 로직을 실행하는 클래스를 우선적으로 구현한다.
    • 그리고 할인 내역에 대한 내용은 Discount 클래스에서 모두 관리하고 상위의 클래스인 EventPlanner의 경우 선물과 뱃지까지 필드와 메서드로 관리한다.
    • 출력할 때에는 Eventplanner가 처음 생성될 때 초기화된 값인지 조회하여 값이 변하지 않았으면 없음을 출력하도록 할 것이다.

 

어플리케이션 설계

  1. 상수 설정
    • 먼저 필요한 기능의 상수들을 구분하여 작성하였다: 연관된 상수들인 Menu, 그에 종속된 Sort, Badge를  Enum클래스로 정의하였다.
    • 사용하면서 자주 반복되는 상수들은 구현하고 리팩터링 하면서 수시로 추가하였다: 특히, 중요하면서 반복되고, 프로그램 환경 설정 처럼 바꿀 수 있도록 할인율이나 증정품을 증정할 최소한의 금액 등을 우선적으로 추가함.
  2. 실행 가능한 가장 작은 주요 기능을 먼저 구현하기 위해, 메뉴와 날짜를가지고 할인 이벤트를 결정하는 메서드를 먼저 작성하기로 하였다: Discount 클래스
    • 뱃지와 gift에 대한 기능은 배제하여 책임 권한을 명확히 하였다.
    • 적용할 할인을 확인하는 메서드와 할인 금액을 결정하는 메서드들로 구성하였다.
    • 날짜도 동일하게 InputView로 입력받은 내용을 EventPlanner에서 필드로 받고, 이를 인자로 받아 Discount 내역을 결정할 것이다: 적용할 이벤트의 종류를 판단.
    • 입력 받은 메뉴는 InputView에서 가공하여 List<Menu>의 형태로 인수로 전달받은 EventPlanner의 필드값으로 Discount 클래스에 인수로 전달한다: 할인 금액을 결정한다.
  3. 그 다음, 이벤트가 결정되면 그로부터 얻은 이벤트 정보와 더불어 메뉴를 읽어 상세한 할인 금액을 결정하였다. 이 단계에서는 클래스를 딱히 분리하지는 않았다.
    • EventPlanner 클래스: 메인 기능들이 실행되는 주요 클래스이다.
  4. 여기까지 시작하면서 생각하였던 구조이다. InputView 와 OutputView를 제외하고는 아래 내용은 구성하면서 필요하여 추가한 내용이다.
  5. EventPlanner 의 메서드들을 수행하고 매개변수를 전달하는 Controller 클래스를 추가하였다. Controller를 가지고 여러 클래스들을 중개하여 자료를 전달하고 메인 함수에서는 기능별 컨트롤러의 메서드들만 호출하도록 하였다.
  6. 주어진 InputView와 OutputView를 분리해서 구현해야 한다.
    • 주요 기능은 아니므로 가장 나중에 작성하였다.
    • InputView에서는 Date 입력과 오더 입력만을 진행하였는데, 요구사항에서 제시한 예외 처리를 진행하도록 한다. 20개 이상의 메뉴를 고를 수 없는 것, 음료만 주문할 수 없는 것에 대한 것은 EventPlanner에서 처리하도록 계획하였다. 그 외의 잘못 입력된 사항에 대해서는 InputView에서 모조리 예외처리 하도록 하였다.
    • Output View를 구현할 때에는 Dto등 클래스를 통해 전달하는 것이 결합도를 낮추는 것이 좋다고 생각하였지만, 우선 구현 속도를 위해 OutputView에 EventPlanner 클래스 의존성을 주입하여 구현하였다
  7. 단위 테스트 코드는 public메서드가 구현될 때마다 작성한다.

 

구현하면서

  • 자바의 다양한 기능들을 추가적으로 사용하였으나 그건 코드를 읽어보면 될 듯?
  • 처음에는 필드값들을 Null로 초기화 하였으나, NullPointerException을 피하고, Null로 초기화하는것이 메리트가 없었기 때문에 0으로 초기화하고 출력할 때에만 OutputView에서 이에 대한 조건 처리만 주의하여 구현하였다.
  • 처음 구현할 때 실수로 List<Menu>로 구현하였다. 원래는 Menus를 반복적으로 추가하여 주문 메뉴 숫자를 체크하려고 하였으나, 나중에는 Menu 클래스를 한 번 wrapping하는 Order클래스를 추가하여 코드를 다시 수정하였다(메뉴의 숫자를 추가해야할 때 쯔음엔 반복 추가가 이미 불가능했다.).
  • OutputView에서 출력 내용을 결정할 때 주의해야했던 부분은:
    • 할인이 적용되지 않은 부분이었다. 이 경우 초기화 후 메뉴 총 가격이 10000원을 넘지 않을경우 할인 산출 메서드를 호출하지 않고, 출력부분에서 초기화되고 변경이 없다면(할인 가격에 대한 산출은 Discount클래스의 메서드를 호출하여 전체 필드를 수정하고 최종적으로 totalDiscount에 반영하므로 이 값이 0이라면 실행된 적이 없거나 할인이 없는 것이다.) 없음으로 출력하도록 하여 따로 케이스를 관리할 필요는 없었다.
    • 즉 케이스가 다름에도, (1)할인이 0인 경우와 (2)10000원 이하라서 이벤트 적용 대상이 아닌것을 따로 분리할 필요가 없었다.
  • 리팩터링 하면서 되도록 드러난 메서드들을 줄이고 하나로 묶는 작업을 하였다.
    • 입력 내용에 대한 로직상 유효성 검사는 하나의 메서드로 묶어 setMenuOrders안에 넣도록 수정한 것: 리팩터링시 테스트코드와 입력 로직의 수정이 불가피했다.
  • 특히, 출력과 입력 메서드들은 동일한 기능을 하는 반복 코드가 있었으므로 이에 대해 고치려고 하였다.
  • Controller를 구현하면서 복잡한 메서드들을 동일한 기능의 구성으로 묶어내며 로직을 좀 더 거시적으로 보기 쉽다는 것을 깨달았다.
  • New Bing를 사용하여 코드의 리뷰를 받은 것으로 도움 받을 수 있었다.

 

부족하다고 느낀 점

코드의 결합도가 높게 나타나는 것이 나중에 코드에 대한 유지보수와 변경에 대해 유연하게 대처할 수 없다는 것을 알고 있다. 정식으로 이론부터 학습하지 않고 작동하는 코드를 구현후 리팩터링 하는 방식으로 진행하였기 때문인데, 코드를 리팩터링하고 강의 등을 통해 처음 설계부터 결합도를 낮추는 기법들에 대한 내용을 적용하는 방법을 숙지하면 보다 쉽게 리팩터링/구현할 수 있을 것이다.

다만 피드백 받은대로, 다른 사람들과 피어 리뷰를 통해 빠르게 이를 보완할 수 있을 것으로 보인다.

 

소감 한마디: 4 주간 재밌었다. 이제 뭐하지

 

반응형