구독 vs 사용량 기반 청구: 처음부터 무엇을 저장해야 할까
데이터 모델 관점에서 본 구독형 vs 사용량 기반 청구: 미터, 한도, 인보이스, 비례배분 규칙 및 처음부터 저장해야 할 기록들.

가격 모델에는 데이터 계획이 필요하다
가격은 단순히 웹사이트의 한 페이지가 아니다. 가격은 무엇을 기록해야 하는지, 어떻게 보고할지, 몇 달 후 청구 내역을 설명할 수 있는지를 결정한다. 구독형과 사용량 기반 청구를 선택할 때, 청구 데이터의 형태도 함께 선택하는 것이다.
단순한 구독은 보통 몇 가지 사실로 계산할 수 있다: 플랜, 청구 기간, 시작일, 할인. 사용량 기반 요금은 더 많은 것을 필요로 한다: 무엇이 사용되었는지, 언제 발생했는지, 어느 고객과 연결되는지, 그리고 그 사용량이 어떻게 금액으로 전환되는지. 이러한 기록이 없으면 인보이스는 보낼 수 있겠지만 정당성을 입증할 수는 없다.
나중에 사용량을 추가하려다 계획 없이 시작하면 보통 세 곳에서 문제가 발생한다:
- 신뢰할 수 있는 사용 내역이 없어 고객이 요금을 이의 제기한다.
- 팀마다 "사용량"을 다르게 정의해 분석이 추측 수준이 된다.
- 재무팀이 원시 입력값이 없거나 덮어써졌기 때문에 인보이스를 감사할 수 없다.
목표는 지루하지만 필수적인 것: 같은 인보이스를 항상 같은 방식으로 계산하는 것이다. 즉 저장된 사실(플랜 조건, 미터 규칙, 사용 이벤트, 적용된 정확한 가격 버전)로부터 계산을 재생할 수 있어야 한다.
"모델링 관점"은 엔지니어가 아니더라도 청구를 서로 맞춰 들어가는 구성 요소로 설명하는 것을 뜻한다. 예를 들어 팀 채팅 제품을 상상해 보자:
- 구독: 워크스페이스당 월 $99
- 사용량: 50,000건 초과 메시지당 $0.01
나중에 이를 지원하려면 데이터는 다음 질문에 답할 수 있어야 한다: 어떤 워크스페이스인지, 어떤 월인지, 몇 건의 메시지인지, 포함된 양은 얼마인지, 어떤 가격 규칙이 활성화되어 있었는지.
구독과 사용량: 실제로 어떻게 다른가
구독은 접근권에 대해 청구하고, 사용량 기반은 소비에 대해 청구한다. 업그레이드, 다운그레이드, 비례배분(proration)과 현실의 예외 상황이 생기면 두 방식은 매우 다르게 행동한다.
구독에서는 핵심 질문이 "고객이 이 기간에 제품을 사용할 권한이 있었는가?"이다. 주로 플랜, 좌석 수, 청구 기간, 인보이스 결제 여부를 추적한다. 사용량은 여전히 중요하지만 보통 라인 항목보다는 한도(소프트 또는 하드)로 나타난다.
사용량 기반 청구에서는 핵심 질문이 "무슨 일이, 정확히, 언제 일어났는가?"가 된다. 신뢰할 수 있는 계량(metering), 사용을 언제 집계할지에 대한 명확한 규칙, 나중에 모든 요금을 설명할 수 있는 수단이 필요하다. UI에 하나의 숫자(예: "1,243 API 호출")만 보이더라도 그 뒤에는 일관되고 감사 가능한 이벤트 집합이 있어야 한다.
많은 B2B SaaS 팀은 기본 요금에 번들을 포함하고 초과분을 과금하는 하이브리드 가격을 선택한다.
흔한 하이브리드 패턴
대부분의 하이브리드 모델은 다음 몇 가지 형태 중 하나로 귀결된다:
- 플랫폼 기본 요금 + 좌석당 요금
- 기본 요금 + 포함 단위(메시지, 작업, API 호출) + 초과 요율
- 계층형 플랜 + 사용량 추가 옵션(활성화된 경우에만 과금)
- 최소 약정 + 사용량 크레딧 소진 방식
예측 가능성은 고객이 예산 승인을 받아야 하거나 안정적인 월별 지출이 필요할 때 유리하다. 사용량에 따라 가치가 확실히 늘어날 때(예: "처리된 인보이스 건수당")는 종량제가 더 적합하고, 고객이 시험 중이어서 리스크를 낮추고 싶을 때도 적합하다.
플랜 변경은 필연적이다. 가격, 번들, 패키징은 바뀔 것이다. 청구 시스템을 설계할 때 새로운 미터를 추가하거나 새로운 계층을 도입하거나 "포함"의 의미를 바꾸더라도 과거 기록을 다시 쓰지 않아도 되게 하라. 실용적인 규칙: 고객의 플랜과 가격 조건을 청구 시점에 적용된 상태로 저장하라. 현재 상태만 저장하지 마라.
신뢰할 수 있게 측정할 수 있는 미터 정의하기
미터는 요금 부과 대상이 되는 정확한 항목으로, 두 사람이 세어도 같은 값이 나오도록 명확히 문서화되어야 한다. 미터는 이벤트(무슨 일이 일어났는지), 단위(무엇을 세는지), 타이밍(언제 집계되는지) 세 부분으로 구성된다.
많은 분쟁은 여기서 시작된다. 한 쪽은 결과에 대해 비용을 부과한다고 생각하고, 다른 쪽은 측정 가능한 활동에 대해 과금한다고 생각한다.
미터를 모호하지 않게 만들기
제품 동작에 직접 대응하고 자동으로 기록 가능한 미터를 선택하라. 흔한 예:
- 좌석(로그인할 수 있는 활성 사용자 수)
- API 호출(성공한 요청만 또는 모든 요청)
- 저장소(시점의 GB, 또는 기간 평균)
- 메시지(발송, 전달, 열람)
- 컴퓨트 분(작업이 실행된 시간)
그다음에 무엇이 포함되고 제외되는지 정의하라. 예를 들어 API 호출 과금이라면 재시도(retries)를 포함할지, 4xx/5xx 응답을 포함할지, 자체 통합에서 발생한 내부 호출을 포함할지 결정하라.
타이밍은 단위만큼 중요하다. 좌석은 청구 기간당 시점 스냅샷(point-in-time)이 더 잘 맞는다. API 호출은 일반적으로 윈도우 내 합계로 계산한다. 저장소는 까다롭다: 고객은 보통 "얼마나 저장했는가"에 대해 비용을 내길 기대하는데, 이는 피크가 아니라 기간 평균을 의미하는 경우가 많다.
범위도 결정하라: 계정당인가, 워크스페이스/프로젝트당인가. 팀별로 별도 청구가 가능하면 미터는 워크스페이스 단위가 되어야 한다는 단순 규칙이 유용하다.
한도, 계층, 그리고 엔타이틀먼트
엔타이틀먼트(권한)는 고객이 구매로 인해 무엇을 할 수 있는지를 정하는 규칙이다. 몇 명의 사용자를 추가할 수 있는지, 어떤 기능이 활성화되는지, 한 달당 허용되는 볼륨은 얼마인지 같은 질문에 답한다. 엔타이틀먼트는 접근과 청구 사이에 위치한다: 제품이 허용하는 것을 형성하고, 미터링은 실제로 발생한 것을 기록한다.
엔타이틀먼트는 미터링 로직과 분리해 두라. 엔타이틀먼트는 읽기 쉽고 안정적이어야 한다(플랜, 애드온, 계약 조건). 미터링은 제품 변화에 따라 진화할 것이므로(새 이벤트, 새 미터 등) 모든 미터 변경이 접근 제어를 위험에 빠뜨리지 않도록 해야 한다.
하드 리밋, 소프트 리밋, 오버에이지 과금은 UI 상으로 비슷해 보일 수 있지만 동작은 매우 다르다:
- 하드 리밋은 상한 이후 동작을 차단한다.
- 소프트 리밋은 동작을 허용하지만 경고하고 플래그를 남긴다.
- 오버에이지 과금은 동작을 허용하고 초과분을 과금한다.
- 유예 기간(grace period)은 하드 리밋을 일시적으로 소프트 리밋처럼 취급한다.
- 체험판과 무료 티어는 엔타이틀먼트를 적용하되 가격은 (일반적으로) 특정 날짜나 임계치까지 0으로 처리한다.
한도가 초과되었을 때 어떤 일이 일어나는지 미리 결정하라. 예: "Starter" 티어는 5좌석과 월 10,000 API 호출을 포함한다. 6번째 사용자를 초대하면 차단할지, 초과 좌석당 과금을 시작할지, 아니면 7일의 유예 기간을 줄지 결정해야 한다. 각 선택은 인보이스와 지원 로그에서 보여줄 수 있는 규칙이어야 한다.
엔타이틀먼트를 시간 범위가 있는 기록으로 저장하라: 고객, 플랜 또는 애드온, 시작/종료 타임스탬프, 한도 값, 집행 모드(하드/소프트/오버에이지). 이렇게 하면 접근 결정과 청구 결정이 일관되게 유지된다.
감사 가능한 인보이스와 청구 기간
인보이스는 단지 PDF가 아니다. 누가, 무엇에 대해, 어떤 날짜 동안, 어떤 규칙 하에서 청구되었는지를 답해주는 감사 추적이다. 가격을 변경해도 예전 인보이스를 정확히 재생할 수 있어야 한다.
기본 레코드부터 시작하라: 고객, 구독(또는 계약), 청구 기간, 라인 아이템을 가진 인보이스. 각 라인 아이템은 출처(plan fee, usage summary, one-time charge)를 가리켜야 한다. 그 연결 고리가 분쟁 시 청구를 설명 가능하게 만든다.
청구 기간에는 앵커와 시간대가 필요하다. 단순히 "월별"은 부족하다. 사이클 앵커 날짜(예: 15일 00:00)와 기간을 구분하는 데 사용된 시간대를 저장하라. 일관성을 유지하지 않으면 서머타임 전후에 하루 차이가 발생할 수 있다.
감사 가능한 인보이스는 보통 다음을 필요로 한다:
- 각 인보이스와 라인 아이템에 period_start와 period_end
- 해당 인보이스에 사용된 가격 버전(플랜/가격 ID)
- 변경 불가능한 총액: 소계, 세금, 할인, 납부액, 통화
- 사용량 기반 라인 아이템의 정확한 사용 윈도우
- 해당되는 경우 외부 결제 참조(결제 프로세서의 청구 ID)
수익 인식은 청구와 관련되지만 동일하지는 않다. 선불 구독 수수료는 보통 서비스 기간에 걸쳐 인식되고, 사용량은 제공 시점에 인식되는 경우가 많다. 나중에 이를 지원할 수 있도록 충분한 날짜를 저장하라.
신용노트, 환불, 조정은 옛 인보이스를 수정하지 말고 1등급 레코드로 다뤄라. 고객이 중간 주기에 업그레이드하면 원래 인보이스를 참조하고 사용된 비례배분 규칙을 명시하는 조정 라인이나 크레딧 노트를 생성하라.
인보이스 생성과 결제 시도에는 멱등성(idempotency) 키가 중요하다. 작업이 두 번 실행되면 인보이스나 결제가 두 번 생성되지 않고 명확한 로그가 남아야 한다.
비례배분과 중간 주기 변경
중간 주기 변경은 분쟁이 시작되는 지점이다. 누군가가 20일에 업그레이드하고 일주일 동안 일시 중지했다가 취소하면 이러한 행동을 인보이스의 합리적인 숫자로 바꿀 규칙이 필요하다.
어떤 변경을 허용하고 언제 적용할지 결정하라. 많은 팀은 업그레이드는 즉시 적용해 고객이 바로 가치를 얻도록 하고, 다운그레이드는 갱신 시점에 적용해 복잡한 환불을 피한다.
설명 가능한 비례배분 정책을 선택하라
비례배분은 일별, 시간별, 또는 전혀 하지 않는 방식일 수 있다. 더 정밀할수록 더 많은 타임스탬프, 반올림 규칙, 엣지 케이스를 저장하고 테스트해야 한다.
정책 선택을 일찍 고정하라:
- 업그레이드는 즉시 비례배분, 다운그레이드는 갱신 시 적용
- 일별 비례배분(시간별보다 단순하고 보통 충분히 공정함)
- 반올림 규칙 정의(예: 센트 단위로 반올림)
- 일시중지 동작 결정(시간을 크레딧으로 환산할지, 기간을 연장할지)
- 취소 시 환불 정책(전액, 일부, 없음)
비례배분을 인보이스 라인 항목으로 모델링하라
숨겨진 계산을 피하라. 비례배분은 인보이스 상의 명시적 조정으로 표현하라: 새 플랜의 남은 시간에 대한 차변(debit)과 이전 플랜의 사용하지 않은 시간에 대한 크레딧(credit)처럼. 각 라인 항목은 이를 발생시킨 정확한 변경 이벤트(change_id)를 가리키고 비례배분 윈도우(start_at, end_at), 수량/시간 기준, 세금 카테고리를 포함해야 한다.
플랜 변경 시 미터가 리셋되는지, 누적되는지, 세그먼트로 나뉘는지도 결정해야 한다. 단순하고 감사 가능한 방법은 플랜 버전별로 사용량을 세그먼트하는 것이다. 예: 한 고객이 월 중간에 10좌석에서 25좌석으로 업그레이드하면 사용 이벤트는 그대로 두고, 평가(rating)는 이벤트 발생 시점의 엔타이틀먼트 기간별로 그룹화한다.
무엇을 되돌릴 수 있는지도 정하라. 사용 이벤트는 수용된 이후에는 최종으로 취급하고, 구독 변경은 인보이스가 확정되기 전까지 되돌릴 수 있게 하는 것이 도움이 된다. 변경 이벤트와 인보이스 조정 기록을 저장해 규칙이 변경될 때도 인보이스를 깔끔하게 재생할 수 있게 하라.
첫날부터 저장해야 할 데이터
고객이 불평한 뒤에 청구 데이터를 설계하면 결국 추측으로 끝난다. 구독, 사용량, 또는 하이브리드 B2B SaaS 모델 중 무엇을 선택하든, 나중에 항상 감사할 수 있는 소규모 레코드 집합으로 시작하는 것이 안전하다.
명확한 고객 계층 구조부터 시작하라. B2B SaaS에서는 지불 주체가 보통 회사 계정이지만 사용은 워크스페이스나 프로젝트 내부에서 발생하고 액션은 개별 사용자로부터 온다. 계정, 워크스페이스, 사용자 세 레벨을 모두 저장하고 누가 무엇을 했는지 기록하라. 청구 분쟁은 종종 "어떤 팀이 이걸 했나?"로 귀결된다.
실제 인보이스와 깔끔한 조사 지원을 위한 최소 청구 데이터베이스 설계:
- 계정 및 조직 구조: 계정, 워크스페이스(또는 프로젝트), 사용자, 역할, 청구 담당자, 필요하면 세금 필드
- 구독: 플랜, 상태, 시작/종료일, 갱신 설정, 취소 이유, 시작 시 적용된 가격 버전
- 가격 카탈로그: 제품, 플랜 구성 요소(기본 요금, 좌석, 미터), 계층, 통화, 유효일
- 사용량 미터링 데이터: 타임스탬프, 워크스페이스, 사용자(가능하면), 미터 이름, 수량, 고유 중복 방지 키를 가진 불변의 추가 전용(append-only) 이벤트 로그
- 인보이스 아티팩트: 청구 기간 경계, 라인 아이템, 총액, 세금/할인 조정, 사용된 입력값의 스냅샷
속도를 위해 집계 카운터에만 의존하지 마라. 속도를 위해 카운터를 유지할 수 있지만 이벤트 로그를 진실의 원천(source of truth)으로 취급하라. 간단한 규칙: 이벤트는 불변(immutable)이다. 수정은 새로운 이벤트(예: 음수 수량)로 처리하고, 모든 이벤트는 특정 미터 정의에 연결되어야 한다.
예: 고객이 지난달에 "API 호출"이 두 배로 늘어났다고 말하면, 워크스페이스별 일자별 원시 이벤트를 꺼내 어디서 스파이크가 났는지 보여주거나 통합 루프를 찾아낼 수 있어야 한다.
단계별: 사용 이벤트에서 인보이스까지
어려운 부분은 수학이 아니다. 어려운 부분은 제품과 가격이 바뀐 뒤에도 몇 달 후에 결과를 설명할 수 있게 만드는 것이다.
1) 타임트래블 가능한 가격 카탈로그로 시작하라
제품, 플랜, 미터, 그리고 유효 일자를 가진 가격 및 정책을 설정하라. 옛 가격을 덮어쓰지 마라. 고객이 3월에 청구되었다면 3월에는 3월 카탈로그를 사용해 재실행할 수 있어야 한다.
예: "API Calls"가 4월 1일부터 개당 $0.002라면 3월 인보이스는 이전 요율을 사용해야 한다.
2) 해당 기간 동안 고객이 어떤 권한을 가졌는지 스냅샷을 남겨라
각 청구 기간 시작 시(또는 변경이 발생할 때) 엔타이틀먼트 스냅샷을 저장하라: 플랜, 포함된 단위, 한도, 계층 규칙, 할인, 세금 설정 등. 이를 그 기간 동안 "우리가 약속한 것"으로 생각하라.
3) 사용 이벤트를 수집하고 초기에 검증하라
사용량은 불변 이벤트로 들어와야 한다: 타임스탬프, 고객, 미터, 수량, 중복 제거용 고유 ID. 도착 시 기본 검증을 수행하라(미터 누락, 음수 수량, 불가능한 타임스탬프 등)하고, 정리된 버전이 있더라도 원시 이벤트는 기록하라.
그다음 계산은 두 단계로 수행하라:
- 이벤트를 고객/미터/청구 기간별 합계로 집계(그리고 집계 버전을 기록)
- 카탈로그와 엔타이틀먼트 스냅샷을 사용해 합계를 요금으로 환산(포함 단위, 초과 요금, 계층 적용)
인보이스 라인 아이템은 카탈로그 버전, 스냅샷 ID, 집계 ID 같은 정확한 입력을 참조해야 한다.
마지막으로 인보이스를 잠그라. 계산 입력과 출력을 저장하고 확정 표시를 해 두어 늦게 들어온 이벤트가 인보이스를 변경하지 못하게 하라. 늦게 들어온 이벤트는 다음 인보이스나 별도 조정으로 가야 하며 명확한 감사 메모가 포함되어야 한다.
고객 및 내부 보고 필요사항
고객은 당신이 구독형을 선택했는지 사용량 기반을 선택했는지 신경 쓰지 않는다. 그들이 신경 쓰는 것은 당신의 수치가 그들의 수치와 일치하고 다음 청구를 예측할 수 있는지다.
고객용 보고는 지원 티켓 없이 몇 가지 질문에 답하게 단순화하는 것이 가장 좋다:
- 나는 어떤 플랜인가, 그리고 무엇이 포함되어 있는가?
- 이번 기간에 얼마를 사용했나, 사용량은 어떻게 계산되는가?
- 내 한도는 무엇이며, 초과하면 무슨 일이 발생하나?
- 청구 기간은 언제 끝나며 예상 다음 청구액은 얼마인가?
- 과거 인보이스와 결제 내역은 어디서 볼 수 있나?
내부적으로는 지원과 재무가 단순히 숫자를 보여주는 것이 아니라 그 숫자를 설명할 수 있어야 한다. 스파이크를 찾아내고 변경을 추적하며 미리보기 수치와 확정된 청구를 구분할 수 있어야 한다.
청구 미리보기와 인보이스를 분리하라. 미리보기는 늦게 들어오는 사용량이나 미터 정의 정밀화에 따라 재계산될 수 있지만 인보이스는 그래서는 안 된다.
내부 보고는 이상치(갑작스런 사용량 급증, 음수 사용량, 중복 이벤트)를 플래그하고, 원시 사용량과 규칙으로부터 인보이스 라인 항목을 재생성하며, 해당 기간의 플랜 변경 및 비례배분 규칙을 쉽게 볼 수 있도록 해야 한다.
감사 추적은 대부분의 팀이 생각하는 것보다 더 중요하다. 고객이 "우리는 12일에 업그레이드했다"고 말하면 타임스탬프, 행위자(사용자, 관리자, 자동화), 그리고 플랜/좌석/한도/미터 요율의 정확한 이전/이후 값을 보여줘야 한다.
보존 관점에서 원시 사용 이벤트와 확정된 인보이스 아티팩트를 장기 보관하라. 실용적 규칙: 요금에 영향을 주거나 분쟁에서 방어하는 데 도움이 될 수 있는 항목은 불변으로 저장하라.
분쟁을 일으키는 흔한 함정들
대부분의 청구 분쟁은 가격 자체 때문이 아니다. 인보이스의 숫자를 설명할 수 없거나 동일한 입력으로 나중에 다른 합계가 나오는 경우에 발생한다.
흔한 실수는 월별 합계만 보관하는 것이다. 원시 이벤트가 없으면 "어느 날이 한도를 넘겼는가?" 같은 간단한 질문에도 답할 수 없다. 이벤트를 보관한 다음 집계하라.
또 다른 빈번한 문제는 미터의 의미를 추적하지 않는 채로 변경하는 것이다. "활성 사용자"가 처음에는 "한 번이라도 로그인한 사용자"였다가 나중에 "레코드를 생성한 사용자"가 될 수 있다. 미터 정의에 버전 관리를 적용하지 않으면 고객은 옛 인보이스와 새 인보이스를 비교하며 어떤 규칙이 적용됐는지 증명할 수 없게 된다.
분쟁은 보통 다음 패턴에서 발생한다:
- 엔타이틀먼트가 UI에서만 강제되고 백엔드는 여전히 추가 사용을 허용하거나 유효한 사용을 차단함
- 모든 용도로 하나의 타임스탬프 필드(이벤트 시간, 수집 시간, 청구 기간 시간)를 사용함
- 시간대를 무시함(현지 시간 00:30에 발생한 사용이 잘못된 일/월로 들어감)
- 청구 앵커를 잊음(어떤 고객은 1일에 청구하고 다른 고객은 가입일 기준으로 청구)
- 플랜, 가격, 한도 변경에 대한 감사 추적이 없음
예: 도쿄에 있는 고객이 현지 시간으로 31일에 업그레이드했다. UTC 타임스탬프만 저장하고 청구 앵커를 저장하지 않으면 업그레이드가 시스템에서는 30일로 보일 수 있어 비례배분과 사용량이 잘못된 기간으로 넘어갈 수 있다.
신뢰를 잃는 가장 빠른 방법은 옛 인보이스 계산을 재현할 수 없게 만드는 것이다. 입력(이벤트, 버전, 앵커, 적용된 가격)을 저장해 나중에 같은 로직을 다시 실행하면 같은 결과가 나오도록 하라.
출시 전 빠른 점검 목록
출시 전에 한 번 연습해 보라: 임의의 고객을 골라 저장된 데이터만으로 마지막 인보이스를 재생성해 보라. "그때 코드가 뭘 했는지"가 필요하면 감사 추적이 없는 것이다.
모든 미터가 모호하지 않은지 확인하라. 미터는 명확한 단위(요청, 좌석, GB, 분), 신뢰할 수 있는 출처(어떤 서비스가 이벤트를 발행하는가), 시간 창(일별, 청구 기간별, 달력 월별)을 필요로 한다. 미터를 한 문장으로 설명할 수 없다면 고객은 신뢰하지 않을 것이다.
대부분의 청구 문제를 잡아내는 빠른 점검:
- 입력값(플랜 버전, 가격, 사용 합계, 세금, 할인, 비례배분 파라미터)만으로 인보이스를 재생할 수 있다
- 사용 이벤트는 추가 전용(append-only), 불변(immutable), 중복 제거(idempotency key 또는 이벤트 id)
- 플랜과 가격 변경은 유효일을 가진 버전으로 관리(옛 가격 덮어쓰기 금지)
- 비례배분 규칙은 평문으로 작성되어 있고 테스트로 보장됨
- 지원팀이 저장된 동일한 사실을 사용해 "왜 청구됐나"에 대해 빠르게 답할 수 있다
예: 고객이 18일에 10좌석에서 25좌석으로 업그레이드하고 23일에 사용 한계치를 넘겼다면 시스템은 (1) 각 날짜에 어떤 플랜 버전이 활성화됐는지, (2) 좌석 변경 이벤트의 타임스탬프, (3) 사용한 비례배분 공식, (4) 최종 합계에 롤업된 사용 이벤트를 보여줄 수 있어야 한다.
다음 단계: 스스로를 궁지에 몰아넣지 않고 구현하기
작게 시작하되 모호하게 시작하지 마라. 가장 안전한 경로는 몇 달 후에도 "왜 이 금액을 청구했나?"라는 질문에 답해줄 수 있는 최소한의 데이터 모델이다.
출시 전에 청구 스키마와 관리자 화면을 빠르게 프로토타입하라. 첫 가격 변경 전에 해두지 않으면 영업에서 새 티어나 중간 주기 업그레이드를 요구할 때 논리가 여러 곳에 패치되는 결과가 난다.
실용적인 분리 방법은: Stripe에게 결제, 영수증, 결제 재시도를 맡기되, 사용량을 라인 아이템으로 바꾸는 평점(rating)은 자체 시스템에서 관리하는 것이다. 즉 원시 사용 이벤트, 가격 버전, 인보이스 미리보기에 사용된 계산 결과를 자체 DB에 저장한다.
유연성을 유지하는 간단한 출시 계획:
- 신뢰할 수 있게 측정할 수 있는 미터 1~2개 선택(예: 일별 활성 좌석, API 호출)
- 처음부터 가격 규칙에 버전 관리를 적용해 옛 인보이스가 옛 규칙과 일치하도록 함
- 사용량, 오버라이드, 크레딧, 분쟁을 볼 수 있는 내부 청구 관리자 패널 구축
- 고객 포털에 현재 플랜, 이번 기간 사용량, 예상 청구액 기본 보기 추가
- 모든 인보이스를 재계산 가능한 추측이 아닌 감사 가능한 스냅샷으로 취급
많은 커스텀 백오피스 코드를 쓰지 않고 빠르게 진행하려면 AppMaster에 엔티티를 모델링하고 시각적 도구로 관리자와 포털 화면을 빌드하되, 이벤트와 인보이스의 시스템 오브 레코드로 PostgreSQL을 유지할 수 있다.
구체적 예: 좌석 미터 하나로 출시한다. 세 달 뒤 저장소 GB 미터를 추가한다. 미터, 엔타이틀먼트, 가격 규칙에 버전 관리가 되어 있다면 새 미터를 도입해도 옛 인보이스를 깨지 않고 지원팀이 몇 분 안에 라인 항목을 설명할 수 있다.
자주 묻는 질문
요금을 청구한 이유를 나중에 증명하려면 무엇을 저장해야 하는지부터 결정하세요. 구독형은 플랜, 청구 기간, 권한(엔타이틀먼트) 기록이 필요하고, 사용량 기반은 언제 무엇이 발생했는지와 어떤 가격 규칙이 적용됐는지에 대한 불변 기록이 필요합니다.
저장된 입력값으로 인보이스를 재생할 수 없다면 분쟁이나 감사를 신뢰할 수 없습니다. 가장 안전한 설정은 기간이 지정된 플랜 조건, 버전 관리된 가격, 원시 사용 이벤트, 그리고 인보이스가 확정될 때 사용한 정확한 계산 결과를 저장하는 것입니다.
좋은 미터는 두 사람이 같은 방식으로 셀 수 있는 항목입니다. 이벤트, 단위, 집계 시간 창을 정의하고 재시도, 실패 응답, 내부 호출 등 무엇이 포함되는지 명확히 적어 두세요. 의미를 중간에 바꾸지 않는 것이 핵심입니다.
하드 리밋은 동작을 차단하고, 소프트 리밋은 경고만 하며 허용하고, 오버에이지 과금은 허용하고 추가 요금을 청구합니다. 각 권한(엔타이틀먼트)에 대해 한 가지 동작을 선택하고 시작/종료 타임스탬프와 함께 규칙으로 저장하세요.
엔타이틀먼트는 고객이 무엇을 할 수 있는지를 통제하고, 미터링은 실제로 무슨 일이 일어났는지를 기록합니다. 둘을 분리하면 미터 정의 변경으로 접근 제어가 깨지는 일을 방지할 수 있고, 인보이스 설명도 쉬워집니다.
성능을 위해 집계(counter)를 둘 수는 있지만, 출처는 이벤트 로그인 append-only 원시 이벤트가 되어야 합니다. 이벤트에는 타임스탬프, 청구 범위(예: 워크스페이스), 미터 이름, 수량, 중복 방지용 고유 id가 포함되어야 합니다.
기존 가격이나 플랜 규칙을 덮어쓰지 말고 유효 시작일을 갖는 새 버전을 추가하세요. 각 인보이스(또는 엔타이틀먼트 기간)에 어떤 가격 버전이 적용됐는지 저장하면 과거 인보이스를 정확히 재생할 수 있습니다.
월별 청구에는 단순히 “월별”이라고만 저장하지 말고 사이클 앵커 날짜와 사용할 시간대(timezone)를 저장하세요. 이벤트 시간과 수집 시간을 구분해서 사용하지 않으면 서머타임이나 로컬 날짜 경계에서 오차가 생깁니다.
한 문장으로 설명할 수 있는 정책을 선택하고, 계산은 인보이스의 명시적 라인 항목(크레딧/차변)으로 표현하세요. 하루 단위 비례배분(daily proration)은 시간 단위보다 테스트와 설명이 더 쉽습니다.
인보이스를 불변 스냅샷으로 확정하고 늦게 들어온 사용량은 다음 기간이나 별도 조정으로 처리하세요. 미리보기(preview)는 자유롭게 재계산할 수 있지만, 확정된 인보이스는 변경해서는 안 됩니다.


