파트너 수수료 수정 즉시 미반영 버그 수정
목차
수수료 계산 로직을 건드리는 건 항상 긴장된다. 금전 도메인이라 버그 하나가 조용히 누적되다가 정산 시점에 터지는 패턴이 많기 때문이다. 이번 작업도 그 계열이었다.
파트너 수수료 요율을 수정한 뒤 즉시 반영이 안 되는 현상이 있었다. 배치 다음 실행 주기까지 이전 요율이 그대로 쓰이는 상황이었는데, 수수료 요율 조회 흐름을 단계별로 따라가면서 어느 지점에서 변경된 값이 즉시 반영되지 않고 머물러 있는지 확인하고 수정했다. 정산 배치는 주기적으로 실행되는 구조라, 이 갭이 있으면 요율 변경의 효력이 늦게 적용되어 실제 정산 금액과 기대 금액 사이에 차이가 생긴다. 배치 주기가 길수록, 그 사이 처리된 거래 건수가 많을수록 차이가 불어난다.
수수료 계층 구조와 계산
파트너 정산 모델은 유통 계층별로 요율이 다르게 설정되는 차등 구조다. 하위 계층이 더 높은 요율을 부담하고, 인접 계층 간 요율 차이가 상위 계층의 수익으로 쌓이는 방식이다.
| 계층 | 요율 | 비고 |
|---|---|---|
| 최하위 | 1.0% | 최대 부담 |
| 중간 | 0.8% | 차액 0.2% 수익 |
| 최상위 | 0.6% | 차액 0.2% 수익 |
단순해 보이지만 실제 계산에는 비율 수수료 외에 건당 고정 수수료가 더해진다. 한 거래의 최종 수수료는 두 항목을 합산한 값이다.
// 비율 수수료 + 건당 고정 수수료 합산
long feeAmount = (long)(txAmount * feeRate) + perTxFee;
(long) 캐스팅으로 소수점 이하를 버리는 건 의도된 정책이다. 수수료를 내는 쪽에 유리하게, 즉 내림으로 처리하겠다는 선택이다. 반올림이나 올림을 쓰면 거래 한 건에서는 미미하지만 건수가 쌓이면 파트너 입장에서 납득하기 어려운 누적 차이가 생기고 분쟁 소지가 된다. 정산 도메인에서 내림이 관행처럼 쓰이는 이유가 여기에 있다. 어느 방향으로 버릴지는 선택이지만, 선택했으면 시스템 전체에서 일관되어야 한다. 계층마다 반올림 방식이 다르면 미묘한 불일치가 쌓인다.
validation 추가와 조기 차단
즉시 반영 문제를 수정하면서 한 가지를 더 손댔다. 계층별 요율 순서를 검증하는 로직이 없었다는 점이다.
하위 계층 요율이 상위보다 낮게 설정되면 마진이 음수가 된다. 상위 계층이 오히려 돈을 잃는 역전 구조가 되는 것이다. 운영 중에 누군가 실수로 잘못 입력해도 배치가 실행되기 전까지는 눈에 안 띈다. 발견이 늦을수록 소급 수정 범위가 넓어지고 정산 재처리 공수가 커진다.
저장 시점에 막는 게 가장 비용이 낮다. 배치 실행 후 결과 검증 단에서 잡거나, 이미 발생한 차이를 수동으로 추적하는 것과는 차원이 다르다.
// 계층 요율 순서 검증 - 하위 계층이 반드시 상위보다 높은 요율이어야 함
for (int i = 0; i < tiers.size() - 1; i++) {
if (tiers.get(i).feeRate >= tiers.get(i + 1).feeRate) {
throw new IllegalArgumentException(
"하위 계층 요율은 상위보다 높아야 합니다: index=" + i
);
}
}
이런 validation은 보통 "당연히 잘못 입력하겠어?" 싶어서 생략하게 된다. 하지만 수수료 요율은 자주 바뀌지 않아서, 담당자가 바뀌거나 시간이 지나면 계층 구조 자체를 모르는 채로 값만 수정하는 경우가 생긴다. 도메인 제약이 코드 안에 있지 않으면 언제든 깨진다. 특히 내부 구조에 대한 구두 설명은 사라지지만 코드는 남는다.
정산 배치와 멱등성
정산 배치를 다룰 때마다 확인하는 게 멱등성이다. 같은 기간을 두 번 실행해도 결과가 달라지면 안 된다. 시스템 장애 복구나 수동 재실행 상황에서 중복 정산이 생기면 파트너와의 분쟁으로 이어진다. 그리고 배치는 언제나 재실행 가능성을 열어두고 설계해야 한다.
멱등성을 확보하는 방법으로 자주 쓰는 패턴은 대략 이렇다.
- 정산 기간별 처리 상태를 별도로 관리하고, 이미 완료된 기간이면 스킵하거나 기존 결과를 그대로 반환한다.
- 결과를 저장하기 전에 동일 키 존재 여부를 체크하거나, 충돌 시 무시하는 쿼리 옵션을 사용한다.
- 배치 실행 자체에 락을 걸어 동시에 같은 기간을 처리하는 인스턴스가 없게 한다.
세 가지를 다 쓸 필요는 없지만, 적어도 하나는 반드시 있어야 한다. 이번 작업에서도 요율 반영 흐름을 수정하면서 배치 멱등성 처리 경로도 같이 확인했다. 수수료 요율이 중간에 바뀌는 경우를 배치가 어떻게 처리하는지, 요율 변경 시점 기준으로 거래를 어떻게 구분하는지까지 점검했다.
작업 규모 자체는 크지 않았다. 하지만 요율 즉시 반영이 안 되는 구멍과 계층 역전을 막는 validation 부재는, 배치 실행 후에야 인지했다면 수습이 훨씬 복잡해졌을 것이다. 금전 도메인에서 작은 수정이 오히려 더 신경 쓰이는 이유가 여기 있다. 계산식은 단 한 줄이어도 그 주변 흐름 전체가 방어되어 있지 않으면, 어느 지점에서든 조용히 구멍이 생긴다.
댓글 0
첫 댓글 달아줘.