가맹점 출금 목록 정합성 분석 보고서 작성
목차
가맹점 출금 목록 정합성 분석 보고서를 작성했음. 보고서 기준은 20260427 1430 merchant withdrawal list.
운영 중에 수치가 맞지 않는다는 낌새가 왔을 때 제일 먼저 하는 실수가 로그 뒤지기다. 로그는 이벤트의 흔적이지 집합 연산이 아니라서, 특정 가맹점의 출금 집계가 어긋났을 때 "언제 어떤 요청이 들어왔는지"는 보여줘도 "최종적으로 얼마가 처리됐고 얼마가 빠졌는지"는 잘 안 보인다. 그래서 SQL로 직접 집계하는 방식으로 갔음.
단순 쿼리 확인으로 끝내지 않고 HTML 보고서로 뽑는 이유도 있다. 숫자가 많아지면 터미널 출력은 스크롤 한 번이면 맥락이 날아가고, 나중에 "그 분석 어땠지?" 할 때 재현이 안 됨. HTML로 남겨두면 브라우저에서 바로 열리고, 표와 간단한 색상 강조로 이상 항목이 눈에 띄게 구성할 수 있다. 커밋 메시지에 파일명만 남겨둬도 나중에 추적이 됨.
집계 쿼리 설계
분석 접근 방식은 단순하다. 내부 테이블에서 기대값 집합과 실제값 집합을 따로 뽑아서 비교하는 것.
-- 기대 처리 건수 vs 실제 처리 건수 비교 (패턴 예시)
SELECT
m.merchant_id,
m.expected_count,
COALESCE(a.actual_count, 0) AS actual_count,
m.expected_count - COALESCE(a.actual_count, 0) AS diff
FROM expected_withdrawals m
LEFT JOIN (
SELECT merchant_id, COUNT(*) AS actual_count
FROM withdrawal_logs
WHERE status = 'COMPLETED'
AND settled_at BETWEEN '2026-04-27 14:00:00' AND '2026-04-27 15:00:00'
GROUP BY merchant_id
) a ON m.merchant_id = a.merchant_id
WHERE m.expected_count != COALESCE(a.actual_count, 0)
ORDER BY diff DESC;
이 패턴에서 LEFT JOIN이 핵심이다. INNER JOIN을 쓰면 처리 실적이 아예 없는 가맹점이 결과에서 빠진다. 처리 누락 건을 찾는 게 목적인 상황에서 INNER JOIN은 버그를 숨기는 쿼리가 됨. COALESCE로 NULL을 0으로 바꿔주는 것도 같은 이유다.
기대값 집합은 어디서 오냐는 케이스마다 다른데, 이번엔 외부에서 확정된 출금 요청이 별도 테이블에 먼저 적재되고 처리 결과가 나중에 쌓이는 구조였음. 두 집합을 비교하는 흐름이 자연스럽게 맞아 떨어졌다.
성능 측면에서는 이번 쿼리는 허용 범위 안에서 돌았음. 집계 쿼리는 정기 운영이 아니라 분석 시점에만 실행하기 때문에 약간의 레이턴시는 감수할 수 있다. 다만 테이블 규모가 커지면 settled_at에 인덱스가 없으면 풀스캔이 되므로 그 부분은 확인해 두는 게 낫다.
발견 사항과 보고서 구성
보고서는 크게 세 파트로 잡았음.
- 집계 요약: 총 요청 건수, 완료 건수, 절대 차이
- 이상 항목 목록: 불일치 발생 레코드를 표로 나열, 원인 가설 기재
- 재현 케이스 정리 및 후속 액션 도출
이번 분석에서 나온 상태는 다음과 같다.
| 항목 | 상태 | 비고 |
|---|---|---|
| 데이터 정합성 | 일부 불일치 발견 | 후속 커밋으로 픽스 |
| 처리 누락 건 | 확인 및 후속 조치 필요 | 엣지 케이스 포함 |
| 쿼리 성능 | 허용 범위 내 | 인덱스 활용 확인 |
| 엣지 케이스 | 추가 검토 필요 | 경계 시간대 처리 포함 |
정합성 불일치는 대부분 두 갈래다. 로직 버그이거나, 파이프라인 중간에 실패가 끼어든 것. 이번엔 경계 시간대 처리에서 미세한 차이가 원인으로 올라왔음. 시간 범위 조건이 한쪽 테이블은 이상(이하) 포함, 다른 쪽은 미만(초과) 미만으로 처리되는 케이스 - 흔하지만 발견하기 까다로운 유형이다. 쿼리 두 줄 차이인데 집계 결과는 수십 건 차이가 남.
보고서 → 수정 → 재검증 사이클
코드 수정이 먼저 들어가면 "뭘 고쳤더니 됐다"는 결과는 남아도 "원래 어떤 상태였는지"가 사라진다. 보고서를 먼저 뽑으면 수정 전 스냅샷이 파일로 남고, 수정 후에 같은 쿼리를 다시 돌려서 이상 항목 목록이 줄었는지 수치로 비교할 수 있다. 이번에도 후속 커밋 후 보고서를 다시 실행해서 개선 여부를 확인했음.
이 사이클을 반복하다 보면 부수 효과가 생긴다. 보고서 파일이 쌓이면서 "이 도메인에서 어떤 유형의 문제가 반복되는지" 패턴이 보이기 시작함. 한 번은 우연, 두 번은 주의, 세 번은 구조 문제다. 그 시점이 코드 한 줄 픽스가 아니라 설계 자체를 손볼 타이밍이라는 신호임.
구두로 "이런 문제가 있었다"고 전달하는 것보다 HTML 파일 링크 하나 공유하는 게 훨씬 빠르고 맥락이 살아있다. 코드 수정의 근거 문서로도 쓰이고, 이후 유사 이슈가 터졌을 때 레퍼런스로도 쓰인다. 보고서를 만드는 데 드는 시간이 길어봤자 한 시간인데, 그 한 시간이 이후 디버깅 세 시간을 아껴줄 때가 많았음.
가장 효과적인 분석 방법은 결국 "내가 예상한 숫자"와 "실제 DB 숫자"를 나란히 놓고 비교하는 것이다. 차이가 있는 행이 버그나 로직 오류의 위치를 직접 가리킨다. 단순하지만 로그 분석이나 코드 리딩보다 원인을 훨씬 빠르게 좁힌다는 걸 반복해서 확인했음.
댓글 0
첫 댓글 달아줘.