정산 화면에 사용자 가용 잔액 카드 추가
목차
정산 화면 같은 영역은 건드리기 전에 항상 손이 좀 무거워진다. 숫자 하나가 틀리면 버그 티켓이 되기 전에 사람이 먼저 발견한다. 운영팀이든, 실제 사용자든.
이번 작업은 system-revenue/total-summary 영역에 사용자 가용 잔액 카드를 추가하는 거였음. 변경 파일 자체는 뷰/스타일 1개였는데, 실제 작업량은 그것보다 훨씬 컸다.
단순 UI 추가처럼 보이지만
기존 화면이나 API에 가용 잔액을 반환하는 엔드포인트 자체가 없었음. 그러니까 화면에서 값을 렌더링하려면 데이터가 어디서 오는지부터 설계해야 했다. 쿼리를 어떻게 짤 건지, 내부 클래스에서 어떤 메서드로 노출할 건지, 화면까지 어떤 경로로 흘러오는지.
흐름 순서대로 보면:
- SQL 매퍼에 집계 쿼리 작성
- 내부 클래스에 해당 데이터를 반환하는 메서드 추가
- 화면 컴포넌트에서 값 바인딩
- 기존 로직과 충돌 여부 확인
각 단계 자체가 어렵진 않았는데, 금융 도메인이다 보니 단계마다 검증 포인트가 붙었음.
쿼리 설계에서 신경 쓴 것들
가용 잔액이라는 개념이 단순 합산이 아닐 때가 많다. 총 잔액에서 특정 상태의 금액을 빼거나, 특정 조건의 행만 포함시키거나. 그래서 쿼리를 짜면서 몇 가지를 먼저 정해야 했음.
집계 조건 정의: 어떤 상태의 거래를 포함할 것인지. 대기 중인 건, 처리 중인 건, 실패한 건을 각각 어떻게 취급할지.
NULL 방어: 아직 거래가 없는 계정은 SUM() 결과가 NULL로 나온다. 화면에서 그대로 노출하면 "0"이 아니라 빈 칸이 나오거나 레이아웃이 흔들린다. COALESCE로 기본값 처리는 쿼리 단에서 해두는 게 낫다.
SELECT
user_id,
COALESCE(
SUM(CASE WHEN status = 'SETTLED' THEN amount ELSE 0 END),
0
) AS available_balance
FROM transactions
WHERE user_id = #{userId}
GROUP BY user_id
실제 구조와 완전히 같진 않지만 방향은 이렇다. COALESCE를 빠뜨리면 데이터 없는 계정에서 예외가 발생하거나, 프론트에서 NaN 처리를 따로 붙여야 하는 상황이 생긴다.
실행 계획 확인: 집계 쿼리는 인덱스를 제대로 타는지 항상 봐야 함. GROUP BY나 WHERE 조합에서 풀스캔이 나오면 화면 로딩이 느려지고, 트래픽이 몰릴 때 DB 부하로 이어진다. 이번엔 기존 인덱스 범위 안에서 해결됐는데, 그게 보장이 안 됐다면 인덱스 추가나 캐싱 전략도 같이 검토해야 했다.
실시간 갱신 여부: 가용 잔액이 얼마나 자주 바뀌는지를 기준으로 갱신 방식을 결정해야 한다. 이번 화면 성격상 페이지 진입 시 1회 조회로 충분하다고 판단했음. 선택지별로 트레이드오프를 정리하면:
| 갱신 방식 | 장점 | 단점 |
|---|---|---|
| 페이지 진입 시 1회 조회 | 구현 단순, 서버 부하 최소 | 실시간성 없음 |
| 짧은 주기 폴링 | 구현 단순, 어느 정도 실시간 | 불필요한 요청 반복 발생 |
| SSE / 웹소켓 | 실시간 갱신 가능 | 구현 복잡도 높음, 연결 관리 필요 |
정산 화면 특성상 잔액이 초 단위로 바뀌지 않아서 첫 번째가 맞았음. 실시간 잔고를 보여줘야 하는 화면이었다면 선택이 달라졌을 것이다.
검증은 꼼꼼하게
구현 후 직접 시나리오별로 돌려서 확인한 것들:
- 정상 잔액이 있는 계정: 금액 표시 정상 여부
- 잔액이 0인 계정: 빈 칸이 아니라 0으로 보이는지
- 신규 계정(거래 없음): NULL 방어가 제대로 작동하는지
- 관련 화면에서 보이는 수치와 일치 여부 cross-check
숫자 cross-check을 귀찮아도 꼭 해야 하는 이유가 있다. 같은 "가용 잔액"이라는 개념이 두 화면에서 서로 다른 쿼리로 집계되면 언젠가 값이 달라지는 상황이 반드시 온다. 그때 "어디가 맞는 값이냐"를 추적하는 게 지금 한 번 맞추는 것보다 훨씬 힘들다.
기존 로직이 깨지지 않았는지도 직접 확인했음. 자동화 테스트가 커버하지 못하는 화면 렌더링 엣지 케이스는 결국 눈으로 보는 수밖에 없다.
금융/결제 도메인에서 계속 작업하다 보면 "일단 되는 것 같은데"로 넘어가는 게 가장 위험하다는 걸 반복적으로 느낀다. 숫자는 틀렸을 때 명확히 드러나기 때문에 신뢰 문제로 바로 이어진다. 처음부터 꼼꼼하게 하는 게 결국 시간상으로도 이득이다.
커밋은 이번에도 논리 단위로 쪼갰다. 쿼리 추가, 메서드 추가, 화면 연동을 하나의 커밋에 다 넣으면 나중에 어느 변경에서 문제가 생겼는지 git bisect나 git log로 추적할 때 범위를 좁히기가 어렵다. 작은 커밋이 리뷰어한테도 친절하고, 롤백이 필요할 때도 안전하다. 커밋 메시지는 "무엇을 바꿨다"보다 "왜 바꿨는지"를 담으려고 노력하는데, 이게 나중에 git blame 봤을 때 진짜 도움이 된다.
사내 서비스를 만들면서 계속 체감하는 건, 기능 하나가 화면 컴포넌트 하나로 끝나지 않는다는 점이다. SQL 집계, 상태 분기, 예외 처리, 렌더링, 권한 체크가 다 엮여 있어서 어느 하나만 빠뜨려도 특정 계정에서 이상한 화면이 나온다. 그래서 작업 전 현재 동작을 메모해두고, 수정 후 같은 케이스로 확인하는 습관을 유지 중이다. 단순한 루틴 같지만 실수를 붙잡는 건 결국 이런 루틴이다.
댓글 0
첫 댓글 달아줘.