개발 slecs

커미션 대신 계층별 차등 이용수수료 정산 로직으로 전환

목차

커미션 분배 방식을 걷어내고 계층별 차등 이용수수료 정산 로직으로 갈아엎는 작업이었다. 작업 규모 자체는 크지 않았지만, 금전 계산이 엮인 로직이라 설계 결정 하나하나가 꽤 신중해야 했다.

기존 커미션 구조는 단순했다. 거래가 발생하면 일정 비율을 떼서 특정 주체에게 주는 방식. 근데 유통 계층이 복수로 존재하는 구조에서는 이걸 그대로 쓰면 어색한 부분이 생긴다. 계층 간 수익 배분 관계가 명확하지 않고, 계층이 추가되거나 요율이 바뀔 때마다 로직 전체를 손봐야 한다. 이번에 전환한 방식은 각 계층이 자기 요율로 수수료를 납부하고, 상위 계층은 그 차액을 수익으로 가져가는 구조다.

계층 차등 요율 설계

핵심 아이디어는 단순하다. 하위 계층일수록 높은 요율을 부담하고, 상위 계층은 낮은 요율만 납부한다. 그 차이가 상위 계층의 마진이 된다.

계층 요율 마진 구조
최하위 1.0% 전액 부담
중간 0.8% 0.2%p 마진
최상위 0.6% 0.2%p 마진

이 구조의 장점은 계층을 추가해도 요율 테이블만 관리하면 된다는 점이다. 각 계층의 수수료 계산은 독립적이고, 상위 계층이 하위 계층 거래 내역을 직접 참조할 필요가 없다. 정산 단계에서 각자 납부한 수수료를 집계하면 마진이 자동으로 산출된다.

비율 수수료 외에 건당 고정 수수료도 존재해서 계산이 조금 더 복잡해진다. 구현은 이렇게 됐다.

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

(long) 캐스팅으로 소수점 이하를 버리는 게 핵심이다. 수수료를 납부하는 쪽에 유리한 방향으로 내림 처리하는 게 관례고, 이렇게 해두면 나중에 "왜 더 냈냐"는 분쟁이 없다. 받는 쪽은 정확히 계산된 금액을 받고, 끝 전에 반올림 오차로 몇 원이 붕 뜨는 것보다 명확성이 훨씬 중요하다.

Validation: 요율 순서 체크가 빠지면 안 되는 이유

이번에 validation 로직을 추가하면서 한 가지를 못 박았다. 하위 계층 요율이 상위보다 낮으면 마진이 음수가 된다. 시스템이 조용히 통과시키면 상위 계층이 수익은커녕 손실을 쌓게 되는 상황이 생긴다.

요율 설정 저장 시점 또는 배치 실행 직전에 계층 순서대로 요율이 단조 감소하는지 확인하는 로직을 넣었다. 이 체크가 없으면 운영 중에 설정 실수가 바로 데이터 오염으로 이어진다. 금전 관련 로직에서 "설정이 잘못됐으면 조용히 실패"는 없어야 한다. 명시적으로 튕겨내는 게 맞다.

for (int i = 0; i < tiers.size() - 1; i++) {
    if (tiers.get(i).feeRate <= tiers.get(i + 1).feeRate) {
        throw new IllegalArgumentException("상위 계층 요율이 하위보다 낮거나 같을 수 없음");
    }
}

정산 배치와 멱등성

정산 배치는 주기적으로 실행되면서 해당 기간 거래를 집계하고 수수료를 확정한다. 여기서 멱등성이 빠지면 배치가 두 번 돌았을 때 수수료가 두 배로 찍히는 사고가 난다. 재처리 시나리오, 타임아웃 후 재시도, 수동 재실행 등 실제 운영에서 같은 구간을 두 번 처리하는 경우는 생각보다 자주 온다.

멱등성을 보장하는 방법은 몇 가지가 있다.

  • 정산 실행 단위마다 고유 키(기간 + 계층 조합 등)를 두고 DB 유니크 제약으로 중복 삽입을 막는 방법
  • 실행 전 해당 기간 정산 레코드가 이미 존재하는지 확인하고 스킵하는 방법
  • 정산 상태를 PENDING / CONFIRMED / CANCELLED 같은 상태 머신으로 관리하고 CONFIRMED 상태면 재실행 자체를 거부하는 방법

이번 구현에서는 기간 + 계층 조합 키로 중복을 막는 방식을 택했다. 상태 머신은 정정/취소 플로우가 추가될 때 도입하는 게 더 자연스럽다고 판단했다.

실제로 작업하면서 느낀 건, 이런 수수료 계산 로직은 비즈니스 규칙이 명확하게 정의돼 있어도 엣지 케이스가 은근히 많다는 점이다. 소수점 처리, 최솟값 수수료 적용 여부, 면제 조건 등. 요율 테이블 하나 바뀌어도 계산 결과가 달라지니 테스트 케이스를 촘촘하게 짜두는 게 나중 운영 부담을 많이 줄인다. 이번 작업에서 계층별 경계 케이스, 0원 거래, 최솟값 수수료 적용 케이스를 전부 단위 테스트로 커버해뒀다. 작업 규모는 작았지만 이쪽 로직은 신경 쓸 게 많은 영역이다.

댓글 0

첫 댓글 달아줘.