Files
sam-docs/plans/usage-subscription-unification.md
김보곤 6072dde239 docs: [plans] 이용현황 통합 계획에 과금정책 반영
- customer-pricing.md 6장 기준 확정: AI 토큰 월 100만, 저장공간 100GB
- 한도 초과 시 실비 과금, 80%/100% 알림 정책 반영
- API 응답에 limit, percentage, warning_threshold 추가
2026-03-18 12:13:31 +09:00

12 KiB

이용현황 + 구독관리 통합 개선 계획

작성일: 2026-03-18 상태: 계획 수립


1. 개요

1.1 목적

React 서비스의 이용현황(/usage)과 구독관리(/subscription) 메뉴를 하나로 통합하고, 현재 mock/하드코딩 데이터를 실제 DB 기반으로 개선한다.

1.2 배경

  • /usage 페이지는 EmptyPage(미구현)
  • /subscription 페이지는 구현되어 있으나, 일부 데이터가 하드코딩(API 호출 상한 10,000)
  • 사용자가 원하는 핵심 정보: AI 토큰 사용량 + 서버 저장공간
  • 메뉴가 분산되어 있어 하나로 통합

1.3 핵심 원칙

✅ 하나의 메뉴(이용현황)에 구독 + 사용량 통합 표시
✅ 모든 데이터는 실제 DB에서 조회
✅ AI 토큰 사용량은 ai_token_usages 테이블 기반
✅ 저장공간은 tenant.storage_used/limit 기반
❌ 하드코딩된 상한값 제거
❌ 별도 구독관리 메뉴 유지 (통합)

2. 현재 상태 분석

2.1 기존 데이터 흐름

React (/subscription)
    ↓
GET /api/v1/subscriptions/current   → subscriptions 테이블 (실제 데이터)
GET /api/v1/subscriptions/usage     → 혼합 (실제 + 하드코딩)
    ↓
SubscriptionManagement 컴포넌트 렌더링

2.2 현재 usage API 반환 항목

항목 데이터 소스 실제/Mock 문제점
사용자 수 tenant.users.count() / tenant.max_users 실제 max_users 미설정 시 0
저장공간 tenant.storage_used / tenant.storage_limit 실제 파일 업로드 시 자동 갱신됨
API 호출 수 ApiRequestLog count (일간) / 10,000 반만 실제 상한 10,000 하드코딩, 1일만 보관
구독 정보 subscriptions 테이블 실제 plan 연결 구조만 존재

2.3 AI 토큰 추적 현황

항목 상태 위치
ai_token_usages 테이블 존재 API 마이그레이션
AI 호출 시 기록 동작 중 MNG에서 AI 기능 사용 시 자동 기록
토큰별 비용 계산 동작 중 ai_pricing_configs 테이블 기반
서비스(React) API 제공 없음 API 프로젝트에 엔드포인트 미구현

2.4 기존 테이블 구조

tenants
├── storage_used (bigint, bytes)     ← 실제 추적 중
├── storage_limit (bigint, bytes)    ← 기본 10GB
├── max_users (int, nullable)
│
├── subscriptions (1:N)
│   ├── plan (varchar 50, 'Business' 등)
│   ├── monthly_fee (bigint)
│   ├── start_date / next_billing
│   └── status (active/cancelled/...)
│
└── ai_token_usages (1:N)
    ├── model (varchar, 'gemini-2.0-flash' 등)
    ├── menu_name (varchar, 기능명)
    ├── prompt_tokens / completion_tokens / total_tokens
    ├── cost_usd / cost_krw
    └── created_at

ai_pricing_configs
├── provider (gemini/claude/google-stt/google-gcs)
├── model_name
├── input_price_per_million / output_price_per_million
└── exchange_rate

3. 목표 설계

3.1 과금 정책 기준 (customer-pricing.md 6장)

출처: rules/customer-pricing.md — 사용량 기반 추가 과금

항목 기본 제공 초과 과금 비고
파일 저장 공간 100GB 100GB당 5만원/월 tenant.storage_limit 기본값 반영
AI 토큰 월 100만 토큰 1,000토큰 단위 실비 과금 매월 1일 리셋, 이월 없음

알림 정책:

  • 기본 제공량의 80% 소진 시 → 경고 알림
  • 기본 제공량의 100% 소진 시 → 한도 초과 알림
  • 미사용 잔여 토큰은 이월되지 않음 (매월 1일 갱신)

3.2 통합 페이지 구성

이용현황 (/usage)
┌─────────────────────────────────────────────────┐
│                                                  │
│  [1] 구독 정보 카드                              │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐         │
│  │ 요금제    │ │ 구독상태  │ │ 다음결제  │         │
│  │ Business │ │ 활성     │ │ 04-01    │         │
│  └──────────┘ └──────────┘ └──────────┘         │
│                                                  │
│  [2] 리소스 사용량                               │
│  ┌──────────────────────────────────────┐        │
│  │ 사용자    ████████░░  5 / 10명      │        │
│  │ 저장공간  ███░░░░░░░  1.2GB / 100GB │        │
│  └──────────────────────────────────────┘        │
│                                                  │
│  [3] AI 토큰 사용량 (2026년 3월)                 │
│  ┌──────────────────────────────────────┐        │
│  │ ██████░░░░  620,000 / 1,000,000     │        │
│  │ 총 비용    ₩1,234                    │        │
│  │ 모델별: Gemini 80% | Claude 20%      │        │
│  │ ⚠️ 80% 소진 시 경고, 100% 초과 시 실비│       │
│  │ [상세 내역 보기 →]                    │        │
│  └──────────────────────────────────────┘        │
│                                                  │
│  [4] 서비스 관리 버튼                            │
│  [ 자료 내보내기 ]  [ 서비스 해지 ]              │
│                                                  │
└─────────────────────────────────────────────────┘

3.3 통합 API 설계

기존 /subscriptions/usage API를 확장하여 AI 토큰 정보를 포함한다.

GET /api/v1/subscriptions/usage (개선)

{
  "users": {
    "used": 5,
    "limit": 10,
    "percentage": 50.0
  },
  "storage": {
    "used": 1288490189,
    "used_formatted": "1.2 GB",
    "limit": 107374182400,
    "limit_formatted": "100 GB",
    "percentage": 1.2
  },
  "ai_tokens": {
    "period": "2026-03",
    "total_requests": 156,
    "total_tokens": 620000,
    "prompt_tokens": 412000,
    "completion_tokens": 208000,
    "limit": 1000000,
    "percentage": 62.0,
    "cost_usd": 0.95,
    "cost_krw": 1234,
    "warning_threshold": 80,
    "is_over_limit": false,
    "by_model": [
      {
        "model": "gemini-2.0-flash",
        "requests": 120,
        "total_tokens": 496000,
        "cost_krw": 987
      },
      {
        "model": "claude-3-haiku",
        "requests": 36,
        "total_tokens": 124000,
        "cost_krw": 247
      }
    ]
  },
  "subscription": {
    "plan": "Business",
    "monthly_fee": 50000,
    "status": "active",
    "start_date": "2026-01-01",
    "next_billing": "2026-04-01",
    "remaining_days": 14
  }
}

변경점:

  • api_calls 섹션 → ai_tokens 섹션으로 교체
  • ai_tokens.limit: 월 100만 토큰 (정책 기반)
  • ai_tokens.percentage: 한도 대비 사용율
  • ai_tokens.warning_threshold: 경고 기준 80%
  • ai_tokens.is_over_limit: 한도 초과 여부
  • ai_tokens.cost_krw: 초과 시 실비 과금 금액 계산에 활용
  • storage.limit: 100GB (정책 기본값)
  • subscription.monthly_fee 추가
  • 하드코딩 제거 (10,000 상한 등)

4. 작업 범위

4.1 API 프로젝트 (sam/api)

작업 파일 설명
R1 SubscriptionService.php usage() 메서드에 AI 토큰 집계 추가
R2 SubscriptionService.php api_calls 섹션을 ai_tokens로 교체
R3 SubscriptionService.php subscription 섹션에 monthly_fee 추가
R4 SubscriptionController.php 변경 없음 (Service만 수정)

R1 상세: AI 토큰 집계 쿼리

// SubscriptionService::usage() 내부
$currentMonth = now()->format('Y-m');

$aiUsage = AiTokenUsage::where('tenant_id', $tenantId)
    ->whereRaw("DATE_FORMAT(created_at, '%Y-%m') = ?", [$currentMonth])
    ->selectRaw("
        COUNT(*) as total_requests,
        COALESCE(SUM(prompt_tokens), 0) as prompt_tokens,
        COALESCE(SUM(completion_tokens), 0) as completion_tokens,
        COALESCE(SUM(total_tokens), 0) as total_tokens,
        COALESCE(SUM(cost_usd), 0) as cost_usd,
        COALESCE(SUM(cost_krw), 0) as cost_krw
    ")
    ->first();

$aiByModel = AiTokenUsage::where('tenant_id', $tenantId)
    ->whereRaw("DATE_FORMAT(created_at, '%Y-%m') = ?", [$currentMonth])
    ->selectRaw("
        model,
        COUNT(*) as requests,
        COALESCE(SUM(total_tokens), 0) as total_tokens,
        COALESCE(SUM(cost_krw), 0) as cost_krw
    ")
    ->groupBy('model')
    ->orderByDesc('total_tokens')
    ->get();

4.2 React 프로젝트 (sam/react)

작업 파일 설명
R5 types.ts UsageApiData 타입에 ai_tokens 추가
R6 actions.ts 기존 getUsage() 유지 (API 응답 구조만 변경)
R7 SubscriptionManagement.tsx AI 토큰 섹션 UI 추가
R8 utils.ts transformApiToFrontend() AI 토큰 변환 추가
R9 /usage/page.tsx 생성 새 페이지 (SubscriptionManagement 재사용)
R10 /subscription/page.tsx /usage로 리다이렉트 처리

4.3 메뉴 변경

작업 대상 설명
R11 menus 테이블 기존 구독관리(/subscription) 메뉴 → URL을 /usage로 변경하거나 비활성화
R12 menus 테이블 이용현황(/usage) 메뉴 유지

결과: 사이드바에 이용현황 하나만 표시, 클릭 시 통합 페이지


5. 제거 대상

항목 사유
api_calls 섹션 (usage API) 일반 API 호출 횟수는 무의미 → AI 토큰으로 대체
하드코딩 apiCallsLimit: 10000 실제 제한이 아닌 의미없는 수치
ApiRequestLog 기반 사용량 1일만 보관, 통계 목적 부적합
/subscription 별도 메뉴 /usage로 통합

6. 작업 순서

Phase 1: API 개선 (api)
  └── R1~R3: usage() 메서드 수정 (AI 토큰 집계 추가)

Phase 2: React 페이지 통합 (react)
  └── R5~R8: 타입/컴포넌트 수정 (AI 토큰 UI 추가)
  └── R9~R10: /usage 페이지 생성, /subscription 리다이렉트

Phase 3: 메뉴 정리
  └── R11~R12: 메뉴 통합 (tinker로 직접 수정)

7. 정책 기반 확정 사항

rules/customer-pricing.md 6장에 따라 확정:

# 항목 확정 내용
D1 AI 토큰 월별 한도 100만 토큰/월 (매월 1일 리셋, 이월 없음)
D2 초과 과금 1,000토큰 단위 실비 (ai_pricing_configs 단가 기반)
D3 알림 기준 80% 경고, 100% 한도 초과 알림
D4 저장공간 기본 한도 100GB (초과 시 100GB당 5만원/월)
D5 비용 노출 토큰 비용(KRW) 고객에게 표시 (customer-pricing에 명시)

7.1 추가 확인 필요 사항

# 질문 영향
Q1 100만 토큰 한도를 tenant.ai_token_limit 필드로 관리할 것인가? (플랜별 차등 가능성) 테넌트 테이블 컬럼 추가 여부
Q2 이용현황 페이지 접근 권한은? (관리자만 vs 모든 사용자) 권한 설정
Q3 저장공간의 실제 사용량이 정확히 갱신되고 있는가? 파일 업로드/삭제 시 incrementStorage/decrementStorage 호출 여부
Q4 tenant.storage_limit 기본값이 현재 10GB인데 100GB로 변경 필요 마이그레이션 또는 데이터 수정

관련 문서

문서 경로
API 개발 규칙 dev/standards/api-rules.md
options 정책 standards/options-column-policy.md

최종 업데이트: 2026-03-18