출금 모달 개선
목차
partner 쪽 버그를 하나 잡았다. 직접적으로는 출금 모달 UX 개선이랑 정산계좌 승인시각 기록 보정 두 가지인데, 변경 파일은 SQL 매퍼 하나, 뷰/스타일 하나로 의외로 적다. 파일 수가 적다는 게 간단하다는 뜻은 아니고, 오히려 그 파일 안에 엮인 맥락이 많다는 신호일 때가 많다. 이번이 딱 그랬다.
SQL 집계, 타임스탬프, 그리고 조용한 버그
출금 관련 데이터는 SQL 매퍼 레벨에서 집계 조건이 조금만 어긋나도 조용히 틀린 숫자를 내뱉는다. 에러가 나는 게 아니라 그냥 숫자가 미묘하게 다르게 나오는 케이스. 로그에도 안 잡히고, 개발자가 실제 금액과 비교하지 않으면 발견 자체가 늦어진다. 이번 케이스도 비슷했다. 특정 상태값이 조합될 때 WHERE 조건이 의도와 다르게 작동해서 일부 행이 빠지거나 중복 집계되는 상황이었다. 쿼리 자체는 문법 오류가 없으니 실행은 정상이고, 숫자만 조금씩 틀림.
이런 집계 쿼리를 건드릴 때 나는 보통 이런 순서로 접근한다.
-- 1. 문제 재현: 실제 데이터로 현상 확인
SELECT partner_id, SUM(amount), COUNT(*)
FROM withdrawal_request
WHERE status IN ('APPROVED', 'COMPLETED')
GROUP BY partner_id;
-- 2. 의심 조건 격리: 엣지 케이스 데이터만 분리
SELECT *
FROM withdrawal_request
WHERE status = 'APPROVED'
AND confirmed_at IS NULL; -- 승인됐는데 시각 없는 케이스
-- 3. 수정 후 파트너사별 행 단위로 이전 결과와 대조
전체 합계만 맞춰보면 특정 파트너사 데이터가 상쇄된 채로 통과될 수 있어서, 파트너사별 행 단위로 대조하는 걸 원칙으로 하고 있다.
정산계좌 승인시각 보정은 별도 이슈였다. 승인 처리 시점에 confirmed_at이 제대로 기록 안 되는 케이스가 있었는데, 원인은 단순했다. 승인 로직이 여러 경로로 들어올 수 있는 구조인데, 그 중 한 경로에서 시각 업데이트 쿼리가 빠져 있었던 것. 결과적으로 승인은 됐는데 시각이 NULL인 행이 조금씩 쌓이고 있었다.
타임스탬프 하나가 대수롭지 않아 보일 수 있는데, 실제로는 정산 기준일 계산이나 감사 로그에 직결된다. "언제 승인됐는지"가 불명확해지면 파트너사 입장에서 처리 지연인지 정상인지 구분이 안 되고, 그게 누적되면 시스템 신뢰 문제로 번진다. 숫자 하나, 시각 하나가 도메인 신뢰의 단위가 되는 게 금융/결제 쪽의 특성이다.
같은 이슈가 다른 경로에도 있는지 grep으로 확인했고, 위험한 케이스는 같이 묶어서 수정했다. 한 경로만 고치고 끝내면 얼마 안 가서 "그 버그랑 비슷한 게 또 나왔어요"가 돌아온다.
출금 모달 UX - 상태를 명확하게 보여주는 것의 중요성
UX 쪽은 SQL 쪽과는 결이 다른 작업이다. 숫자가 맞아도 화면이 헷갈리면 사용자는 믿지 않는다. 출금 플로우는 단계가 여럿이고, 각 단계에서 사용자에게 보여줘야 할 정보가 다르다.
| 상태 | 의미 | 화면에 보여야 할 것 |
|---|---|---|
| PENDING | 출금 신청 접수 | 신청 완료, 처리 대기 중 |
| APPROVED | 내부 승인 완료 | 승인됨, 실제 지급 예정 |
| COMPLETED | 실제 지급 완료 | 출금 완료, 처리 일시 |
| REJECTED | 반려 | 반려 사유 표시 |
이번 수정의 핵심은 APPROVED와 COMPLETED 사이 중간 상태를 사용자가 혼동하지 않도록 레이블링을 다듬은 것, 그리고 confirmed_at이 NULL인 케이스에서 모달 렌더링이 깨지지 않도록 방어 처리를 추가한 것이다. 타임스탬프 보정 작업이 뷰 수정과 연결되는 지점이 여기다. DB에 NULL이 있는 상황을 화면도 감당할 수 있어야 하고, 동시에 그 NULL 자체가 생기지 않도록 서버에서도 막아야 한다. 두 파일 수정이지만 실제로는 같은 문제의 두 레이어를 건드린 셈이다.
뷰/스타일 파일에 렌더링 조건과 스타일이 같이 묶여 있는 구조라서, 렌더링 조건 하나 손대면 스타일 확인도 함께 해야 했다. 파일 하나인데 화면 세 군데를 확인하는 상황. 이런 구조는 나중에 분리할 여지가 있지만, 지금 당장 리팩터링까지 끌고 가면 변경 범위가 너무 넓어진다. 지금은 최소 변경으로 버그만 잡고, 분리는 별도 작업으로 트래킹하는 게 맞다고 판단했다.
이번 작업에서 실제로 쓴 체크리스트
- 중복 경로 확인: 승인 로직이 여러 진입점을 갖는 구조면, 수정 전에 grep으로 같은 패턴을 먼저 찾는다. 하나만 고치면 나머지에서 같은 버그가 반복된다.
- 회귀 방지: 엣지 케이스만 잡으려다 기존에 잘 되던 케이스를 망가뜨리는 건 더 나쁘다. 정상 케이스 데이터로 수정 전/후 결과를 명시적으로 비교했다.
- 실제 화면 동작 확인: SQL 결과만 보고 끝내지 않고, 출금 신청 → 승인 → 완료 흐름을 화면에서 직접 타봤다. 쿼리는 맞아도 렌더링 조건에서 상태 매핑이 틀릴 수 있어서.
- 숫자 cross-check: 출금 모달의 합계가 정산 현황 화면의 합계와 일치하는지 비교. 도메인 내 숫자는 여러 화면에서 동시에 맞아야 한다. 하나만 맞으면 아직 찾지 못한 버그가 있다는 뜻이다.
커밋은 SQL 매퍼 수정, 뷰 수정 두 개로 쪼갰다. 논리적으로 독립된 단위라서 하나로 묶으면 나중에 어느 변경이 어떤 영향을 줬는지 추적하기 어려워진다. 커밋 메시지도 "쿼리 수정", "화면 수정" 같은 what 대신, "정산계좌 승인 처리 누락 경로에서 confirmed_at 업데이트 추가", "출금 모달 승인시각 null 케이스 렌더링 방어 처리" 같은 why를 담으려고 했다.
사내 서비스, 특히 파트너 정산 같은 금융 도메인은 기능 하나가 SQL 집계, 상태 머신, 예외 처리, 화면 렌더링, 권한 체크가 모두 엮여 있어서 어느 하나만 빠뜨려도 숫자가 어긋나거나 특정 상황에서 이상한 화면이 나온다. "대충 맞는 것 같다"로 넘어가면 반드시 다시 돌아온다. 이번에도 그걸 다시 한 번 확인한 셈이다.
댓글 0
첫 댓글 달아줘.