Satori
JSX와 CSS를 SVG로 바꿔 주는 Vercel의 오픈소스 라이브러리 — 동적 Open Graph 이미지의 엔진
Satori는 JSX와 CSS의 일부 문법을 SVG로 렌더링하는 Vercel의 오픈소스 라이브러리이며, 나온 SVG는 PNG로 변환해 씁니다. Vercel의 Open Graph 이미지 생성과 Next.js의 ImageResponse 안에서 도는 엔진이 바로 이것이라, 소셜 카드를 만드는 대부분의 사람은 이름을 의식하지 않고 이미 쓰고 있습니다. 창업자에게는 유통과 관련된 좁은 문제를 제대로 풀어 줍니다. 내가 공유하는 링크에도, 남이 공유하는 링크에도 미리보기 이미지가 따라붙는데, Satori는 그 이미지를 페이지마다 손으로 디자인하는 대신 데이터에서 생성하게 해 줍니다.
Satori는 무엇인가
Satori(github.com/vercel/satori)는 JSX 요소를 받아 SVG 문자열을 돌려줍니다. TypeScript로 작성됐고 라이선스는 MPL-2.0입니다. 평범한 요소 트리와 버퍼 형태의 폰트를 넘기면 React Native가 쓰는 것과 같은 플렉스박스 엔진인 Yoga로 레이아웃을 계산해, PNG로 바꿀 수 있는 벡터 출력을 돌려줍니다. 브라우저, Node 16 이상, 웹 워커에서 모두 돌아가며 WebAssembly 의존성은 라이브러리 안에 함께 들어 있습니다.
의도적으로 브라우저가 아니며, 바로 그 제약이 핵심입니다. JSX는 순수하고 상태가 없어야 합니다. React 훅도, dangerouslySetInnerHTML도 쓸 수 없습니다. style 태그도 외부 스타일시트도 스크립트도 없고 모든 스타일은 인라인입니다. CSS 지원은 문서화된 부분집합이고 `display`의 기본값은 `flex`이며 받아들이는 값은 `flex`, `contents`, `none`뿐입니다. 그리고 출력이 브라우저와 픽셀 단위로 일치한다고 약속하지 않는다고 프로젝트가 분명히 밝히고 있습니다. 대부분은 라이브러리를 직접 만지지 않습니다. `next/og`나 Vercel의 OG 이미지 생성으로 만나게 되는데, 이들은 래스터라이저와 HTTP 응답으로 Satori를 감싼 것입니다. og-playground.vercel.app에 라이브 플레이그라운드도 있어서, 어떤 레이아웃이 표현 가능한지 가장 빨리 확인할 수 있습니다.
미리보기 이미지가 값을 하는 자리
소셜 카드는 링크를 따라 자동으로 이동하는 유일한 크리에이티브입니다. 어디에 뜰지 고를 수 없고, 가장 중요한 자리일수록 내 눈에는 잘 보이지 않습니다.
- 비공개 공유 슬랙, 왓츠앱, 다이렉트 메시지에 붙인 링크는 카드로 펼쳐지고, 그 카드가 당신을 처음 접한 사람에게는 제안의 전부입니다. 이 유입은 리퍼러 없이 도착하므로 Direct로 떨어집니다. 다크 소셜의 공백이고, 다시 말해 카드는 당신이 어떤 리포트에서도 볼 수 없는 일을 하고 있습니다.
- 사이트 단위가 아니라 페이지 단위 도메인 전체에 손으로 만든 카드 하나만 있으면 모든 페이지가 피드에서 똑같이 보입니다. 생성하면 비교 페이지, 릴리스 노트, 문서 페이지가 각자의 제목을 내걸 수 있습니다. "어떤 웹사이트"라고 말하는 링크와 "당신이 물어본 그 답"이라고 말하는 링크의 차이입니다.
- 프로그래매틱 페이지 템플릿 기반 페이지를 조금이라도 규모 있게 발행한다면 — 프로그래매틱 SEO, 디렉터리 등록, 연동별 랜딩 페이지 — 카드를 손으로 만드는 방식은 서른 번째 페이지쯤에서 무너집니다. 큰 페이지 묶음을 견디는 방식은 생성뿐입니다.
한계에 대해서도 솔직할 필요가 있습니다. `og:image`는 순위 신호가 아니고, 아무도 공유하고 싶어 하지 않는 페이지를 카드가 구해 주지도 않습니다. 카드가 바꾸는 것은 어차피 일어날 공유의 클릭률이며, 실재하지만 제한된 효과입니다. 새로운 채널이라기보다 좋은 타이틀 태그에 가까운 크기라고 보면 됩니다.
우리 카드를 배포하며 배운 것
우리는 AgentCeres — agentceres.com의 AI 그로스 오피서 — 의 소셜 카드를 `next/og`로 만듭니다. 안쪽은 Satori입니다. 그리고 카드 자체보다 비싸게 치른 일이 두 가지 있었습니다. 첫째는 오류 메시지가 전혀 없는 라우팅 실수였습니다. 규약 파일이 모든 페이지가 실제로 매달려 있는 레이아웃보다 한 단계 위 세그먼트에 놓여 있어서 이미지가 아예 만들어지지 않았고, 라우트는 조용히 404를 돌려줬습니다. 프로덕션에서 이걸 발견한 날은 2026년 7월 14일입니다. 아무 경고도 없습니다. 미리보기 이미지가 없는 상태는 미리보기 이미지를 아직 안 본 상태와 똑같아 보이고, 자기 링크를 어딘가에 붙여 봤다가 알몸으로 돌아올 때에야 알게 됩니다. 카드를 생성한다면 이미지 라우트를 실제로 요청해 200인지 확인하는 검사를 넣으세요.
둘째는 값은 쌌지만 누구에게나 해당됩니다. Satori의 레이아웃 규칙은 브라우저의 규칙이 아닙니다. 우리 카드에서는 모든 래퍼 요소에 `display: flex`를 명시했고, 브랜드 마크는 내려받는 대신 base64 데이터 URI로 심었으며, 카드는 로케일별로 미리 렌더링해 크롤링마다 엣지에서 그리는 대신 정적 파일이 되게 했습니다. 어느 것도 특이하지 않습니다. 페이지에 쓸 법한 CSS를 쓰려는 시도를 멈추는 순간 Satori 템플릿이 자연스럽게 도달하는 모양입니다. 도구 자체는 십오 분짜리 작업이고, 고민할 가치가 있는 부분은 내 링크가 남의 대화창에 떴을 때 낯선 사람이 무엇을 봐야 하는가입니다.
FAQ
- Satori와 @vercel/og는 같은 것인가요?
- 아닙니다. Satori는 JSX를 SVG로 바꾸는 레이아웃·렌더링 라이브러리입니다. @vercel/og와 next/og에서 내보내는 Next.js의 ImageResponse는 그 SVG를 PNG로 바꾸는 래스터라이저와 캐시 친화적인 HTTP 응답으로 Satori를 감싼 것입니다. 대부분의 애플리케이션은 감싼 쪽이면 충분하고, SVG 출력이 필요하거나 Next.js 밖에서 작업할 때 Satori를 직접 씁니다.
- 동적 Open Graph 이미지는 SEO에 도움이 되나요?
- 직접적으로는 아닙니다. og:image는 순위 신호가 아니며, 미리보기가 예쁘다고 구글이 순위를 올려 주지 않습니다. 효과는 유통 쪽에 있습니다. 카드는 공유된 링크가 피드나 대화창에서 클릭될지를 좌우합니다. 검색 성과가 아니라 공유 클릭률로 평가하세요.
- 주요 제약은 무엇인가요?
- CSS 지원은 문서화된 부분집합이고 Yoga의 플렉스박스로 계산되므로 block이나 grid 레이아웃은 브라우저와 다르게 동작합니다. style 태그와 외부 스타일시트는 지원되지 않고, 폰트는 버퍼로 넘겨야 하며, JSX는 순수하고 상태가 없어야 합니다. 그리고 출력이 브라우저 렌더링과 정확히 일치한다고 약속하지 않는다고 프로젝트가 분명히 밝힙니다. 이미지 한 장에 폰트와 이모지를 많이 실을수록 렌더링도 느려집니다.
- 카드를 요청 시점에 생성해야 하나요?
- 아니고, 오히려 그러지 않는 편이 나은 경우가 많습니다. 빌드 시점에 페이지가 정해져 있다면 이미지를 미리 렌더링해서, 크롤러가 올 때마다 함수를 부르는 대신 캐시에서 나가는 정적 파일이 되게 하세요. 우리가 로케일별로 미리 렌더링하는 이유도 정확히 그것입니다. 요청 시점 생성이 맞는 경우는 실시간 수치, 사용자 이름, 날짜가 들어간 리포트처럼 카드가 변하는 데이터를 비출 때입니다.
You built it. Now grow it.
AgentCeres is a managed AI marketing team — specialists draft the SEO, social, and outreach that fill your links, you approve what ships. 14-day free trial, from $39/month.