개발 slecs

파트너 차등수익 정산 폴링 응답 누락 버그 수정

목차

파트너 차등수익 정산에서 특정 조건으로 조회하면 금액이 0원으로 내려오고, 폴링 응답에 breakdown 필드가 빠져 화면이 갱신되지 않는 버그가 있었다. 두 문제가 묶여서 나타나는 케이스였고, 변경된 파일은 내부 클래스 하나와 SQL 매퍼 하나. 파일 수만 보면 작은 픽스처럼 보이지만 정산 도메인에서 이런 게 더 조심스럽다. 숫자가 얽힌 버그는 증상보다 어디서 파생됐는지를 먼저 잡아야 하기 때문이다.

문제 원인

루트 원인은 두 개가 겹쳐 있었다.

하나는 SQL 집계 조건 문제. 차등수익은 파트너 id별로 요율이 다르게 적용되는 구조인데, 쿼리에서 집계 기준 컬럼이 잘못 잡혀 있었다. 특정 id 모드에서 집계 결과가 0으로 떨어졌고, 이게 그대로 응답에 실려 나갔다. 이런 쿼리는 테스트 환경에 파트너 데이터가 하나뿐이면 사실상 GROUP BY가 없는 것과 동일하게 동작해서, 로컬에서 개발할 때 멀쩡해 보이다가 운영에서 다양한 파트너 조합이 들어오는 순간 깨진다. 테스트 데이터 다양성 부족이 발견을 늦춘 케이스.

다른 하나는 폴링 응답 구조 불일치. 화면이 폴링으로 갱신 상태를 받을 때 breakdown 키를 기대하는데, 서버 응답 객체에 해당 키가 직렬화되지 않고 있었다. 화면 입장에선 아직 데이터 없음으로 해석하고 렌더링을 건너뛰는 것. JSON 키 이름이 맞지 않아서 생기는 조용한 실패였다.

이런 종류의 버그가 발견이 늦어지는 이유는 화면이 에러를 뱉지 않기 때문이다. 그냥 이전 값을 유지하거나 공백을 보여주는 식으로 넘어간다. 숫자가 이상하다는 제보가 없었으면 한동안 모르고 지나갔을 수도 있음. 폴링처럼 반복 호출되는 API는 첫 응답만 확인하고 넘어가는 경향이 있어서 이런 이슈를 더 늦게 발견하게 된다. 응답 스펙을 공유 타입이나 명세로 관리하지 않으면, 한쪽이 필드를 추가하거나 이름을 바꿀 때 다른 쪽이 조용히 깨진다. 프론트-백 계약이 암묵적으로 유지될 때 자주 보이는 패턴이다.

수정 내용

SQL 매퍼는 집계 기준을 수정했다. 차등 요율이 파트너 id 기준으로 분기되는 구조인데 GROUP BY 절에서 기준 컬럼이 빠져 있었다. 대략 이런 구조였음.

-- 수정 전: partner_id 없이 전체 합산
SELECT
    SUM(revenue * rate) AS total_revenue
FROM partner_revenue
WHERE settlement_date BETWEEN :start AND :end

-- 수정 후: partner_id 기준 분리 집계
SELECT
    partner_id,
    SUM(revenue * rate) AS total_revenue
FROM partner_revenue
WHERE settlement_date BETWEEN :start AND :end
GROUP BY partner_id

실제 쿼리는 조건이 더 복잡하지만 핵심은 집계 기준 컬럼 누락이었다. 수정 자체는 간단했지만 비슷한 집계 패턴이 다른 쿼리에도 있는지 확인하고, 위험한 케이스는 함께 손봤다.

내부 클래스는 폴링 응답 객체를 조립하는 부분이었다. breakdown 필드가 응답 구조에 포함되지 않은 이유는 직렬화 설정에서 해당 필드가 제외 처리돼 있었기 때문이다. 화면이 기대하는 키를 포함하도록 수정했다.

증상별로 정리하면 이런 식이다.

증상 실제 원인 확인 방법
화면 수치 갱신 안 됨 폴링 응답 JSON 키 불일치 네트워크 탭에서 응답 직접 확인
금액이 0으로 표시 집계 조건 누락 쿼리 직접 실행 후 샘플 비교
특정 파트너만 이상 분기 기준 컬럼 누락 파트너 id별 케이스 분리 테스트

검증과 재발 방지

수정 후에는 버그를 직접 재현해서 정상 동작을 확인했다. 수치 정합성은 관련 화면과 cross-check했음. 한 화면에서 정상으로 보인다고 끝내면 안 되고, 동일한 집계가 표시되는 다른 화면에서도 숫자가 일치하는지 봐야 한다. 정산 도메인은 화면별로 집계 기준이 미묘하게 다를 때가 있어서 이 단계가 생각보다 중요하다.

검증할 때 개인적으로 지키는 순서는 이렇다.

  • 수정 전 상태 스크린샷이나 수치 메모 - 비교 기준점 확보
  • 수정 후 동일 케이스로 재현 시도 - 버그 재현 불가 확인
  • 정상 케이스가 그대로 동작하는지 확인 - 회귀 방지
  • 관련 화면에서 숫자 cross-check - 파생 이슈 없는지

엣지 케이스를 꼼꼼히 따지는 게 귀찮아 보여도, 같은 버그로 다시 돌아오는 시간 비용이 훨씬 크다.

커밋은 SQL 수정과 클래스 수정을 분리했다. 논리적으로 독립된 변경이어서 하나로 묶는 것보다 쪼개는 편이 나중에 어느 시점에서 깨졌는지 추적하기 쉽다. 커밋 메시지는 "무엇을 바꿨나"보다 "왜 바꿨나"를 담으려고 했음. 2주 뒤에 자기 커밋을 다시 보면 "왜"가 없으면 맥락이 다 날아가기 때문이다.

사내 서비스를 만들다 보면 기능 하나가 SQL 집계, 상태 처리, 응답 직렬화, 화면 렌더링, 권한 체크가 모두 엮여 있어서 어느 하나만 빠뜨려도 숫자가 맞지 않거나 특정 사용자에게 이상한 화면이 나타난다. 이번 버그도 집계와 직렬화 두 군데가 동시에 어긋나 있었던 케이스였음. 특히 금융·정산 도메인은 숫자 하나가 틀리면 신뢰가 무너지고, 신뢰가 무너지면 복구가 훨씬 오래 걸린다. "대충 맞는 것 같다"로 넘어가면 반드시 다시 돌아온다는 걸 이번에 또 확인했다.

댓글 0

첫 댓글 달아줘.