파트너 포털 갭 상세 모달에 미확인 정산 내역 누락 수정
목차
파트너 포털 갭(gap) 상세 모달에서 PENDING_CONFIRM 상태의 정산 감사(audit) 행이 노출되지 않던 버그를 수정했다. 변경 파일은 SQL 매퍼 하나.
"매퍼 파일 하나"라는 말이 작업 규모를 과소평가하게 만들기 쉬운데, 실제로 문제를 추적하는 과정이 수정보다 훨씬 오래 걸렸다. 같은 패턴이 다른 경로에도 있는지 확인하는 데도 시간이 들었고.
왜 이런 버그가 생기나
정산 도메인에서 감사 행(audit row)을 조회할 때 흔하게 만나는 함정이 있다. 상태값으로 필터링하는 WHERE 절을 작성할 때, "정상 완료된 것만 보여주자"는 의도로 특정 상태를 IN 조건에 명시하다 보면, 나중에 새 상태값이 추가됐을 때 자동으로 누락된다는 것이다.
PENDING_CONFIRM은 전형적인 "중간 상태"다. 완전히 확정된 것도 아니고, 취소·실패한 것도 아닌 상태. 갭 집계 화면은 "이 금액 차이가 어디서 발생했는가"를 설명하는 화면인데, 미확인 정산 내역이 이 모달에 안 잡히면 숫자가 맞지 않는다. 담당자 입장에서는 갭이 왜 발생했는지 추적이 안 된다.
기존 쿼리는 대략 이런 구조였을 것이다:
-- 수정 전: 포함할 상태를 열거하는 패턴
WHERE audit_status IN ('CONFIRMED', 'COMPLETED')
AND settlement_date BETWEEN :startDate AND :endDate
PENDING_CONFIRM이 IN 목록에서 빠져 있으니 해당 행은 아예 집계 대상이 되지 않는다. 쿼리가 에러를 뱉지 않으니 오랫동안 묻혀 있기 좋은 버그다. 화면에서 숫자가 이상해 보여도 "다른 필터 조건 때문인가?" 하고 넘어가기도 쉽다.
수정 방향은 두 가지를 검토했다. 포함할 상태를 IN으로 열거하는 방식을 유지하면서 PENDING_CONFIRM을 추가하는 것, 아니면 제외할 상태만 NOT IN으로 명시하고 나머지는 전부 포함시키는 것. 이 도메인에서는 후자가 더 방어적이다. 나중에 상태값이 하나 더 생겼을 때 또 빠뜨리는 실수를 막을 수 있기 때문에.
-- 수정 후: 제외할 상태를 열거하는 방어적 패턴
WHERE audit_status NOT IN ('CANCELLED', 'FAILED', 'REJECTED')
AND settlement_date BETWEEN :startDate AND :endDate
단, 이 방식도 트레이드오프가 있다. 새 상태값이 추가됐을 때 의도치 않게 포함될 수 있다. 그래서 상태 목록을 어느 방향으로 관리할지 팀 내 합의가 필요하다. 우리 케이스에서는 "집계에서 무조건 제외해야 하는 상태"를 명시적으로 관리하는 쪽으로 결론을 냈다.
정산 화면에서 숫자 정합성이 중요한 이유
파트너 정산 화면에서 갭(gap)이란 기대 정산 금액과 실제 집계 금액 사이의 차이를 의미한다. 이 숫자가 맞지 않으면 파트너 측에서 이의를 제기하거나, 내부적으로 원인 파악에 이중 작업이 생긴다.
PENDING_CONFIRM 행이 모달에서 보이지 않으면 실제로 어떤 일이 벌어지냐면:
- 담당자가 갭 원인을 찾으러 다른 화면이나 도구로 우회해야 한다
- 모달의 합산 금액과 상위 화면의 갭 금액이 달라 보여서 혼란이 생긴다
- "시스템이 틀렸나, 내가 틀렸나"를 판단하는 시간이 낭비된다
- 반복되면 화면 자체를 신뢰하지 않게 된다
금융/정산 도메인에서 숫자 하나의 신뢰 문제는 전체 화면 신뢰로 번진다. 한 번 "이 화면 수치 믿어도 되나?"라는 의심이 생기면, 이후 모든 수치를 직접 계산해서 대조하는 불신이 형성된다. 그 비용은 버그 하나 고치는 것보다 훨씬 크다.
검증 방식
"화면에 이제 나온다"만 확인하는 건 충분하지 않다고 생각해서, 아래 순서로 검증했다.
| 검증 항목 | 내용 |
|---|---|
| 버그 재현 | 수정 전 PENDING_CONFIRM 행이 모달에서 빠지는 것 확인 |
| 수정 후 확인 | 동일 케이스에서 행이 정상 노출되고 합산이 맞는지 확인 |
| 회귀 방지 | CONFIRMED/COMPLETED만 있는 케이스에서 기존과 동일하게 나오는지 확인 |
| cross-check | 상위 화면 갭 금액과 모달 내 집계 합계 일치 여부 비교 |
마지막 cross-check가 제일 중요했다. SQL 조건 하나 바꿨는데 다른 케이스에서 숫자가 달라지면 오히려 더 큰 버그를 만드는 거라서, 여러 케이스를 실데이터로 비교했다. 수치가 일치하는 걸 확인하고 나서야 커밋했음.
이번 작업에서 다시 체감한 것
SQL 매퍼에서 상태값을 IN/NOT IN으로 필터링할 때는, 이 쿼리가 나중에 새 상태를 자동으로 포함/제외할지 여부를 의식하고 써야 한다. 코드 작성 시점에는 상태가 몇 개 없어 별 차이처럼 보이지만, 도메인이 성장할수록 상태값이 늘어나고 그때마다 모든 쿼리를 추적하는 건 현실적으로 불가능하다. 처음부터 "뭘 포함할 것인가"보다 "뭘 제외할 것인가"를 기준으로 쓰는 게 이런 도메인에서는 더 안전한 편이다.
커밋 메시지에도 이 판단을 남겨뒀다. 수정이 SQL 조건 한 줄이었지만, 왜 PENDING_CONFIRM을 포함시켜야 하는지, 왜 NOT IN 방향으로 바꿨는지를 적어두면 몇 달 뒤 같은 쿼리를 보는 사람이 다시 빼지 않는다. "무엇을 바꿨다"보다 "왜 이렇게 되어야 한다"가 나중에 훨씬 유용하다. 작은 커밋을 자주, 이유 중심으로 남기는 습관을 유지 중인 이유가 이거다.
사내 서비스, 특히 정산 영역은 기능 하나가 화면에 버튼 하나 추가하는 것으로 끝나지 않는다는 걸 계속 체감한다. SQL 집계, 상태 머신, 예외 처리, 화면 렌더링, 권한 체크가 모두 엮여 있어서 어느 하나만 빠뜨려도 숫자가 맞지 않거나 특정 사용자에게 이상한 화면이 나타난다. 엣지 케이스를 꼼꼼히 따지는 게 귀찮아 보여도, 나중에 같은 버그로 다시 오는 시간 비용이 훨씬 크다. "대충 맞는 것 같다"로 넘어가면 반드시 다시 돌아온다.
댓글 0
첫 댓글 달아줘.