# task-647.1: InsuRo 백엔드 — generate-content 플랜 기반 모델 강제 + 토큰 차감

## 목표
generate-content Edge Function에서 사용자의 구독 플랜에 따라 AI 모델을 강제 적용하고,
사용된 토큰을 차감/기록하는 로직 구현.

## 현재 상태 분석

### Edge Function
- 파일: `supabase/functions/generate-content/index.ts`
- getUserPlan() (114-156줄): 이미 존재 — org/user 구독 → 플랜 features(JSONB) 반환
- MODEL_MAP (4-8줄): ai_model_tier → 실제 모델 매핑
  ```
  flash → gemini-2.0-flash
  pro → gemini-2.5-pro
  top → claude-sonnet-4-6
  ```
- 현재 문제 (250-276줄): planFeatures.ai_model_tier 체크하지만,
  사용자가 settings.aiModel로 상위 모델 보내면 그대로 통과시킴

### DB 테이블 (이미 존재)
- `plan_ai_models`: plan_id, feature_key, model_tier, model_name
- `plan_token_config`: plan_id, monthly_token_quota (-1=무제한)
- `feature_token_costs`: feature_key, model_tier, token_cost
- 시드 데이터:
  - 무료: 100 토큰/월, 베이직: 500, 프로: 2000, 맥스: -1(무제한)
  - ai_generate: haiku=1, sonnet=3, opus=10

### 토큰 사용 기록 테이블
- `token_usage_log` 테이블 존재 여부 확인 필요
- 없으면 생성해야 함

## 작업 내용

### 1. MODEL_MAP 확장
- 현재 3개(flash/pro/top) → 플랜 티어에 맞게 정리
```typescript
const TIER_MODEL_MAP: Record<string, {provider: string, model: string}[]> = {
  haiku: [
    { provider: "gemini", model: "gemini-2.0-flash-lite" }
  ],
  sonnet: [
    { provider: "gemini", model: "gemini-2.0-flash-lite" },
    { provider: "gemini", model: "gemini-2.0-flash" },
    { provider: "gemini", model: "gemini-2.5-pro" }
  ],
  opus: [
    { provider: "gemini", model: "gemini-2.0-flash-lite" },
    { provider: "gemini", model: "gemini-2.0-flash" },
    { provider: "gemini", model: "gemini-2.5-pro" },
    { provider: "claude", model: "claude-sonnet-4-6" }
  ]
};
```

### 2. 플랜 기반 모델 강제 적용
- getUserPlan()에서 받은 ai_model_tier 확인
- 사용자 요청 모델이 해당 티어 허용 범위 내인지 검증
- 범위 초과 시: 에러 반환 `{ error: "upgrade_required", message: "이 모델은 프로 플랜 이상에서 사용 가능합니다", requiredPlan: "pro" }`
- 모델 미지정 시: 해당 티어의 기본 모델 자동 선택

### 3. 토큰 잔량 확인
- 생성 전 토큰 잔량 체크
  1. `plan_token_config`에서 monthly_token_quota 조회
  2. `token_usage_log`에서 이번 달 사용량 합산
  3. 잔량 부족 시: 에러 반환 `{ error: "token_exhausted", message: "이번 달 토큰이 소진되었습니다", used: N, quota: M }`
- -1(무제한) 플랜은 체크 스킵

### 4. 토큰 사용 기록
- `token_usage_log` 테이블 필요 (없으면 마이그레이션 SQL 생성)
  ```sql
  CREATE TABLE IF NOT EXISTS public.token_usage_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES auth.users(id),
    feature_key TEXT NOT NULL DEFAULT 'ai_generate',
    model_tier TEXT NOT NULL,
    model_name TEXT NOT NULL,
    token_cost INTEGER NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now()
  );
  ```
- 생성 성공 후 토큰 사용량 기록
- `feature_token_costs`에서 해당 feature_key + model_tier의 token_cost 조회하여 기록

### 5. API 응답에 토큰 정보 추가
- 성공 응답에 토큰 정보 포함:
  ```json
  {
    "content": "...",
    "tokenInfo": {
      "cost": 3,
      "remaining": 1997,
      "quota": 2000
    }
  }
  ```

## 주의사항
- 1팀이 프론트엔드 수정 중 — 백엔드(Edge Function)만 담당
- Edge Function 호출 인터페이스 변경 시 1팀과 충돌 주의
  - 기존 request 필드 유지 (topic, contentType, settings)
  - 응답에 tokenInfo 필드 추가는 OK (기존 필드 건드리지 않음)
- token_usage_log 테이블 생성 SQL은 `supabase/migrations/` 폴더에 저장
- Supabase Edge Function 배포: `supabase functions deploy generate-content`
- RLS 정책: token_usage_log는 authenticated만 자기 row INSERT 가능

## 검증
- 무료 유저가 Pro 모델 요청 시 → upgrade_required 에러
- 토큰 100개 소진 후 요청 시 → token_exhausted 에러
- 정상 요청 시 → 콘텐츠 + tokenInfo 반환
- 무제한 플랜(맥스) → 토큰 체크 스킵, 기록은 함
- 모델 미지정 요청 → 플랜 기본 모델로 자동 생성