SaaS 사용량 집계 기능을 서비스 기능과 분리해 설계하는 이유 관련 이미지

SaaS 데이터 설계

SaaS 사용량 집계 기능 분리 설계 이유와 구현 체크리스트

발행

사용량 집계 기능을 서비스 기능 안에 밀어 넣으면 한 번의 누락이나 중복이 업무 처리와 과금 판단을 함께 흔듭니다. 핵심 서비스와 집계 경계를 나누면 요청 처리는 계속하고, 사용량 기록은 재처리하고 검증할 수 있는 흐름으로 운영할 수 있습니다. AI 에이전트가 1회 작업을 실행할 때마다 어떤 사건을 몇 단위로 셀지 먼저 정해야 합니다. 이 기준은 B2B SaaS의 요금제 계산을 같은 원본 기록에서 파생하는 출발점입니다. 한도 알림과 비용 분석은 그 결과를 읽는 별도 기능으로 둡니다.

서비스 기능과 사용량 계측의 경계

서비스 기능은 사용자의 요청을 처리합니다. 사용량 집계는 그 처리 과정에서 발생한 사건을 셉니다. 두 영역의 경계를 먼저 나눕니다. 예를 들어 AI 에이전트 작업 1건이 성공하면 서비스 테이블에는 작업 상태를 저장하고 집계 원장에는 tenant_id·usage_type·quantity·occurred_at·idempotency_key를 기록해 두 경로가 서로의 저장 성공 여부에 종속되지 않게 만듭니다. 원장에는 실제 사용량을 계산할 재료를 남기고 화면이나 청구서용 숫자는 별도 집계 결과로 파생시키면 기능 코드와 측정 규칙을 독립적으로 바꿀 수 있습니다.

장애 범위와 재처리 경계

분리 설계는 장애 경계부터 나눕니다. 집계 저장소가 지연되어도 AI 에이전트의 작업 실행 결과를 먼저 반환할 수 있고 누락된 이벤트는 2차 처리 큐에서 다시 계산할 수 있습니다. 운영 흐름이 달라집니다. 서비스 요청은 낮은 지연 시간을 우선하고 집계 작업은 중복 제거와 순서 보정을 우선합니다. 두 기능을 한 트랜잭션에 묶으면 집계 실패가 사용자 요청 실패로 번지지만 분리하면 usage_pending 같은 상태를 기록한 뒤 재처리 대상으로 남길 수 있습니다. 원본 이벤트의 보존 기간과 재처리 권한을 한 문서로 관리해야 수치가 바뀐 이유를 추적할 수 있습니다.

원본 이벤트와 집계표의 역할

원본 이벤트와 집계표는 역할이 다릅니다. 이벤트 원장은 append-only로 두며 일별 사용량은 날짜별 행으로 계산하고 월별 청구 수치는 그 결과를 다시 합산합니다. 운영 기준은 중복과 지연부터 정합니다. 동일한 idempotency_key가 2번 들어와도 한 번만 반영합니다. 발생 시각과 수집 시각을 구분해 늦게 도착한 기록이 어느 집계 구간에 들어갈지 판정합니다. 집계 버전과 계산 시각도 남깁니다. 원본 100건 중 1건이 늦게 들어온 상황에서도 해당 날짜의 원장만 다시 읽어 집계합니다. 이미 처리한 다른 날짜의 서비스 로직까지 다시 실행하지 않는 장치입니다.

서비스 요청에서 나온 사용량 이벤트가 원본 원장에 쌓인 뒤 날짜별 집계표로 변환되는 흐름을 개발자가 비교합니다. 중복 기록과 늦게 도착한 데이터의 처리 기준도 함께 확인합니다.

설계 순서와 검증 기준

설계는 데이터 정의에서 시작합니다. 1단계에서 과금 단위가 작업 수인지 토큰 수인지 정하고 한 이벤트가 몇 단위를 만드는지 문장으로 고정합니다. 2단계에서는 성공 상태만 집계하고 실패·취소는 별도 상태로 남깁니다. 3단계에서는 원장 키, 테넌트 식별자, 발생 시각, 계산 단위를 필수 입력으로 정합니다. 4단계에서는 중복 이벤트와 늦은 도착을 넣은 테스트를 실행하고 원본 수와 집계 수를 대조합니다. 테스트 결과가 1건이라도 어긋나면 화면 숫자를 먼저 고치지 말고 원장부터 추적해야 재발 원인을 찾을 수 있습니다.

AI 에이전트 사용량 사례

가정 사례입니다. AI 에이전트가 한 계정에서 1회 작업을 실행하고 작업 결과를 기준으로 요금 단위를 계산한다고 하겠습니다. 서비스 DB에는 실행 상태와 결과를 저장하고 사용량 원장에는 작업 ID를 남깁니다. 계정 식별자와 계산 단위도 같은 이벤트에 붙입니다. 이 수치는 실제 고객 성과가 아닙니다. 하루 집계가 24건인데 원장에는 25건이 있다면 화면을 고치는 대신 누락된 이벤트의 전송 실패인지 중복 제거 규칙 때문인지부터 구분합니다. 과금 정책을 바꾸는 경우에도 기존 원장의 단위를 보존한 채 새 계산 규칙을 별도 버전으로 적용해야 과거 청구와 새 정책을 같은 기준에서 비교할 수 있습니다.

결합 설계에서 생기는 오류

초기 결합 설계는 단순합니다. 서비스 요청이 성공할 때 같은 함수에서 사용량을 1씩 올리면 코드가 짧아지지만 재시도와 장애 복구가 시작되는 순간 같은 작업이 2번 집계될 위험이 커집니다. 운영 부채가 됩니다. 사용량 원장을 현재값으로만 두고 수정 이력을 남기지 않는 방식도 문제를 키웁니다. 집계 구간의 수치가 바뀌었을 때 어떤 규칙 버전과 처리 주체가 값을 바꿨는지 남지 않기 때문입니다. 원본 이벤트는 보존하고 집계는 재실행할 수 있어야 하며 보정 기록에는 작성 주체와 규칙 버전을 남겨야 합니다.

핵심 요약

  • 핵심 서비스는 요청 처리에 집중하고 사용량 원장은 계측과 재처리를 맡겨 장애 범위를 나눕니다.
  • 원본 이벤트를 기준으로 일별 집계와 청구용 수치를 파생시키면 숫자의 출처를 따라갈 수 있습니다.
  • 중복·지연·부분 실패를 설계 단계부터 시험해야 AI 에이전트 사용량과 과금 수치의 어긋남을 줄일 수 있습니다.
  • 정책 변경은 원본 단위를 보존하고 계산 규칙 버전을 나눠 기록해야 기존 결과와 새 결과를 비교할 수 있습니다.

자주 묻는 질문

사용량 집계 기능을 서비스 기능과 꼭 분리해야 하나요?

1개 코드베이스 안에서도 서비스 모듈과 사용량 모듈을 분리합니다. 이벤트 원장은 별도로 저장하고 집계 작업은 별도 실행합니다. 청구 계산은 집계 결과를 읽도록 해 요청 처리 실패와 측정 실패를 다른 상태로 구분합니다.

사용량 집계에 필요한 최소 데이터는 무엇인가요?

최소 항목은 테넌트 식별자와 이벤트 식별자입니다. 사용량 종류와 계산 단위를 이벤트에 붙입니다. 발생 시각은 별도 필드로 두고 재시도 중복을 막을 idempotency_key를 추가합니다.

늦게 도착한 이벤트는 어떻게 처리하나요?

발생 시각과 수집 시각을 분리합니다. 집계 기준은 발생 시각으로 잡고 허용 범위를 벗어난 이벤트는 보정 큐에 넣어 해당 구간만 다시 계산합니다. 원본 이벤트를 덮어쓰지 않아야 재집계 전후의 차이를 설명할 수 있습니다.

사용량 수치를 과금에 바로 연결해도 되나요?

원장 기록과 집계 결과가 일치하는지 확인한 뒤 청구 계산에 연결합니다. 정책 버전이 바뀌면 같은 원본을 새 규칙으로 재계산해 기존 청구와 구분합니다. AI 에이전트 작업처럼 재시도가 잦은 기능은 중복 제거 결과를 통과한 수치만 과금 입력으로 사용합니다.

Monetai · calendly.com