# Photo Card 템플릿 가이드 ## 1. 소개 Photo Card는 사진을 화면 가득 배치하는 여행·음식·라이프스타일용 템플릿이다. 강한 대표 이미지가 있는 이야기와 포트폴리오에 적합하고, 표·코드·긴 설명처럼 텍스트 구조가 중요한 주제에는 적합하지 않다. ## 2. 톤 가이드 사진이 설명하도록 두고 문장은 짧게 쓴다. 감각적인 명사형 또는 단정적인 평서문을 사용하며 느낌표와 수식어를 반복하지 않는다. ## 3. 슬라이드 흐름 권장 `cover → middle(1~10장) → end` 순서를 지킨다. `cover`와 `end`는 각각 정확히 1장이다. middle은 사진만 보여 주므로 장면 전환과 색감 흐름을 고려해 배열한다. ## 4. 슬롯별 작성 요령 아래 슬롯 한계는 등록 시 manifest에서 자동 생성했다. design_max는 디자인 권장 상한이며 초과해도 렌더는 성공하지만 `design_max_exceeded` 경고가 발생한다. max_chars는 실측 하드 상한이며 초과하면 422 `max_chars_exceeded`가 발생한다. 다만 max_chars는 레이아웃 안전을 보장하는 값이 아니라, 같은 글자 수라도 글자 폭이나 줄바꿈에 따라 max_chars 이내에서 렌더 영역을 넘을 수 있다. 이 경우 렌더는 성공하고 결과 이미지와 함께 `text_overflow` 등의 warning을 반환하므로, 경고가 발생한 슬롯의 문구를 더 줄여 다시 렌더한다. 이 수치는 손으로 쓴 것이 아니라 manifest가 원본이다 — 본 가이드와 manifest는 영구히 일치한다. - 요청 크기: `POST /v1/renders`의 전체 JSON 요청 본문 상한은 1,048,576바이트(1MB)다. 초과하면 HTTP 413 `payload_too_large`가 발생한다. - 글쓰기 원칙: 한 슬라이드에 한 주장. 좋은 예: `API 문서를 자동으로 검증하는 법`. 나쁜 예: `정말 놀랍고 엄청난 API 문서 자동화에 관하여 알아볼까요?`. - 브랜딩: `brand_logo_url`은 프로젝트 브랜드 자산(`logo_horizontal`)으로 렌더 시 자동 공급된다. 슬롯 값으로 데이터 URI를 직접 넣지 않는다. - 색상: `accent_color`는 manifest 테마 또는 `brand.colors.accent`의 `#RRGGBB`로 자동 공급된다. - 이미지: `image` 타입 슬롯은 공개 HTTPS URL과 `data:image/;base64,...` 데이터 URI를 모두 허용한다. 실사진은 본문 상한을 피하도록 원격 HTTPS URL을 사용하고, 데이터 URI는 로고·아이콘 같은 작은 자산에만 사용한다. 슬라이드 타입별로 아래 소절을 확인한다. 슬롯 이름·필수 여부·한계값은 manifest에서 생성되며, 타입별 작성 요령은 manifest의 `slide_types[].guidance`가 원본이다. ### `cover` (min 1, max 1) | 슬롯 | 필수 | 형식·한계 | | --- | --- | --- | | `photo_url` | 예 | 이미지(공개 HTTPS URL 또는 data URI) | | `logo_url` | 아니오 | 브랜드 자산(logo_horizontal) | | `company_name` | 아니오 | max 63자 | | `subtitle` | 예 | max 912자 | | `title` | 예 | max 15자 | ### `end` (min 1, max 1) | 슬롯 | 필수 | 형식·한계 | | --- | --- | --- | | `photo_url` | 예 | 이미지(공개 HTTPS URL 또는 data URI) | | `logo_url` | 아니오 | 브랜드 자산(logo_horizontal) | | `company_name` | 아니오 | max 594자 | ### `middle` (min 0, max 10) | 슬롯 | 필수 | 형식·한계 | | --- | --- | --- | | `photo_url` | 예 | 이미지(공개 HTTPS URL 또는 data URI) | | `logo_url` | 아니오 | 브랜드 자산(logo_horizontal) | | `company_name` | 아니오 | max 76자 | ## 5. 완성된 slides.json 예시 아래 1픽셀 데이터 URI는 형식 검증용의 작은 자산이다. 실제 게시물의 1080×1350 이상 실사진은 `https://...` 형태의 공개 HTTPS URL로 교체한다. 전체 JSON 요청 본문은 1,048,576바이트(1MB)를 넘으면 HTTP 413 `payload_too_large`가 발생하므로 실사진을 데이터 URI로 넣지 않는다. ```json { "template": "photo-card", "format": "instagram", "brand": { "assets": { "logo_horizontal": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" } }, "slides": [ { "type": "cover", "slots": { "photo_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "subtitle": "서울 산책 기록", "title": "비 오는 날의 성수", "company_name": "@lightsoft_crew" } }, { "type": "middle", "slots": { "photo_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "company_name": "@lightsoft_crew" } }, { "type": "end", "slots": { "photo_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "company_name": "@lightsoft_crew" } } ] } ``` ## 6. curl 호출 예시 ```bash curl -X POST 'https://api.cardnews.example/v1/renders' \ -H 'Authorization: Bearer $CARDNEWS_API_KEY' \ -H 'Content-Type: application/json' \ --data-binary @slides.json ``` ## 7. 검증·경고 대응 가이드 - `slide_count_violation`: cover와 end를 각각 1장 두고 middle만 필요한 만큼 반복한다. - `required_slot_missing`: cover의 `title`·`subtitle`과 모든 장의 `photo_url`을 채운다. - `invalid_asset_data_uri`: `photo_url`을 공개 HTTPS URL 또는 올바른 `data:image/;base64,...` 형식으로 바꾼다. 실사진은 원격 HTTPS URL을 사용한다. - `missing_brand_asset`: 프로젝트 브랜드에 `logo_horizontal` 데이터 URI를 등록한다. 이 템플릿의 로고는 선택 항목이므로 없으면 비워 둘 수 있다. - `text_overflow`: cover 제목이나 부제를 줄인다. - `low_resolution`: Instagram은 1080×1350, Threads는 1080×1080 이상의 원본을 사용한다.