매출 대시보드에 외부 채널 수익 카드 추가
목차
dashboard-revenue 영역에 외부 채널 매출·수익 카드를 새로 붙였다. 변경 파일은 뷰/스타일 1개지만, 실제 작업은 SQL 집계 설계부터 순수익 산식 정정까지 꽤 깊이 들어갔다.
외부 채널 수익은 내부 거래와 집계 기준이 조금 다르다. 내부는 결제 완료 기준으로 잡으면 되는데, 외부 채널은 정산 주기나 수수료 처리 방식이 채널마다 달라서 그냥 SUM 하면 숫자가 어긋나는 경우가 생긴다. 기존 화면에서는 이 부분을 대략적으로만 보여줬고, 기존 화면/API가 제공하지 않던 데이터가 필요해진 시점에서 쿼리 레벨부터 다시 설계해서 KPI 카드로 정리했다.
쿼리 설계와 실시간 갱신
일별/월별/누적 집계를 각각 따로 쿼리로 뽑는 게 처음엔 번거롭게 느껴지지만, 한 쿼리에 다 때려박으면 조건 하나 바뀔 때 전체를 손봐야 한다. 집계 단위를 분리해서 서브쿼리나 뷰로 감싸는 이유가 거기 있다.
-- 월별 외부 채널 수익 (채널별 분리)
SELECT
channel_code,
DATE_TRUNC('month', settled_at) AS month,
SUM(revenue) AS gross_revenue,
SUM(fee_amount) AS total_fee,
SUM(revenue - fee_amount) AS net_revenue
FROM external_channel_transactions
WHERE settled_at IS NOT NULL -- 정산 미완료 행 제외
GROUP BY channel_code, DATE_TRUNC('month', settled_at)
ORDER BY month DESC;
settled_at IS NOT NULL 조건이 핵심이다. 정산 전 임시 데이터까지 합산하면 숫자가 부풀려지고, 담당자가 그걸 믿고 의사결정을 내리면 실제 정산 후 숫자와 차이가 생겨 혼란이 온다. NULL 방어는 단순한 코드 습관이 아니라 도메인 의미를 지키는 작업이다.
실시간 갱신은 5초 폴링으로 처리했다. WebSocket이나 SSE도 고려했는데, 이 대시보드의 사용 패턴상 1분 이내 갱신이면 충분하고 인프라 복잡도를 추가할 이유가 없었다. 폴링은 단순하고 디버깅이 쉽다. 다만 탭이 백그라운드에 있을 때도 폴링이 돌면 불필요한 요청이 쌓이니, visibilitychange 이벤트로 백그라운드 탭에서 폴링을 멈추게 했다.
let intervalId = null;
function startPolling() {
if (intervalId) return;
intervalId = setInterval(fetchRevenueSummary, 5000);
}
function stopPolling() {
clearInterval(intervalId);
intervalId = null;
}
document.addEventListener('visibilitychange', () => {
document.hidden ? stopPolling() : startPolling();
});
별거 아닌 것 같지만, 탭을 여러 개 열어두고 자리를 비우는 패턴이 생기면 서버에 요청이 쌓인다. 사내 툴에서도 이런 배려가 쌓이면 인프라 비용이 조금씩 달라진다.
순수익 산식 정정과 정합성 작업
이번 작업의 핵심은 순수익 산식 정정이었다. 기존 코드에서 수수료를 특정 케이스에 이중으로 빼고 있었다. 화면 숫자만 보면 "왜 이렇게 적지?" 싶은 수준이었고 이미 관련 담당자가 의문을 제기했던 부분이다.
산식 정정 후에는 다른 화면의 같은 지표와 cross-check를 했다. 매출 요약 화면, 정산 내역 화면, 이번에 새로 추가한 외부 채널 카드 — 세 곳에서 같은 기간 숫자가 일치하는지 확인했다.
| 화면 | 확인 항목 | 결과 |
|---|---|---|
| 매출 요약 | 월별 외부 채널 합계 | 일치 |
| 정산 내역 | 채널별 순수익 | 일치 |
| 신규 카드 | 누적 / 일별 / 월별 | 일치 |
이 테이블을 PR에 붙여서 리뷰어가 검증 범위를 한눈에 볼 수 있게 했다. "내가 확인했다"는 말이 아니라, 세 곳이 동시에 같은 값을 가리킨다는 게 증거가 되어야 한다.
숫자 포맷 통일(천단위 콤마, 원 단위)도 이번에 정리했다. 같은 화면 안에서 1000000과 1,000,000원이 섞여 있으면 사용자는 둘이 다른 값이라고 오해하거나 읽기가 피곤해진다. 작은 디테일이지만 사내 툴에서 이 부분이 제대로 안 돼 있으면 데이터를 신뢰하지 않게 된다.
금융/결제 도메인에서 "대충 맞는 것 같다"는 없다
금융/결제 데이터에서 정합성이 깨지는 순간, 사람들은 툴 대신 엑셀로 돌아간다. 당연한 선택이다. 내가 만든 화면이 엑셀보다 더 믿음직스러워야 한다는 게 이 도메인에서 일하면서 계속 느끼는 기준이다.
사내 서비스를 만들다 보면 기능 하나가 화면에 버튼 하나 추가하는 것으로 끝나지 않는다는 걸 반복해서 체감한다. SQL 집계, 실시간 갱신, 예외 처리, 화면 렌더링, 숫자 포맷이 모두 엮여 있어서 어느 하나만 빠뜨려도 특정 사용자에게 이상한 숫자가 나타난다.
이번 작업은 파일 하나를 건드린 거지만, 그 안에 담긴 결정들 — 폴링 주기 선택, NULL 처리 방식, 산식 정정, 검증 범위 — 이 하나하나가 나중에 다시 돌아오는 일을 막는다.
변경 전 현재 수치를 메모해두고, 수정 후 같은 케이스로 확인하고, 관련 화면이 있으면 숫자를 cross-check하는 흐름을 유지하면 사후 디버깅 비용이 확연히 줄어든다. 커밋 메시지에도 "외부 채널 수익 카드 추가"보다 "순수익 산식 이중 차감 수정 및 외부 채널 KPI 카드 추가 — 정산 미완료 행 제외 조건 포함"처럼 왜 바꿨는지를 담으려고 한다. 논리적으로 독립된 단위로 커밋을 쪼개는 습관과 함께, 3개월 뒤에 git log를 볼 때 이 차이가 크다.
댓글 0
첫 댓글 달아줘.