개발 slecs

정산 화면 쿠폰마진 집계 버그와 날짜 표시 오류 수정

목차

merchant-balance 정산 화면에서 쿠폰마진 컬럼이 판매대금을 집계하고 있었다. 처음 보고 받았을 때 "설마" 싶었는데 직접 쿼리를 돌려보니 진짜였다. 쿠폰마진이 찍혀야 할 자리에 판매대금 합계가 나오고 있었고, 거기에 일시 포맷팅까지 꼬여서 화면 두 군데가 동시에 이상한 상태였다.

변경 파일은 내부 클래스 하나, SQL 매퍼 하나, 뷰/스타일 하나. 파일 수만 보면 작은 픽스처럼 보이지만 정산 도메인 특성상 숫자 하나가 어긋나면 체감이 즉각적이라 제대로 파고들었다.

뭐가 잘못돼 있었나

SQL 매퍼에서 쿠폰마진을 집계해야 하는 컬럼이 실제로는 판매대금 집계 로직을 타고 있었다. 집계 대상이나 필터 조건이 의도와 달랐던 것인데, 처음부터 거기를 바로 짚어낸 건 아니고 기대값-실제값 비교부터 시작했다.

방법은 단순하다. 테스트 데이터 하나를 골라서 "이 주문의 쿠폰마진이 얼마여야 하는가"를 먼저 손으로 계산한 다음, 화면에 찍히는 값과 비교한다. 값이 다르면 쿼리를 단계별로 쪼개서 어느 지점에서 숫자가 갈라지는지 좁혀가는 방식. 이번엔 집계 쿼리 자체가 범인이었다.

간략히 표현하면 이런 구조였다.

-- 버그 상태: 쿠폰마진 컬럼이 판매대금 집계를 타고 있음
SELECT
    SUM(sale_amount) AS coupon_margin,  -- 잘못된 집계 대상
    ...
FROM merchant_balance
WHERE ...

-- 수정 후
SELECT
    SUM(coupon_margin_amount) AS coupon_margin,  -- 올바른 컬럼
    ...
FROM merchant_balance
WHERE ...

실제 쿼리는 더 복잡하지만 핵심 구조는 이랬다. 컬럼명이 다르고 집계 대상이 다른데 오래 봐도 헷갈리는 건, JOIN이 여러 개 걸리고 서브쿼리 안에 비슷한 이름의 컬럼이 반복되기 때문이다. 중간 단계 결과를 하나씩 찍어보지 않으면 놓치기 쉽다.

일시 포맷팅 문제는 별도였다. 뷰 레이어에서 날짜 데이터를 받아 렌더링하는 과정에서 형식이 어긋났던 것으로, 뷰/스타일 파일과 내부 클래스 양쪽을 손봤다. 날짜 처리는 서버-클라 경계에서 한 번, 렌더링 직전에 또 한 번 가공되다 보면 어느 쪽에서 의도한 포맷을 덮어쓰는 경우가 흔하다.

수정 과정과 검증

파일은 세 곳이지만 순서가 있다.

  • SQL 매퍼 - 집계 조건/대상 수정. 근본 원인이 여기라 먼저 숫자를 맞췄다.
  • 내부 클래스 - 매퍼 결과를 받아 처리하는 로직. 포맷팅 처리가 여기 같이 들어 있었다.
  • 뷰/스타일 - 화면 렌더링 최종 표시 수정.

단계마다 중간 값을 확인하면서 진행했다. 매퍼 고치고 쿼리 결과 확인, 내부 클래스 고치고 반환값 확인, 뷰 고치고 화면 확인. 세 파일을 한꺼번에 다 고쳐놓고 "됐나?" 확인하면 어디서 뭐가 고쳐진 건지 나중에 추적이 안 된다. 집계 버그는 수정 범위가 너무 좁거나 너무 넓으면 사이드 이펙트가 생기기 쉬워서 끊어서 가는 게 안전하다.

검증은 두 단계였다. 버그 재현 케이스를 직접 다시 밟아서 정상 동작 확인, 그다음 관련 화면 숫자와 cross-check. 정산 관련 수치는 화면이 여럿이어도 같은 기간 같은 조건이면 합계가 맞아야 한다. 상세 합산과 요약 합계가 다르거나, 기간별 합산이 전체 합산과 어긋나면 수정이 제대로 된 게 아니다.

버그 수정을 대하는 방식

증상만 픽스하고 넘어가면 시간문제다. 이번처럼 집계 버그는 특정 조건에서만 틀린 숫자가 나오는 경우가 많아서, 재현 케이스 외의 경로는 멀쩡해 보일 수 있다. 그래서 수정 전에 비슷한 집계 패턴이 다른 쿼리나 화면에도 있는지 먼저 확인했다. 같은 실수가 반복되어 있을 수 있고, 한 곳만 고치고 나머지를 놔두면 원인은 잡았는데 결과가 또 틀리는 상황이 생긴다.

버그를 잡을 때 습관적으로 따르는 체크리스트:

  • 같은 로직이 다른 경로에도 있는지 - 있으면 같이 고침
  • 수정이 기존 정상 케이스를 망가뜨리지 않는지 - 회귀 방지
  • 실제 화면에서 동작 확인 - 쿼리만 보고 끝내지 않음
  • 관련 화면 숫자 cross-check - 다른 기준으로 본 합계도 일치하는지

마지막 cross-check는 정산/결제 도메인에서 빠뜨리기 어렵다. 단일 화면에서 확인했을 때 멀쩡해 보여도 다른 기준으로 보면 안 맞는 경우가 있기 때문이다. 금융 도메인에서 숫자 신뢰는 기능 신뢰 전체와 이어진다. "대충 맞는 것 같다"로 넘기면 나중에 반드시 돌아오는데, 그것도 더 나쁜 타이밍에.

커밋은 SQL 수정, 클래스 수정, 뷰 수정으로 나눠서 올렸다. 세 파일을 하나에 묶어도 되지만, 어느 변경이 어떤 문제를 잡았는지 뒤에서 추적하려면 논리 단위로 쪼개는 게 낫다. 메시지도 "무엇을 바꿨다"보다 "왜 바꿨는지"를 담으려 했다. 석 달 뒤에 git blame 보는 사람이 나 자신일 수도 있으니까.

사내 서비스를 만들다 보면 화면 하나가 SQL 집계, 상태 처리, 예외 핸들링, 렌더링을 전부 건드린다는 걸 계속 체감한다. 쿠폰마진 컬럼 하나도 SQL 매퍼-내부 클래스-뷰 레이어를 관통해야 제대로 잡힌다. 어느 한 층만 보고 해결했다고 판단하면 나머지 층에서 같은 문제가 다른 모습으로 남는다. 귀찮아 보여도 끝까지 따라가는 게 결국 빠른 길이다.

댓글 0

첫 댓글 달아줘.