정책에 기본 금액 3,000원을 입력했는데 다른 화면에서는 0원으로 표시된다는 문의가 왔다.
Table of contents
Open Table of contents
증상 — 3,000과 0이 함께 있는 상태
DB 부터 봤다.
SELECT initial_delivery_fee, delivery_fee FROM policies WHERE id = ?;
initial_delivery_fee 3000
delivery_fee 0
initial_delivery_fee 와 delivery_fee 둘 다 값이 있는데 서로 다르다.
각 화면이 무엇을 표시하는지 확인하니 보는 컬럼이 갈려 있었다.
정책 목록 initial_delivery_fee → 3,000
연동 상세 delivery_fee → 0
같은 policies 행인데 화면마다 다른 컬럼을 읽고 있었다.
원인 — 값을 대입하는 계산 게터
생성 화면의 코드를 열었다.
get deliveryFee() {
if (this.policy.feePolicy === 'free') {
this.policy.fee = 0; // ← 대입
} else {
this.policy.fee = this.policy.initialFee; // ← 대입
}
return this.policy.fee;
}
deliveryFee 는 계산해서 돌려주는 자리인데 this.policy.fee 에 값을 쓰고 있었다.
get 은 읽어서 계산한 값을 돌려주는 자리이고 읽는 쪽은 아무것도 안 바뀔 것으로 기대한다. 여기서는 읽는 동작이 다른 속성을 바꾸는 부수효과를 갖고 있었다.
전체 흐름 — 0이 저장된 경위
순서를 늘어놓으니 앞뒤가 맞았다.
사용자가 기본 금액 3,000 입력
사용자가 유형을 '무료'로 선택
↓
게터가 호출되며 fee = 0 대입
↓
저장
↓
initial_delivery_fee = 3000
delivery_fee = 0
입력한 3,000은 initial_delivery_fee 에 남고 delivery_fee 는 0이 된다.
화면을 그리는 동안 deliveryFee 가 불리고 그때 대입이 일어난다. 사용자가 한 조작은 feePolicy 선택 하나인데 그것이 저장될 값까지 바꿔 놓은 것이다.
판단 기준 — 버그가 아니었다
두 컬럼의 뜻이 서로 달랐다.
기본 금액 → 반품·교환 비용 산정 기준
청구액 → 실제로 고객에게 받는 금액
feePolicy 가 free 이면 delivery_fee 가 0인 것이 맞다.
입력한 3,000은 사라진 것이 아니라 initial_delivery_fee 에 다른 용도로 남아 있었다. 그러면 고칠 것은 계산이 아니라 그 사실이 화면에 안 보인다는 쪽이 된다.
대응 — 안내와 표시 추가
문의에는 안내로 답했다.
배송비 종류가 무료이므로 실제 청구 배송비가 0원으로 표시되는 것이 정상입니다.
입력하신 기본 배송비 3,000원은 반품·교환 비용 산정 기준으로만 사용됩니다.
정상입니다만 적으면 납득이 안 되므로 어디에 쓰이는지를 붙였다.
개선 대상도 함께 적었는데 로직이 아니라 표시를 늘리는 쪽이었다.
[표시 전] 청구액 0원
[표시 후] 청구액 0원 (기본 3,000원 · 무료 배송)
같은 데이터인데 initial_delivery_fee 를 함께 보여 주면 오해가 없어진다.
연동 상세 모달에 기본 금액 항목이 안 나오는 것이 혼란의 원인이었다. 화면이 가진 값을 다 보여 주지 않으면 사용자는 없어진 것으로 읽는다.
교훈 — 읽기와 쓰기를 나눈다
게터의 부수효과는 따로 적어 두었다.
get displayFee() {
return this.policy.feePolicy === 'free' ? 0 : this.policy.initialFee;
}
// 저장 직전에 별도로
prepareForSave() {
this.policy.fee = this.displayFee;
}
displayFee 는 계산만 하고 대입은 prepareForSave 에서 한 번만 한다.
화면에 보여 줄 값과 저장할 값을 한 곳에서 처리하니 편했을 것이다. 대신 값을 확인했더니 값이 바뀌는 상태가 되어 디버깅할 때 짚기가 어려웠다.
조사 방법 자체는 단순했다. 저장 쪽과 표시 쪽을 각각 따라간 것이고 표시 쪽만 봤으면 왜 0인지에서 막혔을 것이다.
정리
- 비슷한 이름의 두 컬럼이 다른 뜻일 수 있다
- 화면마다 다른 컬럼을 표시할 수 있다
- 계산 게터가 상태를 바꾸는 부수효과를 가질 수 있다
- 읽는 쪽은 아무것도 안 바뀔 것으로 기대한다
- 그래서 입력한 값이 다른 컬럼에만 남는다
- 설계상 의도면 버그가 아니라 표시 문제다
- 정상이라는 답에 어디에 쓰이는지를 붙인다
- 개선이 로직이 아니라 표시 추가일 수 있다
- 읽기와 쓰기를 나누고 게터는 읽기만 한다
- 저장 쪽과 표시 쪽을 각각 따라간다