# task-2157: AI 주제 추천 기능 점검 + 채널 제한 에러 원인 확인

## S - Situation
InsuRo AI 콘텐츠 작성 페이지에서 "AI 주제 추천" 기능과 채널 제한 로직에 대한 점검이 필요하다. suggest-topics Edge Function과 generate-content Edge Function의 코드는 완전 구현된 상태이다.

## C - Complication
두 가지 문제가 보고됨: (1) AI 주제 추천 버튼 클릭 시 주제가 입력란에 자동으로 안 들어가는 현상, (2) Hidden 플랜에서 Threads 선택 시 "현재 플랜에서 지원하지 않는 채널입니다" 에러 발생.

## Q - Question
각 문제의 근본 원인은 무엇이며, 코드 수정 없이 원인을 특정할 수 있는가?

## A - Answer
두 문제 모두 코드 분석 및 API 테스트를 통해 근본 원인 후보를 3~4개로 좁혔다. suggest-topics Edge Function은 배포 확인 완료(HTTP 401 정상 반환). generate-content의 채널 에러는 getUserPlan이 리턴하는 planName이 PLAN_HIERARCHY에 매핑되지 않아 level 0으로 폴백되는 시나리오가 가장 유력하다. 코드 수정은 task-2156(2팀) 완료 후 별도 태스크로 진행 필요.

---

## 작업 1: AI 주제 추천 기능 점검 결과

### 1-1. suggest-topics Edge Function 배포 상태
- **배포 확인**: OPTIONS 요청 → HTTP 200, POST (인증 없음) → HTTP 401 `{"error":"인증이 필요합니다"}`
- **결론**: 함수가 정상 배포 및 동작 중

### 1-2. suggest-topics 코드 분석
- `getUserSortOrder()`: profiles → org_subscriptions → user_subscriptions → Free 폴백 (sort_order=1)
- `PRO_SORT_ORDER = 3` → sort_order >= 3 이어야 통과 (Pro 이상)
- Hidden 플랜 sort_order=5 → **통과해야 정상**
- AI 호출: `gemini-2.0-flash-lite` → JSON 배열 파싱 → `{topics: [...]}` 반환

### 1-3. 프론트엔드 흐름 분석
```
useFeatureGate("ai_topic_suggest") → allowed → canUseSuggest
  ↓ true → "AI 주제 추천" 버튼 표시
  ↓ false → "Pro 이상 이용 가능" 잠금 텍스트
  
버튼 클릭 → handleSuggestTopics()
  → supabase.functions.invoke("suggest-topics", {body})
  → 성공: setAiTopics(data.topics)
  → 실패: toast 에러 + setAiTopicsRequested(false) 롤백

주제 버튼 렌더링: aiTopicsRequested ? aiTopics : quickTopics
  → onClick: setTopic(t) → Textarea value={topic} 반영
```

### 1-4. "주제가 입력란에 안 들어가는" 근본 원인 후보

**후보 A (가능성 최고): DB features에 ai_topic_suggest 키 누락/값 불일치**
- `useFeatureGate`는 `subscription_plans.features["ai_topic_suggest"]`를 읽음
- 키가 없으면 `allowed=false` → 버튼 자체가 표시되지 않음 → "Pro 이상 이용 가능" 텍스트만 보임
- 검증 방법: Supabase SQL 실행 — `SELECT name, features->>'ai_topic_suggest' FROM subscription_plans WHERE is_active=true`

**후보 B: Edge Function에서 403 반환 (sort_order 불일치)**
- 히든 유저의 subscription status가 'active'가 아니면 Free 폴백 (sort_order=1) → sort_order < 3 → 403 반환
- `handleSuggestTopics` catch → toast 에러 + `setAiTopicsRequested(false)` 롤백 → 주제 목록 빈 상태
- 검증 방법: 히든 유저의 user_subscriptions.status 확인

**후보 C: useCallback 의존성 배열 미세 불일치**
- `[channel.label, settings, toast]`이지만 함수 body에서 `channel.value`를 사용
- 실제 동작에 영향 가능성 낮음 (label과 value가 동시에 변경되므로)

**후보 D: setTopic(t) 동작 문제 (가능성 최저)**
- React 단방향 데이터 흐름이 정상이므로 이 단계에서 끊길 가능성 거의 없음

---

## 작업 2: 채널 제한 에러 근본 원인 확인 (코드 수정 금지)

### 2-1. generate-content Edge Function 배포 상태
- **배포 확인**: OPTIONS → HTTP 200, POST (인증 없음) → HTTP 500 `{"error":"AI API key is not configured"}`
- verify_jwt=false이므로 Supabase 레이어 통과, 함수 내부에서 500 발생
- **결론**: 함수가 배포되어 동작 중이나, 인증 없이는 AI API 키 미설정 에러

### 2-2. 코드 분석: 채널 체크 로직

```
getUserPlan(sb, userId) → { planName, planId, features }
  ↓
PLAN_HIERARCHY[planName] ?? 0  → planLevel
  ↓
PLAN_CHANNEL_MAP[planLevel] → allowedChannels
  ↓
allowedChannels.includes(requestedChannel) → 통과 or 403
```

- PLAN_HIERARCHY: `"Hidden": 4, "히든": 4` — 두 버전 모두 존재
- PLAN_CHANNEL_MAP[4]: `["naver-blog", "tistory-blog", "instagram", "threads", "youtube-reels"]` — threads 포함
- **코드상 Hidden 플랜이면 threads 통과해야 정상**

### 2-3. 근본 원인 후보 (가능성 순)

**후보 1 (가능성 최고): 히든 유저의 구독 레코드 status가 'active'가 아님**
- Hidden 플랜은 `is_public=false`로 관리자 수동 배정
- user_subscriptions.status가 'active'가 아니면 (예: 'trial', 'inactive') → getUserPlan의 경로 A/B 모두 통과 못함
- Free 폴백 진입 → planName="무료" → level=0 → PLAN_CHANNEL_MAP[0]=["naver-blog"] → threads 거부
- **검증 SQL**: `SELECT us.status, sp.name FROM user_subscriptions us JOIN subscription_plans sp ON us.plan_id=sp.id WHERE sp.name IN ('히든','Hidden')`

**후보 2: 'Internal' 플랜 배정 문제**
- 마이그레이션 20260310120000에서 `'Internal'` 플랜이 INSERT됨
- PLAN_HIERARCHY에 `"Internal"` 키 없음 → `PLAN_HIERARCHY["Internal"] ?? 0` = 0
- 히든 유저가 'Internal' 플랜에 배정되었다면 threads 거부
- **검증 SQL**: `SELECT * FROM user_subscriptions us JOIN subscription_plans sp ON us.plan_id=sp.id WHERE sp.name='Internal' AND us.status='active'`

**후보 3: Supabase FK Join 응답 구조 문제**
- `.select("subscription_plans(id, name, features)")` 결과가 배열로 반환될 가능성
- `(orgSub.subscription_plans as any).name` → undefined → `"Free"` 폴백 → level 0
- **검증**: Edge Function에 console.log 추가 후 Supabase Functions 로그 확인

**후보 4: generate-content가 구버전으로 배포됨**
- 최신 코드에는 PLAN_HIERARCHY에 Hidden이 있지만, 배포된 버전에 없을 수 있음
- **검증**: `supabase functions deploy generate-content` 재배포 후 테스트

### 2-4. suggest-topics vs generate-content 구조 차이 (핵심)

| 항목 | suggest-topics | generate-content |
|---|---|---|
| 플랜 조회 | sort_order (숫자) 직접 비교 | planName (문자열) → PLAN_HIERARCHY 룩업 |
| 정규화 | 불필요 (숫자) | 없음 (normalisePlanTier 미적용) |
| 실패 모드 | 숫자 비교라 안정적 | planName 불일치 시 level 0으로 강등 |

**suggest-topics는 sort_order 숫자 비교 방식이라 구조적으로 안전하지만, generate-content는 planName 문자열 매칭 방식이라 취약하다.**

---

## L1 스모크테스트 결과

- 서버 재시작: Vite dev server 기동 확인 (localhost:8080)
- API 응답 확인:
  - suggest-topics OPTIONS: HTTP 200 (배포 확인)
  - suggest-topics POST (인증 없음): HTTP 401 `{"error":"인증이 필요합니다"}` (정상)
  - generate-content OPTIONS: HTTP 200 (배포 확인)
  - generate-content POST (인증 없음): HTTP 500 `{"error":"AI API key is not configured"}` (verify_jwt=false로 인한 정상 동작)
- 스크린샷: 인증 필요로 인해 AI 콘텐츠 작성 페이지 직접 접근 불가 (로그인 페이지로 리다이렉트 확인)

---

## 발견 이슈 및 해결

### 자체 해결 (0건)
- 코드 수정 금지 작업이므로 해당 없음

### 범위 외 미해결 (3건)
1. **DB features.ai_topic_suggest 값 검증 필요** — 범위 외 사유: Supabase 대시보드 직접 접근 필요 (CLI 토큰 미설정)
2. **히든 유저 구독 레코드 status 확인 필요** — 범위 외 사유: DB 직접 조회 필요
3. **generate-content 재배포 필요 여부** — 범위 외 사유: Supabase CLI 인증 토큰 없음, task-2156(2팀) 완료 후 처리

---

## 권장 후속 조치

1. **즉시**: Supabase 대시보드에서 히든 유저의 `user_subscriptions.status` 확인 + `subscription_plans.features` 내 `ai_topic_suggest` 키 존재 확인
2. **task-2156 완료 후**: generate-content Edge Function 재배포 (`supabase functions deploy generate-content`)
3. **구조적 개선 (별도 태스크 권장)**: generate-content의 planName 문자열 매칭을 suggest-topics처럼 sort_order 숫자 비교로 전환 — 문자열 불일치 리스크 원천 제거

---

## 셀프 QC 체크리스트

- [x] 1. 이 변경이 다른 파일에 영향을 미치는가? → 코드 수정 없음, 분석 전용 작업
- [x] 2. 엣지 케이스: planName 불일치, subscription status 비활성, FK Join 배열 반환
- [x] 3. 작업 지시와 정확히 일치: 코드 수정 금지 준수, 원인 파악만 수행
- [x] 4. 에러 처리와 보안: 분석 작업이므로 해당 없음
- [x] 5. 테스트: 코드 수정 없으므로 테스트 불필요
- [x] 6. 발견한 이슈: 3건 범위 외 미해결 (DB 직접 접근 필요)
- [x] 7. 코드 아키텍처 원칙: 분석 전용 작업
- [x] 8. 인터페이스 변경: 없음
- [x] 13. L1 스모크테스트: API 호출 확인 완료

---

## 머지 판단
- **머지 필요**: No (코드 수정 없음, 분석 전용 작업)
- **worktree**: 미사용 (태스크 지시에 따라 main 브랜치에서 작업)

---

## 모델 사용 기록
- 루(Lugh, 백엔드): generate-content 채널 에러 분석 / sonnet
- 브리짓(Brigid, 프론트엔드): useFeatureGate 및 프론트엔드 흐름 점검 / sonnet

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

