---
task_id: task-1957
type: context
scope: task
created: 2026-04-19
updated: 2026-04-19
status: completed
---

# 맥락 노트: task-1957

**task**: task-1957

---

## 결정 근거

### 서버사이드 플랜 검증 위치: FastAPI vs Edge Function
- **결정**: FastAPI main.py에 @require_plan 데코레이터 추가
- **이유**: Edge Function(generate-content)에 이미 플랜 검증 로직 존재. 하지만 FastAPI 엔드포인트(/api/insuro/ai/generate, 향후 추가 API)에는 플랜 검증 없음. 두 곳 모두 적용해야 완전한 방어.
- **대안 기각**: Edge Function만 보강 → FastAPI 엔드포인트 노출 위험

### 플랜 데이터 소스: DB vs 하드코딩
- **결정**: DB(subscription_plans 테이블) features JSON blob 기반 — 기존 패턴 유지
- **이유**: 이미 useFeatureGate, useUserPlan이 DB 기반으로 동작. 하드코딩하면 이중 소스 문제 발생
- **대안 기각**: planFeatureMap.ts에 하드코딩 → DB와 불일치 리스크

### LockedFeatureOverlay vs PlanGuard 확장
- **결정**: LockedFeatureOverlay를 별도 컴포넌트로 신규 생성
- **이유**: PlanGuard는 전체 페이지 블러. LockedFeatureOverlay는 개별 기능 단위 오버레이로 용도가 다름. 반투명 오버레이 + 업그레이드 CTA.
- **대안 기각**: PlanGuard 내부 리팩토링 → 기존 CRM 페이지 사용처에 영향

## 3 Step Why

### 1st Why: "왜 이 설계가 필요한가?"
→ A: 현재 FastAPI 서버에 플랜 검증이 전혀 없어서, JWT만 있으면 모든 API 접근 가능. 무료 사용자가 Pro 전용 기능(AI 고급 모델, 다채널 생성)을 제한 없이 사용 가능한 보안 허점 존재.

### 2nd Why: "왜 A(서버사이드 미들웨어)가 최선의 접근인가?"
→ B: Edge Function에만 검증이 있으면 FastAPI 직접 호출로 우회 가능. @require_plan 데코레이터 패턴은 FastAPI의 Depends 시스템과 자연스럽게 결합되고, 매 요청마다 DB 조회(Supabase client)로 최신 플랜 상태 반영 가능.

### 3rd Why: "왜 B가 다른 대안보다 나은가?"
→ C: Redis 캐시(TTL 5분)를 추가하면 DB 부하를 줄이면서도 실시간성 유지. 대안1(JWT claim에 플랜 포함)은 토큰 갱신 전까지 stale 데이터 문제. 대안2(API Gateway 레벨)는 Supabase 아키텍처에 맞지 않음. @require_plan + Redis 캐시가 비용-정확도 균형 최적.

**A-B-C 논리적 일관성**: 보안 허점(A) → 서버사이드 미들웨어(B) → Depends + Redis 캐시(C) 경로가 일관됨. ✅

## 참조 자료

- 인슈로 전수조사: `memory/research/insuro-page-audit.md`
- 인슈로 시스템 계획서: `memory/plans/insuro-system/plan.md`
- 인슈로 체크리스트: `memory/plans/insuro-system/checklist.md`

## 주의사항

- Stripe 결제(H1)는 이 task에서 구현하지 않음 — premiumOnly 미완성 디자인 절대 건드리지 말 것
- API 직접 호출 금지 — CLI만 사용 (anu_provider 패턴)
- Edge Function(generate-content)의 기존 플랜 검증은 그대로 유지 — FastAPI에만 추가
- 플랜 tier 순서: Free(1) < 베이직(2) < 프로(3) < 맥스(4) < 히든(5) — normalisePlanTier()과 일치
- Redis 환경: 서버에 Redis 미설치 가능성 → 인메모리 TTL 캐시(dict + timestamp)로 대체
