# InsuRo 정보성 키워드 분석 — 외부 워커 제거 + 아누 서버 내부 처리

## 작업 레벨: Lv.3

## 프로젝트 시스템 3문서
- DevSystem: `/home/jay/workspace/memory/plans/anu-guide-system/plan.md`

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

## 배경
정보성 키워드 분석이 외부 워커(`http://localhost:8100`)에 의존하는데, 해당 워커가 존재하지 않아 `Connection refused` 에러 발생. 외부 워커 대신 아누 서버(main.py) 내에서 claude CLI + 네이버 API로 직접 처리하도록 전환.

## 수정 사항

### 1. _run_keyword_analysis() 함수 전면 교체
`server/main.py`의 `_run_keyword_analysis()` (약 1800줄):

**기존**: InfoKeyword Worker API 호출 (POST /analyze → 폴링 /status/)
**변경**: 아누 서버 내에서 직접 분석

```python
async def _run_keyword_analysis(job_id: str, keyword: str, user_id: str):
    """정보성 키워드 분석 — claude CLI + 네이버 API 통합."""
    sb = _get_supabase_client()
    sb.table("keyword_jobs").update({"status": "processing"}).eq("job_id", job_id).execute()
    
    try:
        # 1단계: 네이버 검색량 수집
        search_data = await _get_naver_search_volume(keyword)
        
        # 2단계: claude CLI로 정보성 판정
        analysis_prompt = f"""
        보험 키워드 "{keyword}"에 대해 블로그 SEO 관점에서 분석해주세요.
        
        네이버 검색 데이터:
        - PC 월간 검색량: {search_data.get('pc', 0)}
        - 모바일 월간 검색량: {search_data.get('mobile', 0)}
        - 경쟁강도: {search_data.get('comp', 'N/A')}
        
        아래 7단계로 분석하고 JSON으로 출력:
        1. 키워드 제목 적합성 (블로그 제목으로 사용 가능한지)
        2. 검색량 평가 (월 1000건 이상이면 PASS)
        3. 검색 의도 분류 (정보성/거래성/항해성)
        4. 관련 키워드 5개 추출
        5. 경쟁도 평가 (상/중/하)
        6. 정보성 판정 (INFORMATIONAL/PROMOTIONAL)
        7. 최종 점수 (0-100)
        
        출력 형식:
        {{
          "keyword": "{keyword}",
          "verdict": "INFORMATIONAL" 또는 "PROMOTIONAL",
          "confidence_score": 85,
          "steps": {{
            "title_analysis": {{ "pass": true, "detail": "..." }},
            "search_volume": {{ "pass": true, "pc": 8900, "mobile": 3500 }},
            "intent": {{ "pass": true, "type": "informational" }},
            "related_keywords": ["키워드1", "키워드2", ...],
            "competition": {{ "level": "중", "detail": "..." }},
            "verdict_reason": "...",
            "final_score": 85
          }},
          "search_volume": {{ "total": 12400, "pc": 8900, "mobile": 3500 }}
        }}
        """
        
        result = subprocess.run(
            ["claude", "-p", analysis_prompt, "--model", "haiku", "--output-format", "json"],
            capture_output=True, text=True, timeout=30,
            cwd="/tmp",
        )
        
        # 3단계: 결과 파싱 + DB 저장
        analysis_result = json.loads(result.stdout)
        
        sb.table("keyword_jobs").update({
            "status": "completed",
            "completed_at": datetime.now(timezone.utc).isoformat(),
            "result": {
                "keyword": keyword,
                "worker_results": [analysis_result],
                "analyzed_at": datetime.now(timezone.utc).isoformat(),
            },
        }).eq("job_id", job_id).execute()
        
    except Exception as e:
        logger.exception("키워드 분석 실패: %s", e)
        sb.table("keyword_jobs").update({
            "status": "failed",
            "error": str(e)[:500],
        }).eq("job_id", job_id).execute()
```

### 2. 네이버 검색량 수집 헬퍼
기존 `/api/insuro/naver/search` 로직을 내부 함수로 추출:
```python
async def _get_naver_search_volume(keyword: str) -> dict:
    """네이버 SearchAd API에서 키워드 검색량 조회."""
    # 기존 naver_search_proxy()의 keyword 로직 재사용
    ...
    return {"pc": pc_count, "mobile": mobile_count, "comp": comp_idx}
```

### 3. INFOKEYWORD_WORKER_URL 참조 제거
- `_run_keyword_analysis()`에서 외부 워커 호출 코드 전체 제거
- `INFOKEYWORD_WORKER_URL`, `INFOKEYWORD_API_KEY` 환경변수 참조 제거 (또는 미사용 표시)

### 4. 키워드 순위 TOP 20 데이터 갱신
trend_keywords 테이블의 검색량 데이터가 0인 문제:
- 서버 시작 시 또는 cron으로 네이버 검색량을 주기적으로 갱신하는 배치 함수 추가
- 또는 `/api/insuro/keywords/top` 호출 시 캐시 만료되었으면 자동 갱신

### 5. 플랜 제한 유지
- 정보성 키워드 분석은 기존대로 맥스/히든 플랜만 접근 가능 (require_feature("infokeyword_access"))

## affected_files
- `server/main.py` (수정 — _run_keyword_analysis 전면 교체, 네이버 헬퍼, 외부 워커 참조 제거)
- `server/tests/test_infokeyword.py` (수정 — 테스트 업데이트)

## 검증 시나리오
1. 정보성 키워드 분석: 키워드 입력 → AI 분석 → INFORMATIONAL/PROMOTIONAL 판정 + 7단계 상세
2. 외부 워커 호출 없음 (Connection refused 에러 해소)
3. 네이버 검색량 데이터 정상 포함
4. 기존 프론트엔드(InfoKeyword.tsx) 호환 유지 (API 응답 형식 동일)
5. pytest 전량 통과
6. npm run build 성공
7. 서버 재시작 정상
