1. 서론: 논리적 오류의 함정
편의점 미션의 막바지, 테스트 코드를 돌리는데 이상한 현상을 발견했습니다. "재고보다 많은 수량을 입력하면 예외가 발생해야 하는데, 자꾸 '정가로 구매하시겠습니까?'라고 물어본다?"
분명 코드는 돌아가는데, 요구사항의 의도와 다르게 동작하고 있었습니다. 원인은 검증(Validation)의 순서였습니다.
2. 본론 1: 문제의 상황 분석
당시 제 `Controller`의 로직은 대략 이랬습니다.
- 사용자 입력 받음.
- `checkStockShortage`(프로모션 재고 부족분 체크) 호출 → 부족하면 물어봄.
- `orderItem`(실제 주문 및 전체 재고 검증) 호출.
[시나리오]
- 재고: 10개 (프로모션 7 + 일반 3)
- 주문: 12개
- 기대 결과: [`ERROR] 재고 수량을 초과...`
- 실제 결과: `checkStockShortage`가 먼저 돌면서 "5개는 프로모션 적용 안 되는데 살래?"라고 물어봄.
즉, "살 수 있는지(Total Stock)"를 확인하기도 전에 "혜택을 받을 수 있는지(Promo Stock)"를 먼저 확인해서 생긴 문제였습니다.
3. 본론 2: 해결책 - 선 검증 후 질문
이를 해결하기 위해 `StoreService`에 전체 재고를 미리 확인하는 메서드(`checkStockAvailability`)를 추가하고, Controller의 로직 순서를 바꿨습니다.
graph TD
A[사용자 입력] --> B{전체 재고 확인 checkAvailability}
B -- 부족함 --> C[예외 발생 & 재입력]
B -- 충분함 --> D{프로모션 혜택 확인 checkBonus/Shortage}
D --> E[사용자에게 Y/N 질문]
E --> F[최종 orderItem 호출]
코드로 보면 다음과 같습니다.
// StoreController.kt
for ((name, quantity) in inputOrders) {
// 1. 가장 먼저 전체 재고부터 확인! (여기서 걸리면 바로 catch로 이동)
storeService.checkStockAvailability(name, quantity)
// 2. 그 다음 프로모션 관련 질문 로직 수행
if (storeService.checkBonusStatus(...)) { ... }
if (storeService.checkStockShortage(...)) { ... }
// 3. 최종 결제
storeService.orderItem(...)
}
4. 본론 3: Service와 Controller의 역할 분담
이 과정에서 MVC 패턴의 역할에 대해 깊게 고민했습니다.
- Service: "계산"과 "검증"만 수행한다. 사용자와 대화하지 않는다. 결과값(부족한 수량 등)만 리턴한다.
- Controller: Service의 결과를 보고 "질문(InputView)"을 할지 결정하고, 흐름을 제어한다.
처음에는 Service 안에서 `InputView`를 호출하고 싶은 유혹이 있었지만, 그랬다면 의존성이 꼬여버렸을 겁니다. Service가 판단 근거 데이터(`shortage count`)를 주면, Controller가 View를 호출하는 구조가 유지보수에 훨씬 유리했습니다.
5. 결론
예외 처리와 비즈니스 로직의 순서는 프로그램의 신뢰도를 결정합니다. 단순히 기능이 동작하는 것을 넘어, "사용자의 흐름에서 무엇이 먼저 체크되어야 하는가?"를 고민해야 합니다. 전체적인 가능 여부(Availability) 검증 -> 세부적인 옵션(Option) 확인 -> 실행(Execution)의 순서를 기억한다면, 훨씬 견고한 프로그램을 만들 수 있을 것입니다.
'Dev Log > Kotlin' 카테고리의 다른 글
| [우테코 | 안드로이드] 코틀린 최종 코딩테스트 준비, 과제 모음 (1) | 2026.01.06 |
|---|---|
| [우테코/코테] "검색 시간 0초" 시험장용 만능 유틸 코드 모음집 (복붙용) (1) | 2026.01.06 |
| [Kotlin] 15줄 제한? 아니 10줄 제한! 메서드를 쪼개는 실전 리팩토링 가이드 (0) | 2026.01.06 |
| [우테코] 🏪편의점 미션: 흩어진 데이터를 하나의 객체로 모으는 기술 (feat. 프로모션/일반 재고 관리) (0) | 2026.01.06 |
| [Kotlin] static은 잊어라, Companion Object(동반 객체)의 모든 것 (1) | 2026.01.04 |