OPEN HYPER STEP
← 목록으로 (ai-startup)
AI-STARTUP · 32 / 100
ai-startup
CHAPTER 32 / 100
읽기 약 2
FUNCTION

비용 관리: 토큰 사용량 추적 + 사용자별 제한


핵심 개념

usage tracking·rate limit·plan별 한도·실시간 알림.

본문

비용 폭주 위험

📋 코드 (11줄)
[흔한 시나리오]
- 사용자 1명이 10,000 메시지 보냄 (스크립트)
- 비용: 100만 토큰 × $15 = $15,000
- → 한 달 만에 파산


[방어]
1. Rate Limit (분당·일별)
2. Plan별 한도
3. 비용 알림
4. 자동 차단

사용량 DB 스키마

SQL📋 코드 (22줄)
CREATE TABLE usage (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL,
  model TEXT NOT NULL,
  input_tokens INT NOT NULL,
  output_tokens INT NOT NULL,
  cost_cents INT NOT NULL,  -- 0.01센트 단위
  endpoint TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX ON usage (user_id, created_at DESC);


CREATE TABLE user_plans (
  user_id UUID PRIMARY KEY,
  plan TEXT NOT NULL DEFAULT 'free',  -- free, pro, business
  monthly_token_limit INT NOT NULL,
  daily_request_limit INT NOT NULL,
  current_period_start TIMESTAMPTZ,
  current_period_tokens INT DEFAULT 0
);

사용량 기록 미들웨어

TYPESCRIPT📋 코드 (40줄)
// lib/track-usage.ts
import { createClient } from '@/lib/supabase/server';

const PRICING = {
  'gpt-4o-mini': { input: 0.015, output: 0.06 },         // per 1K tokens (cents)
  'gpt-4o': { input: 0.25, output: 1 },
  'claude-haiku-4-5': { input: 0.08, output: 0.4 },
  'claude-sonnet-4-6': { input: 0.3, output: 1.5 },
  'claude-opus-4-7': { input: 1.5, output: 7.5 },
};

export async function trackUsage({
  userId,
  model,
  inputTokens,
  outputTokens,
  endpoint,
}: any) {
  const pricing = PRICING[model];
  const costCents = Math.round(
    (inputTokens / 1000) * pricing.input * 100 +
    (outputTokens / 1000) * pricing.output * 100,
  );

  const supabase = await createClient();
  await supabase.from('usage').insert({
    user_id: userId,
    model,
    input_tokens: inputTokens,
    output_tokens: outputTokens,
    cost_cents: costCents,
    endpoint,
  });

  // Plan 사용량 누적
  await supabase.rpc('increment_period_tokens', {
    user_id: userId,
    tokens: inputTokens + outputTokens,
  });
}

API 라우트에 통합

TYPESCRIPT📋 코드 (31줄)
import { streamText } from 'ai';
import { trackUsage } from '@/lib/track-usage';

export async function POST(req: Request) {
  const supabase = await createClient();
  const { data: { user } } = await supabase.auth.getUser();
  if (!user) return new Response('Unauthorized', { status: 401 });

  // 한도 체크
  const { canUse, reason } = await checkUsage(user.id);
  if (!canUse) {
    return new Response(reason, { status: 429 });
  }

  const result = streamText({
    model: anthropic('claude-sonnet-4-6'),
    messages: req.messages,
    onFinish: async ({ usage }) => {
      // 응답 완료 후 사용량 기록
      await trackUsage({
        userId: user.id,
        model: 'claude-sonnet-4-6',
        inputTokens: usage.promptTokens,
        outputTokens: usage.completionTokens,
        endpoint: '/api/chat',
      });
    },
  });

  return result.toDataStreamResponse();
}

한도 체크

TYPESCRIPT📋 코드 (30줄)
async function checkUsage(userId: string) {
  const supabase = await createClient();

  // 일별 요청 수
  const today = new Date(); today.setHours(0,0,0,0);
  const { count: todayCount } = await supabase
    .from('usage')
    .select('*', { count: 'exact', head: true })
    .eq('user_id', userId)
    .gte('created_at', today.toISOString());

  // Plan 정보
  const { data: plan } = await supabase
    .from('user_plans')
    .select()
    .eq('user_id', userId)
    .single();

  // 일별 한도
  if (todayCount! >= plan!.daily_request_limit) {
    return { canUse: false, reason: 'Daily limit exceeded' };
  }

  // 월별 토큰 한도
  if (plan!.current_period_tokens >= plan!.monthly_token_limit) {
    return { canUse: false, reason: 'Monthly token limit exceeded' };
  }

  return { canUse: true };
}

Plan 정의

TYPESCRIPT📋 코드 (17줄)
const PLANS = {
  free: {
    monthly_tokens: 50_000,    // 월 50K 토큰
    daily_requests: 20,
    models: ['gpt-4o-mini', 'gemini-flash'],
  },
  pro: {
    monthly_tokens: 1_000_000, // 월 1M 토큰
    daily_requests: 200,
    models: ['gpt-4o', 'claude-sonnet-4-6', 'gemini-flash'],
  },
  business: {
    monthly_tokens: 10_000_000,
    daily_requests: 1000,
    models: ['claude-opus-4-7', 'gpt-4o', 'claude-sonnet-4-6'],
  },
};

사용량 대시보드

TSX📋 코드 (23줄)
// app/dashboard/usage/page.tsx
async function UsagePage() {
  const supabase = await createClient();
  const { data: usage } = await supabase
    .from('usage')
    .select('*')
    .gte('created_at', startOfMonth())
    .order('created_at', { ascending: false });

  const totalCost = usage!.reduce((s, u) => s + u.cost_cents, 0) / 100;
  const totalTokens = usage!.reduce((s, u) => s + u.input_tokens + u.output_tokens, 0);

  return (
    <div>
      <Card>
        <h2>이번 달 사용량</h2>
        <p>{totalTokens.toLocaleString()} tokens</p>
        <p>${totalCost.toFixed(2)}</p>
      </Card>
      <UsageChart data={usage} />
    </div>
  );
}

비용 알림

TYPESCRIPT📋 코드 (15줄)
// 일일 cron — 비정상 사용 검출
export async function checkAbnormalUsage() {
  const supabase = await createClient();

  const { data } = await supabase.rpc('get_top_users_today');
  for (const user of data) {
    if (user.cost_today > 5_00) {  // $5 이상
      await notifyOps({
        userId: user.user_id,
        cost: user.cost_today / 100,
        message: 'Abnormal usage detected',
      });
    }
  }
}

다음 챕터

CH.33 "AI SaaS 종합".


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
무료

월 $0 — 검증·시작 단계

AI 비용 관리을 무료 도구만으로
시작하는 방법을 알려줘.
소자본

월 $20~50 — MVP·초기 운영

월 $20~50 예산으로 AI 비용 관리을
검증·MVP 단계까지 진행하는 전략은?
프로덕션

월 $200~500 — 성장 단계

AI 비용 관리을 프로덕션 단계로
확장할 때 필요한 도구·운영 체계는?
스택

풀스택 — 도구 조합 분석

2026년 AI 비용 관리 관련 도구 5개를
조합한 추천 스택을 알려줘.

⭐ 이것만 기억하세요
비용 관리: 토큰 사용량 추적 + 사용자별 제한 이 3가지만 확실히 잡으세요
1.사용량 기록 (onFinish) + 한도 체크 = 비용 폭주 방어
2.Plan별 한도 (free/pro/business) = SaaS 표준
3.사용량 대시보드로 사용자 투명성


공유하기
진행도 32 / 100