# task-876.1 완료 보고

> 팀: dev3-team | 팀장: 다그다(Dagda) | 작성일: 2026-03-24

---

## SCQA 요약

**S**: task-870.1에서 1팀이 `image_router.py`(라우팅+fallback 로직)를 완성했으나, 생성 함수 4개가 stub(return False) 상태이고 IPTC 메타데이터 삽입 유틸리티가 미구현 상태였다.

**C**: stub 함수는 모든 이미지 생성 요청에 대해 항상 False를 반환하여 실제 이미지를 생성할 수 없었다. 또한 Meta 광고 정책(2024년 4월 의무화) 준수를 위한 IPTC `digitalsourcetype: trainedAlgorithmicMedia` 태그 삽입 기능이 없어 광고 업로드 시 알고리즘 도달 감소(15~30%) 패널티 위험이 있었다.

**Q**: image_router.py의 4개 stub 함수를 실제 API 연동 코드로 교체하고, 기존 65건 테스트를 유지하면서 IPTC 메타데이터 자동 삽입 기능을 구현할 수 있는가?

**A**: `_generate_gemini`(gcloud_auth+Gemini REST), `_generate_satori`(Node.js subprocess), `_generate_hybrid`(generate_hybrid 모듈), `_generate_gpt`(OpenAI API) 실제 구현 완료. `iptc_tagger.py` 신규 구현 및 생성 파이프라인에 자동 연동. 기존 65건 전체 유지 + 신규 26건(iptc_tagger 19건 + 통합 7건) 추가 = **총 91건 pytest 전체 통과, pyright 0 errors**.

---

## 작업 상세

### 1. image_router.py API 연동

#### _generate_gemini
- `gcloud_auth.get_access_token()` → `gemini_pro_generate.generate_image_via_gemini_api()` 호출
- 예외 발생 시 `log.warning` 후 `False` 반환 (안전한 fallback 보장)

#### _generate_satori
- `satori-test/satori_cli.js` 신규 생성 후 `subprocess.run(["node", satori_cli.js, "--prompt", ..., "--output", ...])` 호출
- returncode != 0 또는 출력 파일 미존재 시 `False` 반환
- timeout: 120초

#### _generate_hybrid
- `generate_hybrid.py`에서 `capture_hybrid()` 직접 import 후 prompt 기반 scenario_data 구성
- bg_A.jpg 또는 TEMPLATE_PATH 미존재 시 조기 `False` 반환 (파일 존재 가드)
- Playwright `sync_playwright` 활용

#### _generate_gpt
- `openai.OpenAI` 클라이언트로 `gpt-image-1` 모델 호출 (quality=high, 1024×1024)
- b64_json 우선, URL fallback 방식으로 이미지 저장
- `openai` 패키지 미설치 또는 API key 없는 경우 `False` 반환

#### IPTC 태깅 연동
- `generate_image()` 내 성공 케이스(1차 성공 + fallback 성공) 두 곳에 `iptc_tagger.tag_image()` 호출 추가
- IPTC 태깅 실패는 `log.warning` 처리 (이미지 생성 성공 결과에 영향 없음)

### 2. satori_cli.js 신규 생성

파일: `/home/jay/workspace/tools/ai-image-gen/satori-test/satori_cli.js`
- `--prompt <string>` / `--output <string>` CLI 인자 지원
- 1080×1080px 카드뉴스 디자인 (NotoSansCJKkr Bold 폰트)
- 검정/네이비 그라디언트 배경 + 흰색 텍스트
- 실제 동작 확인: `/tmp/satori_test.png` (144KB) 생성 성공

### 3. iptc_tagger.py 신규 생성

파일: `/home/jay/workspace/tools/ai-image-gen/iptc_tagger.py`

공개 API:
- `make_xmp_packet(title, keywords)` → XMP XML 문자열 (Iptc4xmpExt:DigitalSourceType=trainedAlgorithmicMedia)
- `encode_iptc_iim(title, keywords)` → IPTC IIM 바이트 (Record 2, Dataset 5/25)
- `tag_image(image_path, title, keywords)` → PNG/JPEG 모두 지원

PNG 방식: `PIL.PngImagePlugin.PngInfo.add_itxt("XML:com.adobe.xmp", ...)` 로 XMP 삽입
JPEG 방식: SOI 직후에 APP13(IPTC IIM) + APP1(XMP) 마커 바이트 직접 삽입

---

## 생성/수정 파일 목록

| 상태 | 파일 |
|------|------|
| 수정 | `/home/jay/workspace/tools/ai-image-gen/image_router.py` |
| 신규 | `/home/jay/workspace/tools/ai-image-gen/iptc_tagger.py` |
| 신규 | `/home/jay/workspace/tools/ai-image-gen/satori-test/satori_cli.js` |
| 신규 | `/home/jay/workspace/tools/ai-image-gen/test_iptc_tagger.py` |
| 신규 | `/home/jay/workspace/tools/ai-image-gen/test_image_router_integration.py` |

---

## 테스트 결과

```
test_image_router.py:          65 passed (기존 유지)
test_iptc_tagger.py:           19 passed (신규, 단위 테스트)
test_image_router_integration.py: 7 passed (신규, 통합 테스트)
─────────────────────────────────
총계:                          91 passed in 0.70s
```

pyright: **0 errors, 0 warnings, 0 informations**
black + isort: **OK**

---

## 발견 이슈 및 해결

### 자체 해결 (3건)

1. **satori display:flex 누락** — 자식 요소가 있는 div에 `display: 'flex'` 추가 필수 (satori 제약)
   - 수정: `satori_cli.js` 각 자식 div에 `display: 'flex'` 명시

2. **black/isort 스타일 불일치** — 루(Lugh) 생성 파일 2건 포매팅 불일치
   - 수정: `black iptc_tagger.py image_router.py && isort ...` 실행으로 즉시 수정

3. **test_runner SKIP (관련 테스트 미자동추론)** — qc_verify.py가 `--check-files iptc_tagger.py,image_router.py`에서 관련 테스트 파일을 0개로 추론
   - 원인: qc_verify의 자동 추론 로직이 test_image_router_integration.py를 image_router.py 관련으로 인식 못함
   - 해결: 수동으로 pytest 실행 결과를 보고서에 기재 (91건 전체 PASS 증거)

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

1. **실제 API 키 없는 환경에서 _generate_gemini 등 실제 실행 불가** — 범위 외: 실제 API 키는 .env.keys에서 관리되며 별도 프로비저닝 필요. 통합 테스트는 모두 mock 기반.

---

## QC 자동 검증 결과

```json
{
  "overall": "PASS",
  "checks": {
    "file_check": "PASS",
    "data_integrity": "PASS",
    "pyright_check": "PASS (0 errors)",
    "style_check": "PASS (black+isort OK)",
    "spec_compliance": "PASS"
  }
}
```

---

## 셀프 QC 체크리스트

- [x] 1. 영향 파일: image_router.py 수정 → test_image_router.py 65건 기존 유지 확인. iptc_tagger.py 신규 → test_iptc_tagger.py 19건 신규
- [x] 2. 엣지 케이스: API 키 없음, subprocess 실패, bg 이미지 없음, IPTC 태깅 실패 모두 False/log.warning으로 안전 처리
- [x] 3. 작업 지시 일치: 3개 연동(Gemini/Satori/Hybrid) + GPT fallback + IPTC 태거 + 테스트 기준 모두 충족
- [x] 4. 에러 처리: 모든 생성 함수 try/except 감싸기. IPTC 실패가 생성 성공에 영향 없도록 분리
- [x] 5. 테스트 커버리지: 기존 65 + 신규 26 = 91건. gemini/satori/hybrid/gpt fallback + IPTC 연동 검증
- [x] 6. 발견 이슈 모두 직접 해결: 3건 해결, 1건 범위 외 명시
