입점사가 배송비를 입력했는데 반영이 안 됐다는 분쟁이 있었고 우리 shipping_fees 에는 그 값이 없었다. 둘 중 하나가 틀린 상황이라 데이터로 판정할 수 있는지를 봤다.
Table of contents
Open Table of contents
배경 — 둘 중 하나가 틀린 분쟁
input_histories 에 이력이 있으면 우리 과실이고 없으면 상대 과실이라는 판정 기준이 제시됐다. 그럴듯하지만 그 기준이 성립하려면 전제가 하나 필요했다.
저장이 전송보다 먼저 일어나야 흔적의 유무가 입력 여부를 뜻하게 된다. 그 전제를 확인하지 않고 기준만 쓰면 판정이 근거 없이 나온다.
검증 — 저장과 전송의 순서
두 가지 방법이 있었는데 제시된 기준을 그대로 적용하는 것과 ShippingFeeService::update() 를 읽어 순서를 확인하는 것이다. 뒤쪽을 골랐다. 이유는 그 기준이 성립하려면 저장이 전송보다 먼저라는 전제가 필요해서다. 대신 회신이 하루 늦어지는 것을 감수했다. 코드를 읽으니 값을 먼저 저장하고 input_histories 를 함께 남긴 뒤에 dispatch() 로 외부에 전송하는 순서였다. 그러면 전송이 실패해도 데이터베이스에는 값이 남는다.
반대 순서였다면 전송 실패 시 저장도 안 되므로 입력했는데 유실된 경우가 실제로 가능해진다. 같은 판정 기준이 저장 순서에 따라 성립하기도 하고 무너지기도 했다.
조치 — 흔적 유무가 근거가 되는 조건
순서를 확인하고 나서 input_histories 와 shipping_fees 를 조회하니 둘 다 없었다. 전송 실패로 유실된 경우라면 값이 남아 있어야 하므로 입력이 이뤄지지 않은 것으로 판정됐다.
회신에는 결과만 쓰지 않고 저장이 dispatch() 보다 먼저이며 이력도 함께 남는다는 근거를 적었다. 왜 그렇게 판정했는지를 함께 적으니 추가 문의가 오지 않았다.
주의 — 다른 경로로 저장될 때
이 기준이 흔들리는 경우도 함께 확인했는데 UPDATE 직접 질의로 값을 넣으면 이력이 안 남는다. 그러면 shipping_fees 값은 있고 이력만 없는 상태가 만들어질 수 있다.
이 항목은 ShippingFeeController 화면에서만 입력되는 값이라 해당하지 않았다. 다만 그 확인을 안 했으면 기준이 성립하지 않는 경로가 열려 있는지도 모른 채 판정했을 것이다.
원인 — 상태에 따라 갈리는 후속
같은 조사에서 settlement_adjustments 를 지울 때 재계산이 필요한지도 봤는데 상태에 따라 갈렸다. settlements 가 이미 생성돼 있으면 삭제만으로는 합계가 안 바뀌므로 settlement:recalculate 가 필요했다.
앞선 유사 건은 settlements 미생성 상태라 삭제만으로 끝났는데 이번 건은 생성돼 있었다. 같은 조치라도 상태에 따라 후속이 다르므로 앞선 건과 똑같이 처리했으면 합계가 안 맞았을 것이다.
정리
- 분쟁 판정에 데이터를 쓰려면 저장 순서를 먼저 확인한다
- 저장이 전송보다 먼저면 흔적 유무가 판정 근거가 된다
- 반대 순서면 같은 기준이 무너진다
- 그럴듯한 기준도 전제를 코드로 검증하고 쓴다
- 다른 경로로 저장되면 이력이 빠지는지도 확인한다
- 회신에 판정 근거를 함께 적으면 추가 문의가 없다
- 같은 조치도 상태에 따라 후속이 다르다
- 앞선 건과 똑같이 처리하기 전에 상태를 확인한다