개발 slecs

재선물 후 주문내역 구매자가 가려지는 버그 수정

목차

partner-portal 주문내역 화면에서 재선물(re-gift) 이후 구매자 칸이 holder로 뒤바뀌는 버그를 수정했음. 변경 파일은 SQL 매퍼 1개다.

뭐가 문제였나

선물 플로우에는 크게 두 가지 역할이 있다. 실제로 돈을 낸 "구매자"와, 현재 그 선물을 보유 중인 "holder". 선물을 처음 받은 사람이 그걸 다시 다른 사람에게 전달하는 게 재선물인데, 이 경우 holder는 바뀌지만 구매자는 변하지 않아야 한다.

문제는 SQL에서 주문별 구매자를 조회할 때 holder 쪽 테이블을 JOIN한 이후 집계 조건이 명확하지 않아서, 재선물이 발생한 건에 한해 가장 최근 holder가 구매자 자리를 차지하고 있었음. 일반 주문은 정상이라서 테스트에서 잘 안 잡힌다. 딱 재선물 케이스만 밟아야 보이는 버그였다.

이런 유형의 버그가 SQL 레이어에서 자주 나타나는 이유가 있음. 도메인 초기에 "선물을 받은 사람 = 구매자"로 단순하게 설계하다가, 재선물 같은 상태 전이 기능이 붙으면 기존 쿼리가 암묵적으로 가정하던 것들이 하나씩 깨지기 시작함. 스키마가 바뀌지 않았더라도 데이터의 생애주기가 늘어난 것만으로 기존 쿼리의 전제가 틀려지는 경우다. 커밋 이력을 봤더니 holder JOIN이 들어온 시점이 재선물 기능 추가 때였음. 기능을 붙일 때 구매자 조회 경로까지 검토가 안 됐던 것.

수정 방향

기존 쿼리가 holder 테이블을 JOIN해서 집계할 때 최신 레코드를 그냥 끌어다 쓰던 부분을, 원본 구매자 기준으로 조건을 고정했음. 개념적으로는 대략 이렇다.

-- 수정 전: holder JOIN 시 최신 레코드가 그냥 올라오던 구조
SELECT
  o.order_id,
  h.user_name AS buyer_name   -- 재선물 후엔 holder가 교체되므로 오염됨
FROM orders o
JOIN gift_holders h ON h.order_id = o.order_id
-- holder 이력이 여러 건일 때 어느 걸 쓸지 집계 조건이 빠져 있음

-- 수정 후: 원본 구매자를 직접 참조하거나
-- holder 이력 중 최초 레코드로 한정
SELECT
  o.order_id,
  o.buyer_name                -- 원본 구매자 칼럼 직접 참조
FROM orders o

실제 매퍼에서는 집계 함수 조건과 서브쿼리 범위를 조정했는데, 핵심은 "현재 holder"와 "최초 구매자"를 명확히 분리하는 것이었음. holder 이력이 여러 건 쌓인 경우를 위해 집계 조건도 같이 정리했다.

수정하면서 같은 방식으로 holder를 JOIN하는 다른 쿼리가 더 있는지 매퍼 전체를 훑었음. 구매자 칸을 쓰지 않는 집계 쿼리는 영향이 없었고, 주문내역 상세 쪽 쿼리 하나가 비슷한 패턴이었는데 거기선 holder를 의도적으로 보여주는 게 맞아서 건드리지 않았다. 판단 근거를 커밋 메시지에 남겨뒀음.

이 도메인에서 버그를 다루는 방식

단순히 해당 케이스만 픽스하고 닫는 게 아니라 주변 맥락을 같이 확인하는 게 습관이 됐다.

체크 항목 이번 케이스
동일 패턴 중복 여부 매퍼 전체 JOIN 패턴 확인, 1건 추가 발견(의도적 케이스라 제외)
회귀 확인 일반 주문, 단순 선물, 재선물 세 케이스 직접 재현
숫자 cross-check 주문내역 목록 - 상세 - 정산 화면 간 구매자 일치 여부 확인
커밋 범위 SQL 매퍼 1개, 논리 단위 단독 커밋

재현이 번거로운 버그일수록 이 루틴이 필요하다. 수정 후 통과했다고 바로 넘어가면, 내가 미처 생각 못한 케이스에서 또 나온다. 엣지 케이스를 꼼꼼히 따지는 게 귀찮아 보여도, 나중에 같은 버그로 다시 오는 시간 비용이 훨씬 크다.

이 화면이 단순 조회 UI 같아도 결국엔 파트너에게 보여주는 거래 데이터다. 구매자가 누구냐는 정산이나 CS 처리할 때 직접 연결되는 필드라, 틀리면 단순 UI 버그가 아니라 신뢰 문제가 됨. 금융/결제 도메인에서 "대충 맞는 것 같다"로 넘어가면 안 되는 이유가 여기 있다.

새 기능이 기존 데이터의 생애주기를 바꾸는 경우, 그 기능과 직접 연결된 화면만 확인하는 건 부족하다. 그 데이터를 참조하는 다른 화면과 쿼리를 함께 훑어야 함. SQL 집계, 상태 머신, 예외 처리, 화면 렌더링, 권한 체크가 모두 엮여 있어서 어느 하나만 빠뜨려도 숫자가 맞지 않거나 특정 사용자에게 이상한 화면이 나타남. 이번에는 매퍼 1개 수정으로 끝났지만, 상태 전이가 복잡해질수록 영향 범위가 예상을 넘는 경우가 많다.

작은 커밋 단위를 유지하는 것도 이 맥락에서다. 나중에 비슷한 버그가 생겼을 때 어느 변경이 어느 시점에 어떤 의도로 들어갔는지 파악하는 데 커밋 이력이 실질적인 도움이 됨. 커밋 메시지에 "무엇을" 보다 "왜"를 남기면 몇 달 후의 내가 고마워한다. 변경 전 동작을 수치나 스크린샷으로 기록해두는 것도 마찬가지 이유다. 수정 후 같은 케이스로 확인할 때, 기준이 있어야 뭔가 달라졌는지 알 수 있다.

댓글 0

첫 댓글 달아줘.