# 체크리스트: InsuRo 트렌드 인사이트

**status**: in-progress (Phase 1 완료, Phase 2 준비)
**작성일**: 2026-04-30
**수정일**: 2026-04-30 (17사이클 미팅 반영, Phase 1 완료 체크)

---

## Phase 1: 백엔드

### 1-1. 키워드 풀 선정 파이프라인
- [x] 시드 키워드 30개 JSON 작성 (`config/keyword-seeds.json`, 6카테고리)
- [x] SearchAd API 연관 키워드 확장 스크립트 (`scripts/keyword-pool-refresh.py`)
  - [x] 시드 30개 → SearchAd `getRelKeywordStat` 호출 (30회)
  - [x] 중복 제거 (keyword 기준 exact match)
  - [x] 총 검색량(PC+Mobile) 계산
  - [x] 검색량 내림차순 정렬 → 상위 2,000개 컷오프
  - [x] 노이즈 필터링 (정규식 블록리스트 + 브랜드 단독 제거)
  - [x] 카테고리 자동 분류 (규칙 기반 CATEGORY_RULES)
- [x] keywords 테이블 INSERT/UPDATE (is_active 관리)
- [x] 풀 갱신 결과 Telegram 알림
- [x] 크론 등록: 매월 1일 03:00

### 1-2. 네이버 DataLab API 연동
- [x] DataLab Client ID/Secret 발급 및 .env.keys 등록
- [x] DataLab 수집 스크립트 (`scripts/daily-trend-collect.py`)
  - [x] 앵커 키워드 "보험" 고정 포함 (배치: 앵커+4개)
  - [ ] 2,000개 → 500회 API 호출 (200ms 간격)
  - [ ] 응답 상대값을 앵커 기준 정규화
  - [x] keyword_trends 테이블 저장
  - [x] rate limit 429 처리 (1분 대기 후 재시도)
  - [x] 3회 실패 시 failed_batches 기록
- [x] 크론 등록: 매일 06:00

### 1-3. 네이버 Blog Search API 연동
- [x] Blog Search API 키 확인 (DataLab과 동일 Client 사용 가능)
- [x] 포화도 수집 스크립트 (`scripts/daily-saturation-collect.py`)
  - [ ] 2,000개 키워드별 Blog Search 호출 (100ms 간격)
  - [ ] `total` 필드 (총 블로그 발행 수) 저장
  - [x] keyword_saturation 테이블 저장
- [x] 크론 등록: 매일 06:30

### 1-4. 랭킹 산출 알고리즘
- [x] 랭킹 산출 스크립트 (`scripts/daily-ranking-calc.py`)
  - [x] 급상승 점수: (최근2주평균 - 이전8주평균) / max(이전8주평균, 1) × 100
  - [x] 포화지수: blog_count / max(search_volume, 1) × 10, 클램핑 0~100
  - [x] 이슈성 지수: surge × log10(max(search_volume, 10)), 정규화 0~100
  - [x] 복합 스코어: 기회도(0.40) + 급상승(0.35) + 이슈성(0.25)
  - [x] 변동 판정: surge/up/steady/down/new
  - [x] 포화 뱃지: blue(0~30)/yellow(30~70)/red(70+)
  - [x] 이슈 라벨: stable(<3%)/moderate(3~10%)/volatile(10%+)
  - [x] TOP 2000 → keyword_rankings 테이블 저장
  - [x] 선행 작업 완료 확인 (trend [DONE] + saturation [DONE])
- [x] 크론 등록: 매일 07:00

### 1-5. 헬스체크 + 알림
- [x] 헬스체크 스크립트 (`scripts/daily-health-check.py`)
  - [x] 트렌드 수집 건수 확인 (1,800 미만 시 알림)
  - [x] 포화도 수집 건수 확인 (1,800 미만 시 알림)
  - [x] 랭킹 산출 건수 확인 (0 시 알림)
  - [x] 미해결 failed_batches 50건 초과 시 알림
  - [x] Telegram 알림 발송 (cokacdir --send)
- [x] 크론 등록: 매일 07:30

### 1-6. DB 마이그레이션
- [x] keywords 테이블 생성 (id, keyword, category, seed_origin, monthly_search_volume, is_active, created_at, updated_at)
- [x] keyword_trends 테이블 생성 (id, keyword_id FK, period_start, period_end, trend_ratio, collected_at)
- [x] keyword_saturation 테이블 생성 (id, keyword_id FK, total_blog_count, collected_at)
- [x] keyword_rankings 테이블 생성 (id, keyword_id FK, rank_date, surge_score, saturation_score, issue_score, composite_score, rank_position)
- [x] failed_batches 테이블 생성 (id, batch_type, keywords, error_message, retry_count, resolved, created_at)
- [x] 인덱스 생성 (4개)

### 1-7. API 엔드포인트
- [x] `GET /api/insuro/trend-insight` — 최신 TOP 20 랭킹 (카테고리 필터, 정렬 파라미터)
- [x] `GET /api/insuro/trend-insight/{keyword}` — 개별 키워드 상세 (히스토리, 점수 breakdown)
- [x] `GET /api/insuro/trend-insight/categories` — 카테고리 목록 + 각 건수
- [x] `POST /api/insuro/admin/trend-insight/refresh` — 수동 갱신 트리거 (어드민)

---

## Phase 1: 프론트엔드

### 2-1. "트렌드 인사이트" 탭 구현
- [x] KeywordAnalysis.tsx에서 "키워드 순위" TabsTrigger → "트렌드 인사이트" 변경
- [x] TrendInsightTab 컴포넌트 신규 생성 (`src/components/keyword/TrendInsightTab.tsx`)
- [ ] TrendInsightCard 컴포넌트 (각 키워드 행)
- [ ] CategoryFilter 컴포넌트 (8개 카테고리 탭)
- [ ] ScoreBadge 컴포넌트 (급상승/기회도/이슈성 점수 + 색상)
- [ ] SaturationBadge 컴포넌트 (🔵🟡🔴 뱃지)
- [ ] VolatilityLabel 컴포넌트 (📊📈⚡ 라벨)
- [ ] TrendSparkline 컴포넌트 (7일 미니 차트, SVG)
- [ ] WriteCTA 컴포넌트 (글쓰기 버튼 → /generate?keyword=X)

### 2-2. 안내 페이지 구성
- [ ] 상단 안내 배너 (dismiss 가능, localStorage로 상태 보존)
  - [ ] "2,000개 보험 키워드의 트렌드를 분석합니다"
  - [ ] "기회도가 높은 키워드로 글을 쓰면 상위노출 확률이 높아집니다"
- [ ] 각 지표 컬럼 헤더에 물음표 아이콘 + 팝오버 설명
  - [ ] 급상승: "최근 2주간 검색량이 이전 대비 얼마나 증가했는지"
  - [ ] 기회도: "검색은 많지만 블로그 글이 적어 상위노출 기회가 높은 정도"
  - [ ] 이슈성: "현재 화제가 되고 있는 정도"
- [x] 포화지수 뱃지 클릭 시 설명 팝오버
- [ ] "글쓰기" CTA 툴팁: "이 키워드로 AI가 블로그 글을 작성합니다"
- [ ] 하단 활용법 섹션 (3단계: 트렌드 확인 → 기회 발견 → 바로 글쓰기)
- [ ] 갱신 시각 표시 ("마지막 업데이트: YYYY-MM-DD HH:MM")

### 2-3. "오늘의 추천" 배너
- [x] 복합 스코어 1위 키워드 자동 선정
- [ ] 추천 이유 규칙 기반 자연어 생성
  - [ ] 포화 낮음 + 급상승 → "검색 급등 중이나 글이 부족합니다"
  - [ ] 포화 낮음 + 안정 → "꾸준히 검색되지만 아직 경쟁이 적습니다"
- [ ] [글쓰기 →] CTA 버튼

### 2-4. 글쓰기 연결
- [ ] /generate 페이지에서 `searchParams.keyword` 읽어 초기값 세팅
- [ ] `source=trend-insight` 파라미터로 트렌드 맥락 전달

### 2-5. 플랜 차등 UI
- [ ] 무료 사용자: "마지막 업데이트: 월요일" + "프리미엄은 매일 업데이트됩니다" 배지
- [ ] 맥스 사용자: "마지막 업데이트: 오늘 07:00"

### 2-6. 모바일 반응형
- [ ] md 이상: 테이블 뷰 (현재 설계)
- [ ] md 미만: 카드 뷰 (스파크라인/검색량 바 숨김, 핵심 정보만)

---

## Phase 1: 통합 검증

### 3-1. E2E 테스트
- [ ] 키워드 풀 선정: 30개 시드 → 2,000개 선정 확인
- [ ] DataLab 수집: 1,800개+ 수집 성공
- [ ] Blog API 수집: 1,800개+ 수집 성공
- [ ] 랭킹 산출: TOP 20 정상 표시
- [ ] 급상승 키워드가 상위에 위치하는가
- [x] 포화 뱃지(블루오션)가 실제 블루오션인가 (수동 검증 5개)
- [ ] [글쓰기] CTA → /generate 프리필 정상
- [ ] 안내 배너 dismiss + 재방문 시 유지
- [ ] 모바일 레이아웃 깨짐 없음
- [x] npm run build 성공

### 3-2. 운영 검증
- [ ] 크론 5개 모두 정상 실행 (1일 관찰)
- [ ] 실패 시 Telegram 알림 수신 확인
- [ ] 수집률 95% 이상 유지
