개발하다 생긴 일

perfect dish | Refactoring (3) - 주문과 영수증 구현기

로그관리자 2024. 5. 26. 22:54
728x90

🍽 Perfect dish 개발 일지

 

이 글은 개발 여정을 기록한 것으로 틀린 내용이 나올 수 있습니다. 점점 고쳐나가기 때문이죠 

 

 

✴️ 요구사항

이 부분에서 가장 중요한 건 

테이블 별 주문이 병합되는 것, 그에 따라 합계가 계속 변경되는 것이다. 

 

 

5번이나 수정한 사연 갑니다

지금 생각해 보면 정말 웃기고 별거 아니다. 왜 그랬니? 

 

✴️ 1차 설계

참고로 이때는 jpa로 리빌딩 전이다. (무려 인메모리로 컬렉션만을 사용해서 플젝을 구현하고 있었음 ㅋㅋ 왜..?)

주문을 할인 여부에 따라 바로 주문을 리스트로 담았다.

menu -> menuBoard -> order

메뉴는 isActive에 따라 menuBoard에 저장된다. 
할인이다? 할인 리스트. 할인 아니다? 일반 주문 리스트. 또 이걸 모아서 menuBoard에서 사용할 전체 주문리스트가 있다.

그리고 할인 리스트냐 아니냐 에 따라 합계 프로세스를 다르게 했다.

테스트 코드를 작성하면서 뭔가 잘 못 됨을 감지한다. 

이것은 후에 최종 주문서를 만들 때 또다시 주문을 테이블 별로 분리해서 다시 합쳐야 하는 번거로움이 있었다.

그리고 로직이 discount 적용하는 거 말고는 합계를 구한다. 는 기능이 중복된다는 거였다. 


그러니까 메뉴를 주문에 담을 때 나눠 담았다가 ~ 최종 주문서 만들 때 그걸 합쳐서 ~ 다시 ~ 또 ~ 테이블 번호로 나눠 ~ 🥲 

 

게다가 Optional이 필드로 마구 들어가 있다. 

Jpa로 리빌딩하면서 모두 수정되었지만 Optional에 대해 다시 학습하여 필드로는 지양함을 알게 되었다. 

 


왜 필드로 optional은 지양하는가 
-> 함수 반환을 목적으로 만들어졌음 
-> serializable을 구현하지 않아 직렬화를 하면 이상한 값이 나옴 


직렬화? 객체를 그대로 저장하면 가상주소까지 저장된다.

이 메모리 주소는 다른 시스템에 전달해도 의미가 없다. 
따라서 불필요한 정보는 제외하고 타입 정보, 값 정보를 byte 형태로 만들어 보내는 것 
직렬화를 하려면 클래스가 serializable을 구현해야 한다. 

 

 

 

 

 

 

 

✴️ 2차 설계

여기서부터는 jpa로 리빌딩 이후이다. 

메뉴엔티티에 isDiscount와 availability를 추가하여 
-> available인 메뉴만 메뉴판에 보이도록 menuBoard를 대체하였다. 

 

그리고 주문에서는 여러 메뉴를 담을 수 있고 각 메뉴별 개수도 담아야 하니까 hashMap에 담는다 결정한다.

근데 하다 보니 아무리 봐도 가독성이 너무 떨어진다. 다른 방법이 없을까

 

주문엔티티 단일 주문(OrderItem) / 주문(Order)으로 나눈다. 

여기까지의 흐름은 이렇다. 
Menu -> OrderItem -> Order 

 

 

✴️ 3차 설계

우선 orderItem에 필드로 tableNo, menu, count 가 있고 order에 tableNo, List <OrderItem>가 있다. (나머지 필드는 생략) 

orderItem들을 모아 테이블 번호로 분리해서 각 테이블 번호별로 order를 생성해야 한다. 

여태 request(dto)에서 엔티티로 변환하는 작업을 해왔는데 order에서는 바로 객체화할 수 없고 테이블 번호 별로 나누는 로직을 한 번 거쳐야 한다. 

근데 만약에 하던 대로 request에서 해결한다면 뭐 그냥 한 번에 객체로 바꿀 수 있겠다. 근데 그렇게 하면 단지 데이터를 옮기는 역할인 dto에게 또 다른 역할을 부여하게 된다. 

그렇다면 service로 가져가자 

그리고 Order에서 주문은 해당 메뉴가 지금 주문 가능한지 menu 쪽에서 검증하여 가져와 생성한다.

 

그리고 클래스 이름도 변경했다.
orderItem -> order (주문 담당)로 / order -> bill (최종 영수증을 만든다!)

 

 

✴️ 4차 설계

Bill에서는 테이블 별 주문을 합산하다.

한 번 주문 시 메뉴가 여러 개 일 수 있다.
테이블에 추가 주문을 하면 위의 주문이 여러 개 일 수 있다.   << 에서 이렇게 일단 이렇게 짰나 보다. 제가 왜 그랬는지 기억 안 나요

이때부터 quantity 처리에 대한 고민이 시작되었다. 


e.g.


테이블 no.3
주문 1 (첫 주문)
  메뉴 A, 2개
  메뉴 B, 3개
주문 2 (추가 주문)
  메뉴 A, 1개 일 때,

bill에서 

테이블 no.3
  메뉴 A, 3개 
  메뉴 B, 3개 

이런 식으로 수량을 바로 보이게 하고 싶었던 거다. 

근데 아무래도 List<Map<Menu, Integer>>가 보기 싫다. 가독성이 떨어진다. 

이 로직은 bill이 엔티티로 먼저 객체가 생성되고 주문을 합산해서 다시 그 객체에 넣는 방식에서 
bill 객체가 생성될 때 orders도 같이 생성되는 방식으로 발전했다. 

그러나 Map<Menu, Integer>이 필드였기 때문에 다른 로직들에서 맵 순회를 하며 데이터를 꺼내 써야 하기 때문에 구현이 복잡해진다. 

더 좋은 방법이 있을 거야 

 

 

✴️ 5차 설계

캡슐화하다.

bill에서 order에 수량도 표면적으로(?) 함께 담고 싶은데 List<Map<Menu, Integer>> 처럼 복잡한 컬렉션을 필드로는 지양하고 싶다. 

애초에 orderItem은 단일 주문 정보였으니까. orderInfo라는 embedded 클래스를 만들어 메뉴와 수량을 넣어 준다.

이제 주문을 할 때 테이블 번호와 메뉴, 수량을 전달받으면 orderInfo가 생성되면서 order에 테이블 별로 전달이 된다. 오 ~ 

추가 주문을 하면 orderInfo의 메뉴 이름을 탐색하여 같은 이름이면 그 수량을 증가시킨다.

 

 

+ Bill에도 주문서가 자동으로 병합되도록 하였다. 

 

 

그리고 Bill의 생성 플로우는 아래와 같이 변경되었다. 

 

테이블 번호로 bill 객체를 가져온다. (왜냐, order 생성될 때 테이블 번호에 따라 bill 객체가 자동 생성되고 order들이 알아서 쌓인다)
⬇️
bill의 status를 초기화시키고 (bill이 bill이 최종으로 만들어졌어! 의 flag 같은 거다)
⬇️
각 주문들의 합계를 구해서 넣어준다 (아래와 같이 역할과 책임을 위해 메서드를 배치하였다) 

⬇️
db에 진짜 저장! 

 

 


그래서 Bill에서의 주문 정보는
List<Map<Menu, Integer> ➡️ Map<Menu, Integer> ➡️ List<Order> 로 발전했다. 

대신 BillResponse에서 Map에 메뉴이름과 수량을 담아 가독성을 높였다. 

 

또한 메뉴와 수량을 묶어 orderInfo로 분리함으로 간결해졌다. 

물론 테스트 코드도 

모두 수정을 마치고 통과했다. 

 

 

 

 

 

구현하면서 메모를 좀 더 구체적으로 많이 남겨야겠다. 특히 에러를 캡쳐해 두지 않아서 ^^... !! 

노션이나 세분화한 git commit , 노트 지금 정도로는 기억나는 게 이 정도라 아쉽다. 

request에 validation 처리도 해야 하고 swagger로 api 문서 자동화, jacoco 코드 커버리지 측정도 해야 하고 ㅋㅋㅋ
member파트는 만들어 놨지만 메뉴랑 주문과의 접점이 없기 때문에 포인트 같은 걸 만들어봐야 하나 싶다.
테이블마다 있는 키오스크와 the bear를 보고 주방으로 전송되는 주문서를 상상하며 이거 만들어볼까? 하고 시작한 프로젝트였다.

(그래서?)

 

728x90