# InsuRo 키워드 순위 — 검색량 배치 수집 엔드포인트 구현

## 작업 레벨: Lv.2

## 프로젝트
- InsuRo 서버: `/home/jay/projects/InsuRo/server`

## 현재 문제
`trend_keywords` 테이블에 키워드(암보험, 종신보험 등)가 등록되어 있지만, `naver_search_volume=0`, `score=0`, `last_collected_at=NULL`. 검색량 수집 배치 로직이 구현되지 않아 키워드 순위 TOP 20이 모두 0으로 표시됨.

## 기존 코드 분석
- `_get_naver_search_volume(keyword)` 함수: 이미 구현됨 (라인 1921~1968)
  - 네이버 SearchAd API로 PC/모바일 검색량 조회
  - API 키: NAVER_SEARCHAD_CUSTOMER_ID, NAVER_SEARCHAD_API_KEY, NAVER_SEARCHAD_SECRET_KEY (설정 완료)
- `trend_keywords` 테이블 필드: `naver_search_volume`, `score`, `last_collected_at`, `last_scored_at`
- 검색량 조회 함수는 있지만, trend_keywords 테이블을 일괄 업데이트하는 배치 로직이 없음

## 수정 사항

### 1. 배치 수집 엔드포인트 추가
파일: `server/main.py`

```python
@app.post("/api/insuro/admin/keywords/collect")
async def collect_keyword_volumes(
    background_tasks: BackgroundTasks,
    payload: dict = Depends(verify_admin),  # 어드민 전용
):
    """trend_keywords 전체 키워드의 네이버 검색량 일괄 수집."""
    background_tasks.add_task(_batch_collect_keyword_volumes)
    return {"status": "started", "message": "검색량 수집이 시작되었습니다"}
```

### 2. 배치 수집 함수 구현
```python
def _batch_collect_keyword_volumes():
    """trend_keywords 테이블의 모든 active 키워드 검색량 수집 + score 계산."""
    sb = _get_supabase_client()
    keywords = sb.table("trend_keywords").select("id, keyword").eq("is_active", True).execute()
    
    for kw in keywords.data:
        try:
            vol_data = _get_naver_search_volume(kw["keyword"])
            total_vol = (vol_data.get("pc", 0) or 0) + (vol_data.get("mobile", 0) or 0)
            
            # score 계산 (검색량 기반, 로그 스케일)
            import math
            score = math.log10(max(total_vol, 1)) * 25  # 1000건 → 75점, 10000건 → 100점
            
            sb.table("trend_keywords").update({
                "naver_search_volume": total_vol,
                "score": round(score, 1),
                "last_collected_at": datetime.now(timezone.utc).isoformat(),
                "last_scored_at": datetime.now(timezone.utc).isoformat(),
            }).eq("id", kw["id"]).execute()
            
            # 네이버 API rate limit 방지 (0.5초 간격)
            import time
            time.sleep(0.5)
        except Exception as e:
            logger.warning("키워드 %s 수집 실패: %s", kw["keyword"], e)
    
    # 수집 완료 기록
    sb.table("trend_collection_runs").insert({
        "started_at": datetime.now(timezone.utc).isoformat(),
        "finished_at": datetime.now(timezone.utc).isoformat(),
        "keywords_count": len(keywords.data),
        "status": "completed",
    }).execute()
```

### 3. 서버 시작 시 자동 수집 (최초 1회)
서버 시작 이벤트(`@app.on_event("startup")` 또는 lifespan)에서, `last_collected_at`이 NULL인 키워드가 있으면 자동 수집 실행.
단, 이미 수집 중이면 중복 실행 방지.

### 4. score 계산 로직
score는 검색량 기반 로그 스케일:
- 100건 → 50점
- 1,000건 → 75점
- 10,000건 → 100점
- 100,000건 → 125점
★ score 계산 공식은 `math.log10(max(total_vol, 1)) * 25`

## affected_files
- `server/main.py` (수정 — 배치 수집 엔드포인트 + 함수 추가)

## 검증 시나리오
1. `/api/insuro/admin/keywords/collect` POST 호출 → "started" 응답
2. 수집 완료 후 trend_keywords 테이블의 naver_search_volume > 0
3. score가 검색량에 비례하여 계산됨
4. 키워드 순위 TOP 20 페이지에서 검색량/스코어 정상 표시
5. 네이버 API rate limit 초과 안 함 (0.5초 간격)
6. npm run build 성공 (프론트 변경 없음)
