# task-777.1 완료 보고서
## ThreadAuto 인스타그램 크로스 포스팅 기능 구현

---

**S**: ThreadAuto가 Threads API를 통해 카드뉴스(이미지 캐러셀)를 자동 발행하고 있으며, ThreadsPublisher + ThreadsClient 기반으로 안정적으로 운영 중이다.

**C**: Instagram에도 동일 콘텐츠를 동시 게시해야 하는 요구가 있으나, Instagram Graph API 연동 코드가 전혀 없어 수동 업로드에 의존하고 있다.

**Q**: 기존 Threads 발행 아키텍처를 확장하여 Instagram 크로스 포스팅을 구현하고, 한쪽 실패 시에도 다른 쪽이 정상 발행되도록 할 수 있는가?

**A**: Instagram Graph API 클라이언트, OAuth 인증, 토큰 관리, Instagram Publisher, CrossPublisher 오케스트레이터를 구현하여 Threads + Instagram 동시 발행 체계를 완성했다. 58개 테스트 전체 통과, pyright 에러 0건, black/isort 준수. `CROSS_POST_ENABLED` 플래그로 Instagram 발행 on/off 제어 가능.

---

## 생성/수정 파일 목록

### 신규 파일 (5개 구현 + 5개 테스트)

- `api/instagram_client.py` — Instagram Graph API 비동기 클라이언트 (post_carousel, post_single_image, retry 로직)
- `auth/instagram_oauth.py` — Facebook Login 경유 Instagram OAuth 플로우 (인증 URL, 토큰 교환, Page Token, IG User ID)
- `auth/instagram_token_store.py` — Instagram 전용 토큰 저장/로드/갱신 (.tokens/instagram_token.json)
- `publisher/instagram_publisher.py` — Instagram 카드뉴스 발행 (2200자 캡션, 해시태그 30개, 재시도 3회)
- `publisher/cross_publisher.py` — Threads + Instagram 동시 발행 오케스트레이터 (한쪽 실패 허용)
- `tests/test_instagram_client.py` — 10개 테스트 케이스
- `tests/test_instagram_oauth.py` — 11개 테스트 케이스
- `tests/test_instagram_token_store.py` — 17개 테스트 케이스
- `tests/test_instagram_publisher.py` — 10개 테스트 케이스
- `tests/test_cross_publisher.py` — 10개 테스트 케이스

### 수정 파일 (4개)

- `config.py` — Instagram/Facebook/CrossPost 환경변수 6개 + API 상수 3개 추가, load_env_keys()에 새 접두어 추가
- `publisher/__init__.py` — InstagramPublisher, CrossPublisher export 추가
- `main.py` — Instagram OAuth 콜백 엔드포인트 2개 추가 (/auth/instagram/login, /auth/instagram/callback)
- `cli.py` — instagram-auth, instagram-status 명령어 2개 추가

### 미수정 파일 (설계 판단)

- `scheduler/cron_runner.py` — 스케줄러는 PipelineOrchestrator를 호출하며 발행 레이어와 분리. CrossPublisher는 CLI/파이프라인에서 직접 사용.

## 테스트 결과

```
58 passed in 0.93s
```

- pyright: 0 errors, 0 warnings (8개 파일 검증)
- black --check: 8 files unchanged
- isort --check: 통과

## 발견 이슈 및 해결

### 자체 해결 (3건)

1. **publisher/__init__.py에 새 클래스 미노출** — `InstagramPublisher`, `CrossPublisher`를 `__all__`에 추가
   - 수정: `publisher/__init__.py`:1-5
2. **api/instagram_client.py에서 INSTAGRAM_GRAPH_API_BASE 중복 정의** — config.py에 이미 정의된 상수를 로컬에 하드코딩하여 동기화 위험
   - 수정: `from config import INSTAGRAM_GRAPH_API_BASE`로 변경
3. **instagram_publisher.py에서 top-level import 시 렌더러 의존** — 서버 시작 시 렌더러 미설치 환경에서 import 에러 발생 가능
   - 수정: 불칸-B가 lazy import 패턴으로 구현 (publish_cardnews 내부에서 import)

### 범위 외 미해결 (2건)

1. **Instagram 실제 인증 미완료** — Meta Developer Console에서 Instagram 제품 추가 + Facebook Page 연결 필요. 환경변수(INSTAGRAM_APP_ID 등) 미설정 상태. 범위 외 사유: 외부 서비스 설정이 필요하며 제이회장님의 직접 작업 필요.
2. **실제 크로스 포스팅 테스트 미수행** — Instagram 인증이 완료되지 않아 실제 API 호출 테스트 불가. 유닛 테스트 58개로 로직 검증 완료. 실제 발행 테스트는 인증 완료 후 별도 진행 필요.

## QC 셀프 체크

- [x] 1. 변경 영향 파일: config.py 수정이 기존 Threads 기능에 영향 없음 확인 (접두어 추가만)
- [x] 2. 엣지 케이스: 토큰 없음, IG User ID 없음, 캡션 초과, 해시태그 초과, 한쪽 실패, 둘 다 실패 — 모두 테스트 커버
- [x] 3. 작업 지시 일치: 신규 5개 + 수정 4개 + 테스트 5개 = 작업서 명세와 일치
- [x] 4. 에러/보안: API retry (429/500/503), 토큰 만료 자동 갱신, 예외 격리(CrossPublisher)
- [x] 5. 테스트 커버리지: 58개 테스트, 성공/실패/엣지 케이스 전부 포함
- [x] 6. 발견 이슈 3건 자체 해결, 2건 범위 외 기록

## 아키텍처 요약

```
카드뉴스 렌더링 → 이미지 파일 리스트
       ↓
CrossPublisher.publish_cardnews()
       ├→ ThreadsPublisher.publish_cardnews()  (기존 유지)
       └→ InstagramPublisher.publish_cardnews()  (신규)
              ├→ InstagramClient.post_carousel()
              │     ├→ 아이템 컨테이너 생성 (각 이미지)
              │     ├→ 캐러셀 컨테이너 생성
              │     └→ media_publish
              └→ 결과 통합 (overall_success)
```

## 다음 단계 (참고)

1. Meta Developer Console에서 Instagram 제품 추가
2. Facebook Page와 Instagram Business 계정 연결
3. 환경변수 설정 (INSTAGRAM_APP_ID, INSTAGRAM_APP_SECRET, FACEBOOK_PAGE_ID)
4. `python cli.py instagram-auth` 실행하여 인증 완료
5. 실제 크로스 포스팅 테스트 1건 수행
