파트너 포털 계층별 충전 수수료 정산 로직 개선
목차
파트너 포털 수수료 정산 로직을 손보기 시작한 건 생각보다 여러 곳이 얽혀 있어서 범위가 좀 커졌다. 처음엔 금액 표기 포맷 통일이 메인이었는데, 건드리다 보니 메뉴 권한 제어, SQL 스키마, 충전 상태 배지까지 한꺼번에 정리하게 됐다.
파트너 포털은 유통 계층별로 접근 가능한 메뉴와 기능이 다르게 설정돼 있다. 그동안 activeFeatures 조건으로 메뉴 표시 여부를 제어해 왔는데, 이번에 기능이 추가되면서 새로운 feature 코드를 조건에 넣어야 했다. 그 김에 SQL 쪽에도 portal_features 열을 추가해서 파트너 레벨마다 허용 기능 목록을 DB에서 직접 관리할 수 있게 바꿨다. 기존엔 코드 레벨에서 레벨을 분기해서 하드코딩에 가까운 방식으로 처리하고 있었는데, 이걸 DB로 올리면 레벨별 메뉴 권한을 배포 없이 조정할 수 있어서 운영 편의가 꽤 좋아진다.
UI 쪽에서는 브랜드명 변경이 있었다. 'SLECS'라는 내부 명칭을 'WeaverKit'으로 전반적으로 교체했는데, JSP 파일 곳곳에 텍스트와 타이틀이 흩어져 있어서 빠뜨리지 않게 꼼꼼히 확인했다. 이런 명칭 교체는 놓친 곳 하나가 UX 이질감으로 바로 티가 나기 때문에 파일 단위로 전체 검색해서 처리했다.
금액 표기는 JSP의 fmt:formatNumber 패턴을 #,##0으로 통일하고, 통화 기호 대신 "원" 텍스트로 표기하는 방식으로 맞췄다. JSTL 포맷 태그는 로케일 설정에 따라 통화 기호가 들쭉날쭉 나오는 경우가 있어서 명시적인 패턴 지정이 낫다. 숫자 포맷만 태그로 처리하고, "원" 텍스트는 뷰에서 붙이는 식으로 하면 나중에 표기 방식이 바뀌어도 수정 포인트가 명확하다.
충전 상태 표시 로직은 chargeStatusLabelMap을 도입해서 상태 코드별 텍스트와 배지 색상을 하나로 묶어서 관리하게 리팩토링했다. 기존엔 switch-case 혹은 중첩 if로 상태마다 텍스트랑 색상을 따로 분기하고 있었는데, 이걸 맵으로 정의해두면 상태가 추가되거나 레이블이 바뀌어도 한 곳만 고치면 된다.
수수료 계층 구조와 계산 로직
이번 작업에서 핵심이 된 건 수수료 계산 쿼리 개선이다. 수수료 구조가 계층 차등 모델이라 좀 복잡하다.
하위 계층이 더 높은 요율을 부담하고, 상위 계층은 그 차액을 수익으로 가져가는 방식이다. 예를 들면 아래처럼 구성된다.
| 계층 | 요율 | 비고 |
|---|---|---|
| 최하위 | 1.0% | 최대 부담 |
| 중간 | 0.8% | 차액 0.2% 수익 |
| 최상위 | 0.6% | 차액 0.2% 수익 |
여기에 건당 고정 수수료가 붙어서, 실제 계산은 비율 수수료 + 고정 수수료의 합산이다.
// 계층별 수수료 계산 패턴
long feeAmount = (long)(txAmount * feeRate) + perTxFee;
비율 계산에서 소수점 처리를 어떻게 할지가 항상 민감한 부분인데, (long) 캐스팅으로 절사하는 방식을 쓰면 소수점 이하는 항상 버려진다. 반올림이냐 절사냐는 정책 결정이라 임의로 바꾸면 안 되고, 현재 정책을 유지하면서 쿼리 로직만 정비했다. 결제 수단에 따라 수수료 조건이 다르게 적용되는 케이스가 있어서, 쿼리에서 결제 수단별 분기 처리를 추가했고 내부 테이블 조회 로직도 보완했다.
정산 배치는 일정 주기로 돌면서 해당 기간 거래를 집계하고 수수료를 확정하는 구조다. 배치가 중간에 실패했을 때 멱등성 있게 재실행이 가능한지가 중요한데, 이 부분은 기존 설계를 건드리지 않고 쿼리 레벨 수정에만 집중했다.
계층 요율 역전 방어 로직
추가한 validation은 단순하지만 실수를 막는 데 유효하다. 계층 요율을 설정할 때 하위 계층 요율이 상위보다 낮아지면 마진이 음수가 된다. 그러면 정산 금액이 뒤집히는 버그가 발생하고, 이런 건 배치가 돌고 나서야 발견되는 경우가 많아서 영향 범위가 커진다.
요율 설정 시점에 계층 순서대로 요율이 내려가는지 체크하는 로직을 넣었다. 조건은 단순히
하위계층요율 > 상위계층요율
을 각 인접 계층 쌍에 대해 순서대로 검증하는 것이다. 틀리면 저장 전에 튕겨내도록 해뒀다. 이런 validation은 로직 자체보다 '이 규칙이 왜 필요한지'가 코드에 드러나야 나중에 건드리는 사람이 실수 안 하는데, 주석으로 마진 음수 케이스를 설명해뒀다.
전체적으로 보면 기능 하나 추가에 딸려오는 변경이 여러 레이어에 걸쳐 있었다. JSP 뷰, 네비게이션 조건, DB 스키마, 정산 쿼리가 각각 독립적인 것 같아도 결국 같은 비즈니스 룰 위에서 움직이기 때문에 한 곳 바꾸면 나머지도 맞춰야 하는 식이다. 수수료 계층 구조처럼 규칙이 조금만 복잡해져도 검증 레이어를 코드에 명시해두는 게 나중 유지보수 비용을 줄인다는 걸 다시 확인했다.
댓글 0
첫 댓글 달아줘.