개발 slecs

쿠폰 마진 KPI와 매입원가 분해를 정산 화면에 추가

목차

platform-revenue 영역에 쿠폰 마진 KPI 카드와 매입원가 분해를 추가했다. 변경 파일은 SQL 매퍼 1개, 뷰/스타일 1개. 파일 수만 보면 별거 아닌 것 같지만, 쿼리 레벨부터 설계해서 기존 정산 데이터와 정합성을 맞추는 작업이 대부분이었음.

왜 이 데이터가 필요했나

정산 화면에서 매출 총액이나 주문 건수는 보통 일찍부터 집계된다. 문제는 "이 매출에서 실제로 얼마를 남겼냐"로 파고들 때 생기는데, 쿠폰 할인이 끼면 숫자가 복잡해짐.

쿠폰 할인에는 크게 두 종류가 있다. 플랫폼이 비용을 부담하는 경우와, 공급사 또는 상품 단위에서 부담하는 경우. 화면에서 할인액만 합산하면 누가 쏜 돈인지 구분이 안 된다. 그러면 운영팀이 KPI를 볼 때마다 별도로 계산해야 하고, 계산 방법이 사람마다 달라지면 회의할 때마다 숫자가 달라지는 일이 생김.

매입원가 분해도 비슷한 맥락이다. 상품 카테고리나 공급사별로 원가 구조가 다른데, 총액으로만 보면 어느 채널이 마진을 잡아먹는지 알 수가 없다. 정산 화면에서 바로 분해해서 볼 수 있으면 의사결정이 훨씬 빠름. 그래서 이 기능은 단순 UI 추가가 아니라, 기존 정산 API가 내려주지 않던 집계를 SQL 레벨에서 새로 정의하는 작업이었다.

구현 흐름과 설계 고민

관련 내부 클래스에 메서드를 추가하고, SQL 매퍼에 쿼리를 새로 작성한 뒤, 화면 쪽에서 렌더링 연동까지 이어지는 흐름이었음. 레이어를 순서대로 짚으면:

  1. SQL 매퍼에 집계 쿼리 추가
  2. 내부 클래스에 결과를 받아 가공하는 메서드 추가
  3. 뷰에서 KPI 카드 렌더링
  4. 기존 정산 로직과 충돌 여부 확인

쿼리에서 제일 신경 쓴 건 NULL 처리와 빈 데이터 케이스였다. 매입원가가 아직 입력되지 않은 상품이 섞여 있으면 SUM이 예상과 다르게 나옴. COALESCE로 방어하는 게 기본이지만, 어느 시점의 데이터를 기준으로 집계하느냐에 따라 결과가 달라질 수 있어서 쿼리 실행 계획도 사전에 확인했다.

-- 쿠폰 부담 주체별 분해 집계 (구조 예시)
SELECT
  order_id,
  SUM(CASE WHEN coupon_bearer = 'platform'
           THEN discount_amount ELSE 0 END) AS platform_discount,
  SUM(CASE WHEN coupon_bearer = 'supplier'
           THEN discount_amount ELSE 0 END) AS supplier_discount,
  COALESCE(SUM(purchase_cost), 0)           AS total_purchase_cost
FROM order_coupon_detail
WHERE settled_at BETWEEN :startDate AND :endDate
GROUP BY order_id

GROUP BY 기준이 바뀌면 KPI 카드 숫자도 바뀌니까, 기존 정산 집계와 같은 기준을 쓰는지 꼭 확인해야 했음. 실제로 이 부분에서 시간이 제일 많이 들었다.

설계하면서 고민한 항목을 정리하면 이렇다:

항목 결정 방향 이유
실시간 갱신 여부 명시적 요청 시에만 집계 정산 KPI는 실시간성 불필요, 집계 쿼리 비용 고려
NULL/빈 데이터 처리 COALESCE + CASE WHEN 방어 원가 미입력 상품 혼재 가능성
기존 데이터 정합성 동일 WHERE 조건, 동일 집계 기준 공유 다른 화면 숫자와 불일치 방지
권한 체크 기존 정산 화면 권한 체계 그대로 따름 KPI 민감도, 신규 예외 흐름 최소화

실시간 갱신 쪽은 폴링을 붙이면 서버 부하가 조용히 쌓이는 구조라, 이벤트 기반이든 폴링이든 도입 전에 집계 쿼리 비용을 먼저 재는 게 맞다고 봄. 이번엔 명시적 요청 시에만 집계하는 걸로 결정했다.

화면 렌더링은 기존 스타일 파일에 KPI 카드 컴포넌트를 추가하는 방식이었음. 별도 파일을 만들지 않고 기존 뷰 파일 안에 넣었는데, 카드 종류가 더 늘어나면 분리를 고려해야 할 시점이 올 것 같다.

검증과 작업 방식

구현 후에는 직접 화면에서 동작을 확인했다. 기존 정산 화면의 합계와 신규 KPI 카드 수치를 cross-check했고, 빈 데이터 케이스도 강제로 만들어서 렌더링이 깨지지 않는지 봤음.

금융/결제 도메인에서 반복적으로 느끼는 건, 숫자는 한 번 신뢰를 잃으면 회복이 어렵다는 거다. "대충 비슷한 것 같은데요"가 통하는 영역이 아님. 운영팀이 KPI를 보다가 다른 화면의 숫자와 안 맞으면, 그 순간부터 시스템 전체를 의심하기 시작한다. SQL 집계, 상태 머신, 예외 처리, 화면 렌더링, 권한 체크가 모두 엮여 있어서 어느 하나만 빠뜨려도 숫자가 틀리거나 특정 사용자에게 이상한 화면이 나타남. 기능 하나가 단순히 화면에 버튼 하나 추가하는 것으로 끝나지 않는다는 걸 계속 체감하는 중이다.

작업 루틴으로는 변경 전 수치를 메모해두고 변경 후 같은 케이스로 확인하는 습관을 유지하고 있다. 커밋 메시지도 "무엇을 바꿨다"보다 "왜 바꿨다"를 담으려 노력함. 두 달 뒤에 git log를 보는 사람이 맥락을 알 수 있어야 하니까. 논리적으로 독립된 단위로 커밋을 쪼개는 것도 같은 이유인데, SQL 매퍼 변경, 내부 클래스 변경, 뷰 변경을 하나로 묶으면 나중에 어느 변경이 문제를 일으켰는지 추적하기 어렵다. 작은 커밋이 많으면 bisect나 revert가 훨씬 수월해짐.

댓글 0

첫 댓글 달아줘.