외부 채널 수수료 정산 화면 버그 수정
목차
외부 채널 수수료 정산 화면에서 소소하게 쌓여있던 버그 몇 가지를 한 번에 정리했다. 작업 자체는 하루 안에 끝났지만, 수수료 관련 화면이라 신중하게 짚고 넘어갈 부분이 있어서 기록해둔다.
외부 채널은 결제 플랫폼을 통해 서비스를 제공하는 사업자 단위다. 채널별로 수수료 설정, 메뉴 권한, 하위 채널 관리가 독립적으로 동작한다. 단순히 "채널 하나 = 파트너사 하나"가 아니라, 계층 구조로 묶일 수 있고 각 레벨마다 요율을 다르게 가져갈 수 있는 구조라 설정 항목 자체가 꽤 많다. 그러다 보니 관리자 화면이 기능별로 분산되어 있고, 그 접합부에서 버그가 생기기 쉽다.
이번에 잡은 버그도 딱 그 지점에 몰려 있었다.
수정한 것들
1. 포털 메뉴 노출 조건 오류
파트너 Discord Webhook 필드가 슈퍼관리자 권한에서만 표시되어야 하는데, 조건 분기가 잘못 걸려 있어서 권한 없는 계정에서도 노출되고 있었다. 이런 종류의 버그는 화면 레벨에서만 숨기는 걸로 끝내면 안 된다. API 파라미터 바인딩도 같이 확인해서 서버 사이드에서도 권한 체크가 제대로 동작하는지 같이 봤다. 화면에서 안 보이더라도 직접 요청을 날리면 처리되는 구조면 의미가 없으니까.
2. 수수료 수정 후 즉시 반영 안 되는 문제
수수료를 바꾸고 저장하면 저장은 됐는데 화면 목록은 이전 값 그대로 남아 있었다. 이 패턴은 AJAX 기반 폼에서 자주 보이는 상태 동기화 문제다. 저장 성공 응답을 받은 뒤 목록을 다시 조회하지 않고 로컬 상태만 업데이트하거나, 아예 갱신 자체를 안 하는 경우.
수정은 단순했다. AJAX 응답 콜백에서 목록 재조회 함수를 호출하도록 추가했다.
$.ajax({
url: '/api/channel/fee/update',
method: 'POST',
data: params,
success: function(res) {
if (res.success) {
showToast('저장되었습니다.');
loadFeeList(); // 목록 재조회
}
}
});
단순한 수정이지만 이게 없으면 사용자는 저장이 됐는지 안 됐는지 화면만 보고 판단할 수가 없다. 수수료 같은 금전적 항목은 특히 "저장됐는데 화면이 안 바뀌었다"는 경험이 신뢰 저하로 직결된다.
3. th/td 컬럼 수 불일치
테이블 헤더와 바디의 컬럼 수가 안 맞는 케이스도 있었다. 이걸 브라우저가 알아서 메꿔주기 때문에 화면상으로는 멀쩡해 보여서 모르고 넘어가기 쉽다. 실제로 발견한 것도 개발자 도구 DOM 탭에서 직접 확인하다가 눈에 띈 거였다.
해결은 컬럼 수를 맞추거나, 특정 셀에 colspan을 명시해서 의도를 코드에 드러내는 방식으로 처리했다. 브라우저가 보정해주는 동작에 의존하는 건 나중에 레이아웃 조정할 때 예상치 못한 깨짐으로 돌아온다.
수수료 계산에서 챙겨야 하는 것들
수수료 로직은 금전이 걸려 있어서 화면 버그보다 계산 로직 쪽이 더 예민하다. 이번에 직접 건드리진 않았지만, 관련 코드를 같이 훑으면서 몇 가지 챙겨봤다.
올림/내림 방향
계층별 요율을 다르게 적용하다 보면 원 단위 미만이 생긴다. 이걸 어느 방향으로 처리하느냐가 결과에 영향을 미친다. 일반적으로 수수료를 내는 쪽 입장에서는 내림 처리가 유리하고, 이 방향으로 처리하는 게 분쟁을 줄인다. 받는 쪽도 올림 없이 정확하게 받는 구조면 양쪽 모두 납득하기 쉽다. 단, 이 정책은 시스템 내에서 일관되게 적용되어야 한다. 레이어마다 다른 방향으로 처리하면 누적 오차가 생길 수 있다.
정산 배치 멱등성
정산을 배치로 돌릴 때는 같은 기간을 두 번 실행해도 결과가 달라지지 않아야 한다. 재실행이 필요한 상황은 항상 생기고, 그때 데이터가 이중으로 쌓이거나 계산이 달라지면 수동 수정이 필요해진다. 처리 여부를 상태 컬럼으로 관리하거나, 기간+채널 키로 unique 체크를 걸어두는 방식이 기본이다.
아래는 실무에서 쓰는 간단한 멱등 처리 패턴이다.
INSERT INTO settlement_result (channel_id, period, amount, created_at)
SELECT :channelId, :period, :amount, NOW()
WHERE NOT EXISTS (
SELECT 1 FROM settlement_result
WHERE channel_id = :channelId AND period = :period
);
이 정도만 걸어놔도 배치 중복 실행으로 인한 사고는 거의 막을 수 있다.
정산 UI에서 숫자를 보여주는 방법
정산 화면에서 사용자가 가장 많이 보는 건 결국 숫자다. 총 거래액, 수수료, 실수령액. 이 세 가지를 명확하게 보여주는 게 전부라고 해도 과언이 아니다.
| 항목 | 표기 방식 |
|---|---|
| 금액 일반 | 천 단위 구분자 + 원 단위 명시 |
| 마이너스 금액 | 빨간색 텍스트, 부호 앞에 - 명시 |
| 소수점 | 원 단위 기준이면 표시 안 함 |
| 합계 행 | 굵기나 배경으로 시각적 구분 |
마이너스 금액을 그냥 -100,000원으로만 표기하면 훑어볼 때 놓치기 쉽다. 색상으로 한 번 더 구분해주는 게 실제로 차이가 난다. 이건 디자인 취향이 아니라 실무에서 검증된 UX 패턴이다.
이번 작업은 규모가 크지 않았다. 그런데 수수료 화면이라는 특성상 "작다고 대충 하면 안 되는" 케이스였다. 화면 갱신 하나, 컬럼 수 하나, 권한 체크 하나가 사용자 입장에서는 "이 시스템 믿을 수 있나?"라는 판단 기준이 된다. 쌓이면 달라진다.
댓글 0
첫 댓글 달아줘.