개발 slecs

정산 수수료 계층 구조 검증

목차

정산 수수료 계산 로직은 건드릴 때마다 긴장된다. 숫자 하나 틀리면 수익 배분이 통째로 어긋나고, 운영 중에 발견되면 소급 정산까지 얽혀서 처리가 복잡해진다. 이번 작업은 규모가 크지는 않았지만, 계층 구조 검증 로직이 빠져 있었던 걸 잡아낸 거라 나름 의미 있는 수정이었다.

계층 구조가 깨지면 무슨 일이 생기나

수수료 모델은 유통 계층이 깊어질수록 요율이 높아지는 구조다. 최상위가 0.6%, 중간이 0.8%, 최하위가 1.0%를 부담하고, 그 차액 0.2%씩이 상위 계층의 마진이 되는 방식이다.

계층 요율 마진 (상위 계층 수익)
최하위 1.0% -
중간 0.8% 0.2%
최상위 0.6% 0.2%

문제는 이 요율이 설정 데이터로 관리된다는 점이다. 코드에서 하드코딩하는 게 아니라 DB나 설정 파일에서 읽어오기 때문에, 관리자가 값을 잘못 입력하거나 마이그레이션 과정에서 순서가 뒤집히면 마진이 음수가 된다. 최하위 요율이 최상위보다 낮아지면, 상위 계층이 오히려 손해를 보는 구조가 만들어지는 것이다. 정산 배치가 돌기 전까지는 이 오류가 겉으로 드러나지 않아서 더 위험하다.

그래서 계층 요율 순서가 올바른지 체크하는 validation을 추가했다. 계층을 순회하면서 상위 계층의 요율이 하위보다 항상 낮은지 검증하고, 어긋나면 배치 실행 전에 예외를 던지도록 했다. 정산 배치는 주기적으로 실행되니까, 잘못된 설정 상태로 거래가 집계되기 전에 막을 수 있다.

계산 로직과 정밀도 문제

수수료는 비율 수수료와 건당 고정 수수료를 합산한다.

// 비율 수수료 + 건당 고정 수수료 합산
long feeAmount = (long)(txAmount * feeRate) + perTxFee;

단순해 보이지만 여기서 정밀도 문제를 조심해야 한다. txAmount * feeRate가 double 연산이면 부동소수점 오차가 누적된다. 거래 건수가 많아질수록 오차가 쌓이고, 정산 주기 단위로 합산할 때 원 단위 이하가 어떻게 처리되는지 명확하게 정의해야 한다.

이런 경우 선택지는 두 가지다.

  • BigDecimal을 써서 정밀하게 계산한 뒤 마지막에만 long으로 변환
  • 원 단위 이하를 어느 시점에 버릴지(truncate vs round) 정책을 명시하고, 그 정책 자체를 코드에 주석으로 남기기

어느 쪽이든 "어떻게 버릴 것인가"를 팀이 합의하고 코드에 기록해두는 게 중요하다. 나중에 감사나 오차 추적이 들어올 때 이 판단 근거가 없으면 설명하기가 매우 곤란해진다.

정산 UI에서 숫자를 보여주는 방식

정산 화면은 사용자가 보는 핵심 숫자가 세 개다. 총 거래액, 수수료 합계, 실수령액. 이 세 값의 합계가 맞아 떨어지는지 화면에서 바로 확인 가능해야 한다.

표기 원칙으로 정리해두면:

  • 천 단위 구분자와 '원' 단위는 모든 금액 컴포넌트에서 일관 적용
  • 마이너스 금액은 색상으로 분리해서 한눈에 구분
  • 비율 수수료와 건당 수수료를 별도 행으로 표시해서 구성 내역을 투명하게

특히 건당 수수료가 포함된 구조라면, "수수료 합계"만 보여주면 사용자 입장에서 계산이 안 맞는다고 느낄 수 있다. 내역을 쪼개서 보여주는 게 문의 자체를 줄이는 방법이다.


이번 작업 자체는 코드 변경 규모가 크지 않았다. 그런데 validation이 없던 상태였다는 게 돌아보면 아찔하다. 계층 구조가 깨진 상태로 배치가 몇 번 돌았으면 정산 데이터 전체를 들여다봐야 하는 상황이 됐을 것이다. 정산처럼 돈과 직결된 로직은 "설정이 올바를 거라는 가정"을 코드에 두면 안 된다. 입력이 외부에서 오는 순간, 그 값이 유효한지 확인하는 게 로직의 일부다.

댓글 0

첫 댓글 달아줘.