개발 slecs

잔액 소모에 결제·정산 분기와 부분 금액 처리 기준 도입

목차

잔액 소모 처리에 분기 로직을 얹고, 부분 금액 처리와 매출 기록까지 한 커밋에 묶어서 밀어넣은 작업이다.

왜 SIMPLE / SALE 분기가 필요했나

단순 잔액 차감(SIMPLE)과 매출을 수반하는 결제성 차감(SALE)은 겉으로는 "잔액이 줄어든다"는 동작이 같아 보여도, 시스템 안에서 처리해야 할 맥락이 전혀 다르다.

SIMPLE은 운영자가 직접 잔액을 조정하거나 내부 이벤트로 잔액을 소진하는 케이스다. 외부 정산 대상이 아니고, 매출로 잡히면 안 된다. 반면 SALE은 실제 서비스 이용에 따른 대가로 잔액이 빠져나가는 흐름이라, 매출 원장에 기록이 남아야 하고 통계에도 잡혀야 한다.

이걸 하나의 메서드로 처리하면 조건 분기가 뒤엉키고, 나중에 정산 버그가 터졌을 때 원인 추적이 지옥이 된다. 그래서 진입 시점에 타입을 명확히 받아서 이후 흐름을 아예 갈라버리는 구조로 잡았다.

// 소모 처리 분기 패턴 (예시)
public void consumeBalance(ConsumeRequest request) {
    if (request.getConsumeType() == ConsumeType.SALE) {
        validateSaleCondition(request);
        deductBalance(request);
        recordRevenue(request);   // SALE만 매출 기록
    } else {
        // SIMPLE: 잔액 차감만
        deductBalance(request);
    }
}

이렇게 해두면 recordRevenue 호출 경로가 SALE 분기 안에만 존재하게 되고, 코드 리뷰할 때도 "이 라인이 실행되면 반드시 매출이 기록된다"는 게 눈에 바로 들어온다.

부분 금액 처리, 왜 어려운가

잔액이 딱 떨어지는 케이스는 쉽다. 문제는 요청 금액보다 잔액이 적거나, 한도 제한이 걸리거나, 한 트랜잭션 안에서 일부만 소모 가능한 상황이다.

부분 소모를 허용하지 않으면 UX가 나빠진다. 잔액 10원 남았다고 해서 요청 전체를 거절해버리면 사용자 입장에선 황당하다. 반대로 부분 소모를 아무 제약 없이 허용하면 매출 기록이 "요청 금액"으로 잡힐지, "실제 소모 금액"으로 잡힐지 기준이 흐릿해진다.

이번 작업에서 정리한 기준은 이렇다:

상황 처리 방식 매출 기록 기준
잔액 ≥ 요청 금액 전액 소모 요청 금액
잔액 < 요청 금액 (부분 허용) 잔액 전부 소모 실제 소모 금액
잔액 = 0 소모 스킵 기록 없음

매출 원장에는 항상 "실제로 빠진 금액"만 남긴다. 요청 금액을 그대로 기록하면 잔액이 부족한 케이스에서 허수 매출이 쌓인다.

변경된 파일들 각각의 역할

이번 커밋에서 손댄 파일들을 간단히 정리하면:

  • pay/web 내부 클래스: 잔액 소모 요청을 받는 컨트롤러/서비스 레이어. SIMPLE/SALE 타입을 요청 파라미터에서 파싱해서 내려보내는 진입점 변경이 여기 들어갔다.
  • utl 내부 클래스: 공통 유틸 레이어. 부분 금액 계산 로직, 소모 가능 금액 산출 함수 등 재사용 가능한 코드를 여기에 뒀다. 비즈니스 로직을 웹 레이어에 두지 않으려면 이쪽에 내려야 한다.
  • 쿼리 매퍼: 잔액 차감 쿼리, 매출 INSERT 쿼리. SALE 타입일 때만 수행되는 INSERT가 추가됐고, 부분 소모 금액을 바인딩 파라미터로 받도록 수정됐다.
  • member/detail.jsp: 회원 상세 화면. 잔액 소모 이력을 보여주는 영역에서 SIMPLE/SALE 구분이 표시되도록 뷰 변경.
  • system-revenue/total-summary.jsp: 매출 합산 요약 화면. SALE로 기록된 잔액 소모가 매출 집계에 반영되도록 뷰 쿼리 쪽이 연결됨.

코드 리뷰 때 팀원에게 강조한 것

이런 분기 로직은 타입 값이 서버에서 검증되지 않으면 바로 보안 이슈가 된다. 클라이언트에서 consumeType=SALE을 임의로 날리면 매출 조작이 가능해지니까. 리뷰 때 서버 사이드 권한 체크가 분기 진입 전에 있는지 확인했고, 없으면 반드시 추가하도록 요청했다.

또 부분 소모 허용 여부 자체가 "정책 결정"이지 "기술 결정"이 아니다. 코드로 짜기 전에 기획/운영 쪽과 합의가 됐는지 반드시 짚고 넘어가야 한다. 나중에 "우리 그런 거 허용한다고 한 적 없는데요?" 가 터지면 매출 데이터를 소급 수정해야 하는 최악의 상황이 된다.


잔액 처리는 건드릴 때마다 "단순해 보이는데 왜 이렇게 복잡하지"라는 생각이 든다. 그 복잡함의 대부분은 "어디까지 허용하느냐"의 정책 모호함에서 온다. 코드 짜기 전에 경계 조건을 표로 정리해두는 습관, 이번에도 유효했다.

댓글 0

첫 댓글 달아줘.