# task-667.1 완료 보고서: 카드뉴스 렌더러 버그 3건 수정 + Threads 업로드

**팀**: dev2-team (오딘 팀장)
**작업자**: 토르(백엔드), 헤임달(테스터)
**일시**: 2026-03-18

---

## SCQA

**S**: ThreadAuto 카드뉴스 렌더러가 5단계 파이프라인과 연동하여 Threads에 카드뉴스를 자동 발행하고 있다. 파이프라인은 items에 `label`/`value` 키를 사용하여 데이터를 전달한다.

**C**: 그러나 `render_card_list()`는 `title` 키만 읽어서 파이프라인의 `label` 데이터를 무시하여 _01, _02 슬라이드 제목이 빈 문자열로 렌더링되고, `render_summary_cta()`는 `items=[]`일 때 제목이 맨 위/CTA가 맨 아래에 몰려 거대한 빈 공간이 발생한다.

**Q**: 3가지 렌더링 버그를 수정하고, 수정된 렌더러로 실제 Threads 업로드까지 검증할 수 있는가?

**A**: 3건 모두 수정 완료. label/title/value/description 양방향 fallback을 전체 렌더 함수에 통일 적용하고, CTA 슬라이드 empty items 레이아웃을 중앙 배치로 개선. pytest 84건 전체 PASS (신규 7건 포함), pyright 에러 0건. Threads carousel 업로드 성공 (post_id: `18083509715251202`).

---

## 수정 내역

### 버그 1: card_list 제목 누락
- **파일**: `renderer/cardnews.py:982`
- **변경**: `item.get("title", "")` → `item.get("title", item.get("label", ""))`
- **검증**: 파이프라인 출력 `{"label": "보험 흐름을 모릅니다", "description": "..."}` → 슬라이드에 정상 표시 확인

### 버그 2: CTA 슬라이드 레이아웃 깨짐
- **파일**: `renderer/cardnews.py:1787~1849` (신규 분기)
- **변경**: `if not items:` 분기 신설 — 요약 카드 영역 스킵, 제목+CTA를 HEIGHT*0.37 기준 중앙 배치
- **검증**: items=[] CTA 슬라이드에서 제목이 화면 중앙 위, CTA가 70px 아래에 배치됨 확인

### 버그 3: 필드명 통일
- **render_summary_cta 내부**: line 1867~1868 — `title`/`label`, `description`/`value` fallback
- **render_all**: line 2162~2163 — 동일 패턴 적용
- **render_detail**: 기존에 이미 정상 (변경 없음)

---

## 테스트 결과

**pytest**: 84 passed in 19.71s (기존 77 + 신규 7)
**pyright**: 0 errors, 0 warnings
**black/isort**: 준수

### 신규 테스트 7건 (TestBug667LabelFallback)
1. test_card_list_label_fallback — PASS
2. test_card_list_title_and_label — PASS
3. test_summary_cta_empty_items_no_crash — PASS
4. test_summary_cta_empty_items_layout — PASS (1080x1350 확인)
5. test_summary_cta_label_value_fallback — PASS
6. test_render_all_label_fallback — PASS
7. test_render_from_slides_label_fallback — PASS

---

## Threads 업로드 결과

- **Post ID**: `18083509715251202`
- **토픽**: "서울대보험쌤이 직접 개발한 AI 프로그램이 특별한 이유" (정보제공 카테고리)
- **검수 점수**: 56/70 (통과 기준 42)
- **슬라이드**: 6장 (cover + card_list + detail×3 + cta)
- **테마**: GreenWhite
- **이미지 경로**: `/home/jay/projects/ThreadAuto/output/cardnews_20260318_003135_0[0-5].png`

---

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **card_list의 description은 label과 달리 fallback 미적용** — description은 파이프라인에서 `description` 키를 사용하므로 기존 코드가 정상. value fallback은 summary_cta/render_all에만 필요하여 해당 함수만 수정
2. **render_summary_cta에서 items 유무에 따른 제목 렌더링 중복** — items=[] 분기에서 제목/부제 렌더링을 별도로 구현해야 했으나, 기존 코드(line 1719~1749)의 제목 렌더링이 items 분기 전에 위치하여 중복 발생. `not items` 분기 내부에 별도 렌더링 로직 작성으로 해결
3. **CTA 텍스트 높이 계산이 items 분기 뒤에 위치** — CTA 높이 계산을 items 분기 앞으로 이동하여 양쪽 분기에서 공통 사용

---

## 생성/수정 파일

- **수정**: `renderer/cardnews.py` (버그 3건 수정)
- **수정**: `tests/test_cardnews_renderer.py` (테스트 7건 추가)
- **생성**: `output/cardnews_20260318_003135_0[0-5].png` (렌더링 결과물 6장)

---

## QC 자동 검증

5 PASS, 1 FAIL (범위 외), 3 SKIP, 1 WARN

- **file_check**: PASS (86721 bytes + 55437 bytes)
- **data_integrity**: PASS
- **test_runner**: FAIL — `test_cta_linebreak.py::TestFactDbContainsBusinessPage` 실패 (fact_db.md에 '사업단 페이지' 표기 없음). **본 작업 범위 외 기존 실패** (cardnews 렌더러 84건은 전부 PASS)
- **tdd_check**: PASS
- **pyright_check**: WARN — reportMissingImports 7건 (프로젝트 sys.path 구조 기인, 기존 이슈)
- **style_check**: PASS (black + isort OK)

> ⚠️ 기존 테스트 실패 1건 (본 작업 범위 외): `test_cta_linebreak.py::TestFactDbContainsBusinessPage::test_fact_db_contains_business_page`
