Files
sam-docs/plans/usage-subscription-unification.md
김보곤 0487c61f31 docs: [plans] 이용현황 Q1/Q4 확정 반영
- Q1: tenant.ai_token_limit 컬럼 추가 (기본값 100만, 테넌트별 기록)
- Q4: tenant.storage_limit 기본값 10GB → 100GB 변경 확정
2026-03-18 12:18:27 +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 확정 사항 (2026-03-18 추가)

# 항목 결정
Q1 AI 토큰 한도 관리 tenant.ai_token_limit 컬럼 추가 (기본값 100만). 플랜별 차등 아닌 테넌트별 개별 기록
Q4 저장공간 기본값 tenant.storage_limit 기본값 10GB → 100GB로 변경 (마이그레이션)

7.2 추가 확인 필요 사항

# 질문 영향
Q2 이용현황 페이지 접근 권한은? (관리자만 vs 모든 사용자) 권한 설정
Q3 저장공간의 실제 사용량이 정확히 갱신되고 있는가? 파일 업로드/삭제 시 incrementStorage/decrementStorage 호출 여부

관련 문서

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

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