악명이 높다던 7기 프리코스 편의점 문제를 풀어보았다. 문제를 읽다가 약간 어지러워져 먼저 테스트 코드부터 읽어봤음에도 불구하고 중간에 포기할까 고민까지 했고, 결국에는 10시간이 넘는 시간동안 이 문제만 풀게 되었다.

합쳐서 10시간 5분ㄷㄷ
1차 설계

처음 문제를 보고 설계한 내용은 그렇게 복잡하지 않았다.
- products, promotion 받아옴 -> 날짜 비교 -> 저장
- 보유한 상품 출력 -> 구매할 상품, 수량 입력
- 입력된 내용 형식 오류 검증
- 재고 수량 확인 -> 프로모션 재고 모자라면 계속 진행 여부 확인 후 다음
- 프로모션 가능한 제품이 있으면 증졍 여부 확인 후 다음
- 수량이 없거나 재고가 없는 제품이면 오류 처리
- 모든 단계 완료되면 멤버십 여부 확인
- 멤버십 처리 후 결과 출력
이 순서에 따라서 책임 분리를 하였고, 정리하면 다음과 같다.
View
- InputView(사용자 입력 처리)
- OutputView(출력 처리)
Controller
- Controller(메인 컨트롤러, 입출력 처리 및 종합적인 처리 담당)
Model
- Store(상품 재고)
- Customer(사용자 정보)
- Worker(사용자 내용과 상품 재고 상호작용)
- Calculator(결과 도출)
- Validator(검증)
Controller에서 메인을 담당하게 되며, Worker가 Store와 Worker 사이를 상호작용하는 방식으로 설계하였다. 하지만 여기서 막히게 된 문제가 있었으니 미션 내용을 확인해보면
미션 내용
- 재고가 있는지 확인
- 재고가 있으면 프로모션 적용 가능한지 확인
- 프로모션 적용 가능한데 증정품 있으면 증정품 받을지 확인
- 프로모션 적용 가능한데 불가능한 부분이 일부 있으면 구매할지 확인
- 프로모션 적용 불가능만 있으면 그냥 구매처리
이렇게 나뉜다. 단순히 재고 처리에서만 이 내용이 나오게 되는데, 여기서 하나하나 로직이 굉장히 복잡한데
case 1) 증정품 있는 구매
- 프로모션 적용되는 재고 가져옴
- 그 재고가 구매하고자하는 재고보다 크면
- 해당 프로모션에 대한 정보 가져옴
- 해당 프로모션에 대한 증정품이 추가로 있다면
- 사용자 input으로 증정 여부 입력
- 증정 여부 yes면 증정하고, 구매 개수 추가
- 증정 여부 no하면 증정 안함
case 2) 프로모션 일반 구매
- 프로모션 적용되는 재고 가져옴
- 그 재고가 구매하고자하는 재고보다 크면
- 해당 프로모션에 대한 정보 가져옴
- 해당 프로모션에 대해 정확한 개수(2+1이면 3배수)를 구매하고자 했다면
- 구매 처리
case 3) 프로모션 재고 부족 구매
- 프로모션 적용되는 재고 가져옴
- 그 재고가 구매하고자 하는 재고보다 작으면
- 프로모션 재고 부족한데 구매할건지 사용자 입력 받음
- Yes면 프로모션 재고 소진 후 일반 재고 소진
- No면 구매x
case 4) 프로모션 적용 안된 구매
- 프로모션 적용되는 재고 가져옴
- 프로모션 적용 재고 없으면
- 구매 처리
case는 4개로 정리되었지만 내부에 사용자 입력 후, 나뉘는 case까지 합치면 총 6개가 된다. 그리고 view 계층 즉, 사용자 입력 처리를 controller에서 해야하기 때문에 이 나뉘는 갈래들을 worker에서 해결하고자 했던 나에게는 적절하지 않은 방식이었다.
2차 설계

일단 구매 품목마다 purchaseManager를 생성하는 방식으로 변경하였다. 그리고 return하는 내용 안에 이 구매 정보가 어디까지 진행되었는지 저장하도록 하였다.
근데 또 문제가 발생했는데, purchaseManager를 worker에서 생성하도록 되어있고, 그러면 input을 controller에서 받아서 처리해야되는데, input을 받은 다음의 로직 진행 여부를 manager에서 관리할 수가 없다는 것이었다.
3차 설계

일단 케이스에 따라 나뉘는 구매 진행 단계를 정리하였다. 그리고 return하는 내용은 다음과 같이 정리하였다.
{ 진행된 단계,
상품 이름,
구매 개수,
증정 개수 }
이렇게 정리하였다. 그리고 input을 받아서 -> 진행된 단계를 확인하고 -> 해당되는 단계의 로직을 진행하도록하는 방식을 사용하였다.
상품 하나 구매 진행 단계
구매 상품 하나에 대한 진행 단계를 정리하면 다음과 같다.
- 구매 상품 하나에 대한 정보 입력 받음
- 해당 정보 중 상품 이름, 개수를 바탕으로 purchaseManager 생성
- input 없는 상태에서 단계 확인 로직으로 이동
- input이 없으면 처음 단계 진행
- 해당 상품에 대한 information 받음
- 재고 있는지 확인(프로모션, 프로모션x 재고 합쳐서)
- promotion 재고가 구매 재고보다 작거나 같은지 확인
7-1. 작거나 같고, 증정품 있으면 command 'check-free'로 설정 후 return
7-2. 작거나 같고, 정확한 개수 구매 가능하면 command false로 설정, 결과 return
7-3. 크고, 프로모 재고 있으면 command 'check-promo'로 설정 후 return
7-4. 크고, 프로모 재고 없으면 command false로 설정, 결과 return
- 7-2와 7-4는 완료 처리
- result를 바탕으로 command에 따라 입력 받고, worker의 reprocess로 보냄
- purchaseManager의 단계 확인으로 보냄
- command와 input결과에 따라 각자에 맞는 메서드로 이동
11-1. 'check-free'에 true면 증정품 받음, 결과 return
11-2. 'check-free'에 false면 증정품 안 받음, 결과 return
11-3. 'check-promo'에 true면 프로모 적용 안되는 부분도 구매함, 결과 return
11-4. 'check-promo'에 false면 구매 안함, 결과 return
- 결과를 바탕으로 설정된 재고 구매처리
- 결과를 바탕으로 설정된 고객 구매 정보 저장
이게 고객 1명의 구매 요청 내역 중 구매 상품 '하나'에 대한 내용이다.
종합 control
- 상품 정보, 프로모 정보 가져와서 store 생성(재고 관리)
- while문으로 아래의 내용 감쌈
- 재고 출력
- 구매 요청 내역 입력 받음
- 각 구매 요청 내역에 대해 구매 처리
- 멤버십 여부 받음
- 해당 고객에 대한 영수증 출력
- 추가 구매 여부 받음
- 추가 구매 X면 종료 Y면 계속 진행
test 디버깅
디버깅의 경우 나머지는 다 잘 진행되었지만, 상품 재고 저장 부분에서 어려움이 있었다. 만약 프로모 재고만 있고, 일반 재고는 없는(정보 자체가 없는) 상품이 있을 경우에는 자체적으로 일반 재고 항목을 만들어서 재고없음 이라고 띄워야 했다.
예를들어 사이다 2+1이 10개 있는데 사이다 프로모 없는 상품은 아예 정보가 없을 때, 자체적으로 사이다 프로모 없음을 재고없음 처리해야한다.
그래서 진행한 방식은
- 입력 받을 때, 상품 이름 목록을 받는다.
- 해당 상품 이름들 마다 promo적용 안되는 재고 정보를 받음
- promo 적용 안되는 재고가 없다면(근데 이름은 있으니까)
- promo 적용x, 재고x인 내용 추가
- 추가된 내용으로 store 구성
이렇게 진행하였고, 테스트 최종 통과하였다.
