# task-646.1 완료 보고서 — InsuRo 프론트엔드 플랜 기반 AI 모델 셀렉터

## SCQA

**S**: InsuRo AI 콘텐츠 생성 화면(`/generate`)에서 Gemini 모델 4개가 `generateOptions.ts`에 하드코딩되어 모든 사용자에게 동일하게 노출되고 있다.

**C**: DB에는 이미 `plan_ai_models`, `plan_token_config`, `feature_token_costs` 테이블로 플랜별 모델/토큰 체계가 구축되어 있으나, 프론트엔드가 이를 활용하지 않아 무료 유저도 모든 모델을 선택할 수 있는 상태다.

**Q**: 사용자 구독 플랜에 따라 허용 모델만 선택 가능하고, 상위 모델은 잠금 표시하는 동적 모델 셀렉터를 구현할 수 있는가?

**A**: `usePlanFeatures` 통합 훅 + 티어 기반 모델 매핑 + GenerateSettingsPanel UI 개편으로 구현 완료. 49개 테스트 전수 통과, TypeScript 에러 0건. 무료/베이직/프로/맥스/히든 5단계 플랜별 모델 접근 제어 + 토큰 잔량 표시 + 기본 모델 자동 설정 기능 적용.

---

## 작업 내용

### 1. usePlanFeatures 훅 신규 생성
- **파일**: `src/hooks/usePlanFeatures.ts`
- 기존 `use-user-plan.ts`, `use-user-tokens.ts` 패턴 기반 통합 훅
- 조직 구독 → 개인 구독 → Free 폴백 조회 체계
- 하이브리드 모델 조회: `plan_ai_models` DB 조회 → 로컬 매핑 폴백
- `getModelsForPlan()` 순수 유틸리티 함수 (테스트용)

### 2. generateOptions.ts 모델 목록 재구성
- **파일**: `src/data/generateOptions.ts` (453-492행)
- 기존 Gemini 4개 하드코딩 → 3개 티어 기반 구조
  - haiku: Gemini 2.5 Flash
  - sonnet: Gemini 2.5 Flash + Gemini 2.5 Pro
  - opus: 위 2개 + Claude Sonnet
- `ModelTier`, `AiModelOption` 타입 추가
- `modelsByTier`, `defaultModelByTier`, `planToTier` 매핑 상수
- `export { allAiModels as aiModels }` 하위호환 유지

### 3. GenerateSettingsPanel.tsx UI 개편
- **파일**: `src/components/GenerateSettingsPanel.tsx`
- `usePlanFeatures` 훅 연동
- 플랜 이름 뱃지 표시 (예: "Pro 플랜")
- 토큰 잔량 프로그레스 바 (무제한이 아닌 경우)
- 모델 선택 버튼: 허용 모델 = 클릭 가능, 잠금 모델 = disabled + "🔒 업그레이드 필요"
- useEffect로 플랜 로딩 후 현재 모델이 미허용이면 기본 모델 자동 설정

### 4. 테스트 파일 작성
- `src/data/__tests__/generateOptions.test.ts` (23개)
- `src/hooks/__tests__/usePlanFeatures.test.ts` (26개)

---

## 생성/수정 파일 목록

- `src/hooks/usePlanFeatures.ts` — 신규 (통합 훅 + getModelsForPlan 유틸)
- `src/data/generateOptions.ts` — 수정 (모델 목록 재구성, 티어 매핑 추가)
- `src/components/GenerateSettingsPanel.tsx` — 수정 (플랜 기반 UI)
- `src/data/__tests__/generateOptions.test.ts` — 신규 (23 테스트)
- `src/hooks/__tests__/usePlanFeatures.test.ts` — 신규 (26 테스트)

---

## 검증 결과

- vitest: **49/49 통과** (2 test files, 0 failed)
- TypeScript (`tsc --noEmit --skipLibCheck`): **에러 0건**
- Edge Function 인터페이스: 변경 없음 (settings.aiModel 전달 구조 유지)

---

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **getModelsForPlan 함수 미구현** — 테스트에서 요구하는 순수 유틸리티 함수가 훅에 없었음 → `usePlanFeatures.ts`에 추가
2. **하위호환 aiModels export** — 기존 Gemini 3 시리즈 모델 제거 시 import 깨짐 위험 → `export { allAiModels as aiModels }` alias 처리
3. **기본 모델 자동 전환 타이밍** — planLoading 완료 전에 설정이 변경되면 경합 발생 가능 → useEffect 의존성에 `planLoading`, `defaultModel`만 포함하여 로딩 완료 시점에만 실행

### 범위 외 미해결 (1건)
1. **plan_ai_models RLS 정책 미확인** — DB에서 authenticated role의 읽기 권한 여부는 백엔드(2팀) 소관. 로컬 매핑 폴백으로 RLS 차단 시에도 정상 동작

---

## 테스트 결과

- **vitest** (프론트엔드): 49/49 통과 (2 test files)
- **TypeScript**: 에러 0건 (tsc --noEmit --skipLibCheck)
- ⚠️ 기존 테스트 실패 1건 (본 작업 범위 외): `server/tests/test_ai_parser.py::TestAnalyzeFcpaPdf::test_analyze_fcpa_pdf_includes_pdf_text_in_prompt` — 서버 Python 테스트로 프론트엔드 작업과 무관

## QC 자동 검증

- file_check: 통과 (5/5 파일 확인)
- data_integrity: 통과
- tdd_check: 통과 (테스트 2개 + 구현 3개)
- test_runner: 스킵 (프론트엔드 TypeScript 프로젝트, vitest로 별도 검증 완료)
- 보고서 갭 분석: 통과
