Skip to content
isdnetworks
Go back

가격 계산이 두 곳에 있었다

화면에 보인 값과 실제로 결제된 값이 다르다는 문의가 들어왔다.

Table of contents

Open Table of contents

계산이 두 곳에 있었다

grep -rn 으로 가격을 계산하는 자리를 찾으니 화면에 보여 줄 때와 결제할 때 두 곳이었다. 두 코드를 나란히 놓아 봤다.

// 화면
$price = $base - $discount;

// 결제
$price = $base - $discount;
$price = $price + $shipping;
if ($qty >= 5) { $price = $price * 0.95; }

결제 쪽에만 $shipping 과 수량 할인이 더 있었다. 물어보니 결제 쪽이 맞았다. 처음에는 같은 계산이었고 조건이 추가되면서 갈렸다. 고치는 사람이 두 곳인 것을 몰랐거나 잊었다.

한 곳으로 뺐다

고치는 방향을 먼저 정해야 했다. 화면 코드를 결제 코드에 맞춰 베끼면 지금은 같아지지만 다음에 조건이 추가될 때 또 갈린다. 계산을 PriceCalculator 로 빼고 두 곳이 같은 것을 부르게 했다.

class PriceCalculator {
    public function calc($item, $qty) { ... }
}

이러면 다를 수가 없다. 두 코드가 있는 한 갈릴 수 있고 한 코드면 갈릴 자리가 없다.

합치면서 곱셈도 손봤다. $price * 0.95 는 실수 연산이라 결과가 정수 원 단위로 안 떨어진다. PHP 에서 0.1 + 0.20.3 과 같지 않고 1234.05 를 열 번 더한 값도 12340.5== 로 비교하면 거짓이다. 원 단위로 round 한 뒤 정수로 다루고 표에는 DECIMAL 로 담게 했다.

시점이 다른 것은 다른 문제였다

코드를 합치고도 걸리는 것이 남았다. 화면에서 본 시점과 결제하는 시점이 다르다. 장바구니에 담고 며칠 뒤에 결제하면 그 사이에 가격이 바뀔 수 있다.

이건 코드가 갈린 것과 성격이 달랐다. 같은 시점에 다른 값이 나오면 버그지만 다른 시점에 다른 값이 나오는 것은 정상일 수도 있다. 어느 값으로 결제할지는 코드가 아니라 정책이 정할 일이었다.

물어보니 현재 가격으로 하되 바뀌었으면 알리기로 했다. price_at_add 를 담아 두고 결제 화면에서 지금 가격과 비교해 다르면 표시한다. 모르고 결제하는 것보다는 낫다는 판단이었다.

참조와 복사

계산할 수 있는 값을 굳이 저장하는 이유는 비교 때문이다. 저장하지 않으면 지금 가격만 알 수 있어 그때와 대조할 수가 없다.

cart
  item_id
  qty
  price_at_add

결제할 때도 그때 가격과 적용된 할인을 order_item 에 남겼다. 상품 표를 JOIN 으로 참조만 하면 나중에 가격이 바뀔 때 옛 주문의 금액까지 바뀐다. 그때 값을 복사해 두면 주문 기록은 그대로 남는다.

정리하면 진행 중인 것은 참조하고 확정된 것은 복사한다. 정산이 그래야 맞는다는 이유가 컸다. 정산은 그때 팔린 값으로 계산해야 하는데 지금 가격을 쓰면 과거 매출이 바뀐다.

정리


Share this post on:

Previous Post
전역 값이 계정마다 저장돼 있었다
Next Post
권한이 없어서 버튼이 안 보였다