Editorial OG Card
각 포스트마다 일관된 링크 미리보기 이미지를 만듭니다. 각 카드는 다음으로 구성됩니다:
- 상단 룰: 키커(시리즈나 섹션), 토픽, 메타 라인(읽기 시간 또는 날짜)
- 큰 세리프체로 설정된 제목 (실제 텍스트로 렌더링되어 항상 올바르게 표시되며, 필요한 만큼만 축소, 강조색으로 표시되는 한두 단어)
- 이니셜 또는 사진이 있는 바이라인, 포스트 URL
- 오른쪽 아트: 포스트 슬러그에서 선택된 4가지 블록 컴포지션 중 하나, 직접 선택한 이미지, 또는 없음 (제목이 더 넓게 표시됨)
스크립트가 포스트를 cards.json으로 읽어오고, 사용자가 검토한 후 두 번째 스크립트가 시스템에 설치된 Chrome을 사용해 모든 카드를 렌더링합니다. 설치할 것이 없습니다.
준비 사항
- Node.js 18 이상
- Chrome, Chromium, 또는 Edge. 스크립트가 일반적인 위치에서 찾습니다. 찾지 못하면
CHROME_PATH를 설정하세요. - 렌더링 중 인터넷 접속 (Google Fonts 스타일시트: Instrument Serif, Inter, IBM Plex Mono). 오프라인 렌더링은 문제 해결 섹션을 참조하세요.
이 스킬의 폴더는 <skill>입니다: 이 SKILL.md 파일이 있는 폴더입니다.
단계
1. cards.json 만들기
마크다운 포스트에서 (Astro, Hugo, Jekyll, Eleventy, Next.js MDX 등 프론트매터를 사용하는 모든 것):
node <skill>/scripts/from-posts.mjs content/blog --site=yourblog.example --path=/blog/ --out-dir=public/og
이 명령은 폴더 아래의 모든 .md, .mdx, .markdown 파일을 읽어 최신 포스트 순으로 각각 하나의 카드를 만듭니다. draft: true인 포스트는 건너뜁니다. 프론트매터에서 가져오는 필드는 다음과 같습니다:
| 카드 필드 | 출처 |
|---|---|
slug | slug, 없으면 permalink, 없으면 파일 이름(index.md의 경우 폴더명), 앞의 날짜는 제거 |
title | og_title, 없으면 title |
kicker | kicker, 없으면 series, 없으면 --kicker=..., 없으면 "Article" |
topic | topic, 없으면 첫 번째 category, 없으면 첫 번째 태그 |
meta | reading_time, 없으면 분당 230단어 기준 단어 수: "8 min read" |
author | author (텍스트, 또는 그 아래 name), 없으면 --author=... |
url | --site + --path + slug, 저자 아래 표시 |
out | <out-dir>/<slug>.png; --out-dir 없으면 포스트 옆에 <name>.og.png (index.md 옆에는 og.png) |
포스트가 추가될 때마다 다시 실행하세요: cards.json에 이미 있는 카드는 편집 내용이 유지되고, 새 포스트는 추가되며, 삭제된 포스트는 제거됩니다.
수동으로 (뉴스레터, 랜딩 페이지, 문서 섹션 등 마크다운이 아닌 페이지의 경우) 같은 구조를 직접 작성하세요:
{
"theme": { "paper": "#131313", "ink": "#f2ede4", "accent": "#ff7a45" },
"defaults": { "kicker": "Night Desk", "author": "Ines Varga" },
"cards": [
{
"slug": "issue-41",
"title": "Why every newsroom is quietly *rebuilding its CMS*",
"topic": "Issue 41",
"meta": "Oct 2026",
"url": "nightdesk.example/41",
"tags": ["#media", "№ 41"]
}
]
}
defaults는 카드에서 생략된 필드를 채웁니다.
2. 카드 편집
cards.json을 열고 제목을 확인하세요:
- 핵심을 담은 한두 단어를
*별표*로 감싸면 강조색 이탤릭체로 표시됩니다. 예:"Shipping a design system *in a week*". - 제목은 70자 이내가 가장 좋으며, 제한은 110자입니다. 약 80자를 넘으면 글꼴이 작아집니다. 포스트 프론트매터에서
og_title을 사용하거나cards.json에서 직접 줄이세요.
카드가 가질 수 있는 모든 필드:
| 필드 | 설명 | 제한 |
|---|---|---|
title | 제목 (필수) | 110 |
slug | 파일 이름을 지정하고 레이아웃을 선택 (out이 설정되지 않은 경우 필수) | – |
out | PNG 파일 경로 (cards.json 기준, 기본값 og/<slug>.png) | – |
kicker | 왼쪽 상단 라벨 (강조색) | 24 |
topic | 키커 뒤에 오는 텍스트; 아트의 기본 해시태그 | 28 |
meta | 오른쪽 상단: 읽기 시간, 날짜, 이슈 | 20 |
author | 바이라인 이름; 이니셜은 원 안에 표시 | 32 |
avatar | 이니셜 대신 원 안에 표시할 사진 (경로 또는 URL) | – |
url | 이름 아래 표시 | 48 |
art | blocks (기본값), image 또는 none | – |
image | "art": "image"일 때 사용할 사진 (경로 또는 URL); 390×430으로 잘림 | – |
layout | 1~4, 블록 컴포지션을 직접 선택 | – |
tags | 아트 위에 표시할 최대 두 개의 작은 라벨, 예: ["#css", "Part 2"] | 각 16 |
테마는 모든 카드의 모양을 결정합니다: paper (배경), ink (텍스트), accent (강조색)을 16진수로 지정하고, serif, sans, mono로 다른 Google Fonts 패밀리를 지정할 수 있습니다. 강조색은 블록에 그대로 사용되며, 텍스트의 경우 카드가 읽을 수 있도록 자동으로 어둡게 또는 밝게 조정합니다. 두 가지 기본 테마: 기본 잉크 블루 (#fafaf7, #17181c, #2433e8)와 나이트 (#131313, #f2ede4, #ff7a45).
3. 렌더링
node <skill>/scripts/render.mjs cards.json
먼저 모든 카드를 확인하여 문제를 한 번에 나열한 후, 한 번에 4개씩(각 1초 정도 소요) 카드를 렌더링하고 모든 PNG가 1200×630인지 확인합니다. --scale=2를 추가하면 2400×1260 이미지(고밀도 화면에서 더 선명, 파일 크기는 약 2배)를 생성하고, --only=slug,slug로 일부 카드만 다시 렌더링할 수 있습니다.
4. 확인
cards.json 옆에 생성된 og-preview.html을 엽니다: 모든 카드가 나란히 표시됩니다. 제목이 많이 줄어든 경우(줄이기), 강조 단어가 잘못된 구문에 있는 경우, 인접한 카드가 충분히 다르게 보이는지 확인하세요. 카드의 layout을 변경하여 다른 구성을 선택할 수 있습니다.
5. 사이트에 연결
각 페이지가 절대 URL로 이미지를 가리켜야 카드가 미리보기에 표시됩니다. 페이지의 <head>에:
<meta property="og:image" content="https://yourblog.example/og/design-system-in-a-week.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Shipping a design system in a week" />
<meta name="twitter:card" content="summary_large_image" />
포스트 레이아웃이나 템플릿에서 슬러그를 기반으로 한 번만 구현하면 됩니다 (포스트별로 직접 추가하지 않음):
- Next.js (App Router): 포스트 라우트의
generateMetadata에서openGraph: { images: [{ url:/og/${slug}.png, width: 1200, height: 630 }] }와twitter: { card: 'summary_large_image' }를 반환하고, 루트 레이아웃에서metadataBase를 설정하여 URL이 절대 경로가 되도록 합니다. - Astro, Eleventy, Jekyll, Hugo: 베이스 레이아웃의 head에 사이트 URL과
/og/및 페이지의 슬러그를 사용하여 태그를 추가합니다. - 포스트 옆에 이미지 (
--out-dir없음): 프레임워크가 동일 위치 파일을 제공하는 경로를 사용합니다.
배포 후 포스트 URL을 링크 미리보기 체커에 붙여넣거나 X, LinkedIn, Slack의 초안에 붙여넣어 카드를 확인하세요. 플랫폼은 미리보기를 캐싱하므로 변경된 이미지가 표시되기까지 시간이 걸릴 수 있습니다.
6. 지속적 실행 (선택 사항)
빌드할 때마다 새 포스트의 카드를 만들려면 이 스킬의 scripts/와 template/ 폴더를 저장소(예: tools/og/)에 복사하고 빌드 전에 두 스크립트를 실행하세요. 예를 들어 package.json의 prebuild 스크립트로:
"prebuild": "node tools/og/scripts/from-posts.mjs content/blog --site=yourblog.example --path=/blog/ --out-dir=public/og && node tools/og/scripts/render.mjs cards.json"
빌드 머신에 Chrome이 필요합니다. 대부분의 CI 이미지에 Chrome이 설치되어 있거나 CHROME_PATH를 설정하세요.
문제 해결
render: fix cards.json first: 각 줄이 카드, 필드, 제한을 알려줍니다.- Chrome을 찾을 수 없음: Chrome 또는 Chromium을 설치하거나
CHROME_PATH에 실행 파일 경로를 설정하세요. - 제목이 일반 세리프체로 표시되거나 라벨이 시스템 글꼴로 표시됨: Google Fonts 스타일시트가 로드되지 않았습니다. 오프라인 렌더링을 위해 Instrument Serif, Inter, IBM Plex Mono(모두 Google Fonts에서 무료)를 설치하고 테마에
"fonts": ""를 설정하세요."fonts"는 다른 패밀리를 위한 자체 스타일시트 URL도 사용할 수 있습니다. - 플랫폼에서 미리보기가 오래된 이미지를 표시하거나 표시되지 않음:
og:imageURL이 브라우저에서 열리고 절대 경로(https://와 도메인 포함)인지 확인한 후, 플랫폼에 페이지 재스캔을 요청하거나 캐시가 만료될 때까지 기다리세요.
파일
scripts/from-posts.mjs: 프론트매터를cards.json으로 변환, 이후 실행 시 편집 내용 유지. 의존성 없음.scripts/render.mjs:cards.json확인, PNG 렌더링,og-preview.html작성. 의존성 없음.scripts/chrome.mjs: Chrome을 찾고 스크린샷을 촬영.template/: 카드 자체.card.html과card.css는 레이아웃,card.js는 텍스트를 채우고, 읽기 쉬운 색상을 선택하며, 아트를 그리고 제목을 맞춤.examples/field-notes/: 작은 블로그(posts/)와from-posts.mjs로 만든cards.json(강조 단어 추가됨).examples/night-desk/: 뉴스레터용 수동 작성cards.json(다크 모드). Lookbk의 미리보기는 이 예제에서 렌더링되었습니다.NOTICE.md: 이 스킬이 사용하는 글꼴 및 소프트웨어.