개발 slecs

수수료 계층별 요율 검증과 정산 계산 로직 개선

목차

수수료 계층 구조는 언뜻 단순해 보이지만, 실제로 건드리면 생각보다 신경 쓸 게 많다. 이번에 계층별 요율 검증과 정산 계산 로직을 정리했는데, 작업 규모는 크지 않았어도 짚어둘 게 있어서 기록해둔다.

계층 차등 수수료 모델의 기본 구조

이 시스템의 수수료는 유통 계층마다 요율이 다르게 설정되는 차등 모델이다. 핵심 개념은 간단하다. 하위 계층이 상위 계층보다 높은 요율을 부담하고, 그 차액이 상위 계층의 수익으로 남는 구조다.

계층 요율 비고
최하위 1.0% 최대 부담
중간 0.8% 차액 0.2% 수익
최상위 0.6% 차액 0.2% 수익

이 구조가 성립하려면 반드시 하위 요율 > 상위 요율 이어야 한다. 반대가 되면 중간 계층이 마진이 아닌 손실을 보게 되고, 계산 결과가 음수로 나오는 버그로 이어진다. 그 조건을 코드에서 명시적으로 검증하는 게 이번 작업의 핵심이었다.

요율 외에 건당 고정 수수료도 별도로 존재한다. 비율 수수료와 건당 고정 수수료를 합산하는 방식인데, 거래 건수가 많아질수록 고정 수수료 부분이 전체에서 차지하는 비중이 커진다. 계산 패턴은 이렇다.

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

// 계층별 요율 순서 검증 - 하위 요율이 상위보다 낮으면 마진이 음수
for (int i = 0; i < tiers.size() - 1; i++) {
    FeePolicy upper = tiers.get(i);
    FeePolicy lower = tiers.get(i + 1);
    if (lower.getRate() <= upper.getRate()) {
        throw new InvalidFeePolicyException(
            "하위 계층 요율이 상위 계층보다 낮거나 같습니다: "
            + lower.getTierCode() + " vs " + upper.getTierCode()
        );
    }
}

캐스팅 순서가 조금 민감하다. (long)(txAmount * feeRate) 에서 txAmountlong이면 feeRate(double)과 곱할 때 암묵적으로 double 연산이 되고, 그 결과를 long으로 자르는 방식이다. 이 흐름이 의도한 내림 처리와 맞아야 한다. 반올림인지 내림인지를 명확히 해두지 않으면 나중에 검증 때 숫자가 안 맞는 상황이 나온다.

정산 배치에서 챙겨야 할 것들

정산 배치는 특정 기간의 거래 내역을 집계해서 수수료를 확정하는 작업이다. 이 배치가 안정적으로 동작하려면 몇 가지 조건을 지켜야 한다.

멱등성. 같은 기간을 두 번 돌려도 결과가 동일해야 한다. 배치가 중간에 실패했을 때 재실행하는 상황이 반드시 오는데, 이미 처리된 건을 중복 처리하거나 합산이 두 번 되는 식의 사고가 나면 정산 전체가 틀어진다. 실행 여부를 상태 컬럼으로 관리하든, 정산 기간 기준으로 유니크 제약을 걸든 방법은 여러 가지다. 중요한 건 재실행해도 안전하다는 보장이 코드 수준에서 있어야 한다는 점이다.

원 단위 반올림 정책. 소수점 아래 결과가 나올 때 어느 방향으로 처리하느냐는 사소해 보이지만 분쟁 소지가 있다. 일반적으로 수수료를 내는 쪽에게 유리한 방향, 즉 내림으로 처리하는 게 통상적이다. 수수료가 1원이라도 더 나오면 사용자 입장에서 억울하다고 느낄 수 있다. 정책을 정했으면 코드 주석이든 별도 문서든 명시해두는 게 나중에 모두가 편하다.

계층별 검증을 저장 시점에. 요율 설정을 DB에 저장할 때만 검증하면 운영 중에 누군가 직접 값을 바꾸는 케이스를 못 잡는다. 배치 시작 시점에도 요율 순서가 맞는지 다시 한번 확인하는 게 낫다. 약간의 중복이지만 정산 오류는 나고 나서 수습하는 게 훨씬 힘들다.

정산 화면에서 숫자 표기

정산 UI에서 사용자가 보는 건 결국 숫자다. 총 거래액, 수수료, 실수령액 세 가지가 명확히 구분되면 된다. 구현할 때 통일해두면 좋은 것들을 정리하면:

  • 천 단위 구분자와 '원' 단위를 모든 금액 필드에 일관되게 적용
  • 마이너스 금액은 색상이나 괄호 표기로 직관적으로 구분 (빨간색 또는 (1,200원) 형식)
  • 수수료 요율은 소수점 둘째 자리까지, 금액은 원 단위 정수로 표시
  • 계층별 마진이 음수인 경우 UI에서도 경고 상태로 표시

화면에서 마이너스 마진이 보이면 설정 오류가 즉시 눈에 띄기 때문에, 운영 중 오류 감지 측면에서도 유용하다. 계층 요율 검증을 백엔드에서만 하지 않고 정산 화면에서도 시각적으로 드러내면 담당자가 확인하기 훨씬 쉽다.


이번 작업에서 다시 확인한 건, 금전 계산 로직은 "작은 수정"이라도 정책이 코드 안에 명확히 박혀 있어야 한다는 것이다. 반올림 방향 하나, 요율 순서 조건 하나가 암묵적 관행으로만 남아 있으면 언젠가 다른 사람이 건드렸을 때 바로 터진다. validation을 추가한 것도 그 맥락이고, 코드 자체가 정책을 설명하게 만드는 게 목표였다.

댓글 0

첫 댓글 달아줘.