# task-1549: 네이버 블로그 비전 기반 UI 자동화 (스크린샷→LLM→좌표 클릭)

## 목표
naver_playwright.py에 **비전 기반 요소 탐색** 기능을 추가한다. CSS 셀렉터 대신 스크린샷을 LLM에게 보여주고 좌표를 받아서 클릭하는 방식이다.

## 배경
- 1팀, 2팀, 4팀이 CSS 셀렉터 기반으로 4회 시도 전부 실패
- 네이버 SE 에디터가 복잡한 iframe 중첩 + 동적 클래스로 셀렉터가 안 먹힘
- **CSS 셀렉터 접근은 완전히 포기. 비전 기반으로 전환한다.**
- 프로젝트 경로: `/home/jay/projects/BlogAuto`

## 핵심 아이디어

사람이 화면을 보고 마우스를 이동시켜 클릭하는 것과 동일한 방식:
1. `page.screenshot()` → 스크린샷 캡처
2. LLM 비전 모델(Haiku/Sonnet)에게 "이 화면에서 X 버튼의 좌표를 알려줘" 요청
3. 응답받은 좌표로 `_human_mouse_move()` (베지어 곡선) 자연스럽게 이동
4. 랜덤 딜레이 후 클릭
5. 다시 스크린샷 찍어서 결과 확인 → 다음 단계 반복

## 구현 요구사항

### 1. 비전 헬퍼 함수 (`_vision_find_element`)

```python
import anthropic
import base64

def _vision_find_element(self, page, description: str) -> tuple[int, int]:
    """스크린샷을 찍고 LLM 비전으로 요소의 좌표를 찾는다.
    
    Args:
        page: Playwright page 객체
        description: 찾고자 하는 요소 설명 (예: "취소 버튼", "제목 입력란")
    
    Returns:
        (x, y) 좌표 튜플
    """
    # 1. 스크린샷 캡처
    screenshot_bytes = page.screenshot()
    screenshot_b64 = base64.b64encode(screenshot_bytes).decode()
    
    # 2. Anthropic API로 비전 분석
    client = anthropic.Anthropic()  # ANTHROPIC_API_KEY 환경변수 사용
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",  # 빠르고 저렴한 모델
        max_tokens=200,
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/png",
                        "data": screenshot_b64
                    }
                },
                {
                    "type": "text",
                    "text": f"이 웹페이지 스크린샷에서 '{description}'의 중앙 좌표를 찾아주세요. "
                           f"반드시 JSON 형식으로만 답하세요: {{\"x\": 숫자, \"y\": 숫자}} "
                           f"스크린샷 크기는 가로x세로 픽셀 기준입니다. 다른 텍스트 없이 JSON만 출력하세요."
                }
            ]
        }]
    )
    
    # 3. 좌표 파싱
    import json
    coords = json.loads(response.content[0].text)
    return (coords["x"], coords["y"])
```

### 2. 비전 기반 클릭 함수 (`_vision_click`)

```python
def _vision_click(self, page, description: str, max_retries: int = 2) -> bool:
    """비전으로 요소를 찾아 자연스럽게 클릭한다.
    
    1. 스크린샷 → LLM 비전 → 좌표 파악
    2. _human_mouse_move()로 베지어 곡선 이동 (봇 탐지 방지)
    3. 랜덤 딜레이 후 클릭
    """
    for attempt in range(max_retries):
        try:
            x, y = self._vision_find_element(page, description)
            logger.info("비전 탐색 성공: '%s' → (%d, %d)", description, x, y)
            
            # 자연스러운 마우스 이동 (베지어 곡선) — 절대 순간이동 금지!
            self._human_mouse_move(page, x, y)
            _random_delay(0.3, 0.8)
            
            # 클릭
            page.mouse.click(x, y)
            _random_delay(0.5, 1.5)
            
            return True
        except Exception as exc:
            logger.warning("비전 클릭 실패 (시도 %d/%d): %s", attempt + 1, max_retries, exc)
            _random_delay(1, 2)
    
    return False
```

### 3. 비전 기반 텍스트 입력 (`_vision_type`)

```python
def _vision_type(self, page, field_description: str, text: str):
    """비전으로 입력란을 찾아 클릭 후 텍스트를 입력한다.
    
    클립보드 붙여넣기(Ctrl+V) 사용 — 긴 텍스트에 효율적.
    """
    # 입력란 클릭
    self._vision_click(page, field_description)
    _random_delay(0.3, 0.5)
    
    # 클립보드에 복사 후 붙여넣기
    import subprocess
    subprocess.run(["xclip", "-selection", "clipboard"], input=text.encode(), check=True)
    page.keyboard.press("Control+v")
    _random_delay(0.5, 1.0)
```

### 4. 기존 publish() 흐름 수정

`_handle_draft_modal()`을 비전 기반으로 교체:
```python
# 기존 (실패한 방식)
# self._handle_draft_modal(page, page)

# 신규 (비전 기반)
# 먼저 스크린샷 찍어서 팝업 존재 여부 확인
screenshot = page.screenshot(path="/tmp/draft-check.png")
# 팝업이 있으면 취소 클릭
self._vision_click(page, "임시저장 복원 팝업의 취소 버튼 또는 새로 작성 버튼")
```

제목 입력, 본문 입력 등도 비전 기반으로:
```python
# 제목 입력
self._vision_type(page, "블로그 글 제목 입력란", title)

# 본문 입력 
self._vision_click(page, "블로그 본문 편집 영역")
# Ctrl+V로 본문 붙여넣기
```

### 5. API 키 로딩

- Anthropic API 키는 `/home/jay/workspace/.env.keys`에서 `ANTHROPIC_API_KEY` 로드
- 또는 `/home/jay/projects/BlogAuto/.env.keys`에서 로드
- ⚠️ 코드에 키를 하드코딩하지 말 것

### 6. 의존성

- `pip install anthropic` (이미 설치되어 있을 것)
- xclip 또는 xsel (클립보드용, 이미 설치되어 있을 것)

## 주의사항
- ⚠️ **마우스 순간이동 절대 금지** — `_human_mouse_move()` 필수. `page.mouse.click(x, y)` 직접 호출 시 봇 탐지됨
- ⚠️ **임시저장(draft)만** — public 발행 절대 금지  
- LLM 비전 모델은 **Haiku** 사용 (빠르고 저렴, 좌표 파악에 충분)
- 매 단계마다 스크린샷 저장하여 디버깅 가능하게
- 기존 테스트(186건) 회귀 방지

## 테스트 순서
1. 비전 헬퍼 단위 테스트 (스크린샷 → 좌표 반환)
2. 글쓰기 페이지 접속 → 팝업 취소 클릭 테스트
3. 제목 입력 테스트
4. 본문 붙여넣기 테스트
5. 전체 임시저장 테스트

## 보고서
`memory/reports/task-1549.md`에 작성
