카톡이나 페이스북에 글을 공유했는데 썸네일이 안 뜨면, 이미지 파일을 다시 그리기부터 하기 쉽습니다. 그런데 플랫폼이 읽는 건 디자인 파일이 아니라 배포 URL HTML에 박힌 메타 태그입니다. og:image가 https://로 시작하는 절대 URL이어야 하고, 그 URL이 크롤러 User-Agent로도 200과 실제 이미지를 줘야 카드가 나옵니다.
로컬 브라우저에서 되는 것처럼 보여도 크롤러가 받는 HTML은 다른 경우가 많습니다. 상대 경로가 프리뷰 도메인으로 붙거나, 한글이 렌더링되지 않은 빈 PNG가 나가거나, 이미지 URL이 401을 주는 식입니다. 시안 손질은 그다음입니다.
이 글은 Next.js App Router의 generateMetadata와 ImageResponse를 기준으로, 한국어 블로그에서 자주 실패하는 설정만 순서대로 다룹니다. metadataBase, Satori 한글 폰트 서브셋, 파일 컨벤션과 라우트 핸들러의 캐시 차이, 배포 URL 검증 스크립트까지 포함합니다. 페이지 캐시 무효화와 발행 루프는 DB만 바꾸고 발행하기와 같이 보면 됩니다.
공유 카드에 필요한 메타 태그 여섯 개
제목, 설명, 이미지, URL만으로는 부족합니다. 플랫폼이 읽는 최소 집합은 이렇습니다.
| 태그 | 역할 | 빠지거나 틀리면 |
|---|---|---|
og:title | 카드 제목 | 브라우저 <title>로 대체되거나 비어 보임 |
og:description | 요약 | 카드에 본문 일부가 잘려 들어감 |
og:image | 썸네일 | 상대 경로면 대부분 실패. 절대 URL 필수 |
og:url | 정규 URL | canonical과 어긋나면 다른 페이지가 캐시됨 |
og:image:width / height | 크기 힌트 | 첫 크롤 때 카드가 작게 뜨거나 레이아웃이 튐 |
twitter:card | 카드 형태 | summary_large_image 없으면 작은 정사각 카드 |
og:image:width와 height를 명시하는 이유는 성능이 아니라 첫 크롤 타이밍 때문입니다. 플랫폼이 이미지를 아직 다운로드하지 못한 상태에서 카드를 그릴 때, 크기 힌트가 있으면 큰 카드 레이아웃을 미리 잡습니다. 없으면 작은 카드로 그렸다가 나중에 바뀝니다.
metadataBase, Next.js에서 가장 자주 실패하는 설정
openGraph.images에 /og/post.png 같은 상대 경로를 쓰면 Next.js는 metadataBase를 기준으로 절대 URL을 만듭니다. metadataBase를 설정하지 않으면 빌드 에러가 나거나, Vercel 환경변수에서 유추한 프리뷰 도메인이 박힙니다. 프로덕션 HTML에 og:image로 https://myblog-git-main-xxx.vercel.app/...이 들어가 있는 사고가 여기서 나옵니다.
루트 레이아웃에 한 번만 박으면 하위 전 라우트가 상속합니다.
// app/layout.tsx
import type { Metadata } from 'next'
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://vibecoding365days.com'
export const metadata: Metadata = {
metadataBase: new URL(siteUrl),
title: {
default: 'VIBE 코딩 365',
template: '%s | VIBE 코딩 365',
},
openGraph: {
siteName: 'VIBE 코딩 365',
locale: 'ko_KR',
type: 'website',
},
twitter: {
card: 'summary_large_image',
},
}
프로덕션에 NEXT_PUBLIC_SITE_URL을 반드시 넣으세요. 폴백에만 의존하면 도메인을 바꾼 날 조용히 틀립니다.
한 가지 함정이 더 있습니다. 메타데이터는 얕은 병합(shallow merge) 입니다. 하위 페이지에서 openGraph를 정의하는 순간 레이아웃의 openGraph 필드가 전부 덮어써집니다. siteName이나 locale을 유지하려면 각 페이지에서 다시 쓰거나, 공통 객체를 스프레드해야 합니다.
⚠️ 주의: 상대 경로 og:image를 프로덕션에서 눈으로만 확인하면 브라우저가 현재 호스트로 해석해 "되는 것처럼" 보일 수 있습니다. 크롤러는 HTML에 적힌 절대 URL만 봅니다.페이지 메타데이터를 데이터와 실제로 연결하기
generateMetadata에서 맞추라는 말만으로는 부족합니다. 실제 코드는 이렇습니다.
// app/[category]/[slug]/page.tsx
import type { Metadata } from 'next'
import { getPost } from '@/lib/posts'
export async function generateMetadata(
{ params }: { params: Promise<{ category: string; slug: string }> }
): Promise<Metadata> {
const { category, slug } = await params
const post = await getPost(category, slug)
if (!post) return {}
const path = `/${category}/${slug}`
// updatedAt을 쿼리에 붙여 플랫폼 캐시를 강제로 갈아치운다
const ogUrl = `/api/og?slug=${encodeURIComponent(slug)}&v=${post.updatedAtEpoch}`
return {
title: post.title,
description: post.summary,
alternates: { canonical: path },
openGraph: {
type: 'article',
title: post.title,
description: post.summary,
url: path,
siteName: 'VIBE 코딩 365',
locale: 'ko_KR',
publishedTime: post.publishedAt,
modifiedTime: post.updatedAt,
images: [{ url: ogUrl, width: 1200, height: 630, alt: post.title }],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.summary,
images: [ogUrl],
},
}
}
alternates.canonical과 openGraph.url을 같은 경로로 두는 게 핵심입니다. 둘이 어긋나면 플랫폼이 어느 URL을 캐시할지 제멋대로 정합니다.
파일 컨벤션이냐 라우트 핸들러냐, 캐시가 결정한다
Next.js는 동적 OG 이미지 생성 방법을 두 가지 줍니다. 대부분의 글이 "둘 다 됩니다"로 끝내는데, 운영에서는 전혀 다른 물건입니다.
opengraph-image.tsx 파일 컨벤션은 편합니다. 메타 태그를 Next가 알아서 넣어주고, 빌드 타임에 정적 최적화됩니다. 문제는 Next가 이미지 URL에 붙이는 캐시버스팅 쿼리가 라우트 파일 기준 해시라는 점입니다. 코드를 안 고치고 DB에서 제목만 바꾸면 URL이 그대로입니다. 플랫폼 캐시를 비워도 Next가 옛 이미지를 다시 내주는 상황이 생깁니다. revalidatePath로 세그먼트를 무효화해도 OG 이미지 라우트는 안 따라오는 케이스가 보고되어 있습니다.
콘텐츠가 런타임에 바뀌는 사이트라면 라우트 핸들러를 쓰세요. /api/og?slug=...&v=<updatedAt>처럼 URL 자체에 버전을 넣으면, 제목을 고치는 순간 URL이 달라지고 Next 캐시, CDN, 플랫폼 캐시가 전부 자동으로 갈립니다. 캐시 무효화를 안 해도 됩니다.
| 상황 | 권장 |
|---|---|
| 글이 빌드 타임에 고정 (MDX 커밋 후 배포) | opengraph-image.tsx 파일 컨벤션 |
| 글이 DB에 있고 런타임에 수정됨 | 라우트 핸들러 + ?v=updatedAt |
| 프로필/상품처럼 사용자마다 다름 | 라우트 핸들러 (searchParams 필요) |
파일 컨벤션은 searchParams를 못 받는다는 제약도 있습니다. 쿼리로 변형을 주고 싶으면 처음부터 라우트 핸들러입니다.
한글이 안 나온다, Satori의 폰트 제약
여기가 한국어 블로그에서 제일 많이 터지는 곳입니다. ImageResponse는 내부적으로 Satori로 JSX를 SVG로, Resvg로 PNG로 바꿉니다. Satori에 폰트를 명시하지 않으면 기본 라틴 폰트만 씁니다. 한글은 빈 칸이나 두부로 나옵니다. 로컬에서 안 보이고 배포 후 카톡에 공유했을 때 발견되는 게 보통입니다.
그런데 한글 폰트 TTF는 보통 3~8MB입니다. ImageResponse 번들 상한은 500KB(JSX, CSS, 폰트, 이미지 전부 포함)라 통째로 넣으면 실패합니다. 지원 포맷도 ttf, otf, woff뿐입니다. woff2는 안 됩니다.
해법은 필요한 글자만 서브셋하는 것입니다. Google Fonts CSS2 API의 text= 파라미터가 요청한 글자만 담은 폰트를 돌려줍니다.
// lib/og-font.ts
export async function loadKoreanFont(text: string): Promise<ArrayBuffer> {
const family = 'Noto+Sans+KR:wght@700'
const url = `https://fonts.googleapis.com/css2?family=${family}&text=${encodeURIComponent(text)}`
// UA를 안 보내면 woff2를 돌려줍니다. Satori는 woff2를 못 읽습니다.
const css = await fetch(url, {
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1)' },
next: { revalidate: 60 * 60 * 24 },
}).then((r) => r.text())
const match = css.match(/src:\s*url\(([^)]+)\)\s*format\('(?:opentype|truetype)'\)/)
if (!match) throw new Error('폰트 서브셋 URL 파싱 실패')
const res = await fetch(match[1])
if (!res.ok) throw new Error(`폰트 다운로드 실패: ${res.status}`)
return res.arrayBuffer()
}
구형 UA를 보내는 이유가 핵심입니다. 최신 브라우저 UA로 요청하면 Google이 woff2를 주고, Satori가 파싱에 실패합니다.
전체 라우트 핸들러는 아래처럼 둡니다.
// app/api/og/route.tsx
import { ImageResponse } from 'next/og'
import { NextRequest } from 'next/server'
import { getPostMeta } from '@/lib/posts'
import { loadKoreanFont } from '@/lib/og-font'
export const runtime = 'nodejs'
export async function GET(req: NextRequest) {
const slug = req.nextUrl.searchParams.get('slug')
if (!slug) return new Response('slug required', { status: 400 })
const post = await getPostMeta(slug)
const title = post?.title ?? 'VIBE 코딩 365'
const category = post?.category ?? 'vibe-coding'
// 렌더링에 쓰는 모든 글자를 서브셋 요청에 포함시켜야 합니다
const text = `${title}${category}VIBE 코딩 365`
const font = await loadKoreanFont(text)
return new ImageResponse(
(
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
width: '100%',
height: '100%',
padding: '72px 80px',
background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)',
color: '#f8fafc',
fontFamily: 'NotoKR',
}}
>
<div style={{ display: 'flex', fontSize: 30, color: '#38bdf8' }}>
{category}
</div>
<div
style={{
display: 'flex',
fontSize: 68,
lineHeight: 1.25,
letterSpacing: '-0.02em',
}}
>
{title}
</div>
<div style={{ display: 'flex', fontSize: 28, color: '#94a3b8' }}>
VIBE 코딩 365
</div>
</div>
),
{
width: 1200,
height: 630,
fonts: [{ name: 'NotoKR', data: font, style: 'normal', weight: 700 }],
headers: {
'Cache-Control': 'public, max-age=31536000, immutable',
},
}
)
}
?v=updatedAt으로 URL이 바뀌니 immutable 캐시를 걸어도 안전합니다. 같은 URL은 영원히 같은 이미지, 내용이 바뀌면 새 URL입니다.
Satori에서 자주 터지는 것들
display: flex 명시가 제일 많습니다. 자식 노드가 둘 이상인 <div>에 display가 없으면 Expected <div> to have explicit "display: flex" or "display: none" 에러가 납니다. 문자열과 변수를 섞어 쓴 곳(<div>{a}건</div>)도 자식이 둘로 세어져서 걸립니다. 그 외에 display: grid는 아예 미지원이고, position: absolute와 flexbox 조합으로 풀어야 합니다.
더 나쁜 건 이 에러가 로컬 개발에서만 콘솔에 뜨고 프로덕션에서는 조용히 빈 이미지로 나가는 경우가 있다는 점입니다. 그래서 배포 후 실제 이미지 URL을 눈으로 열어보는 절차가 필요합니다.
💡 Tip: 배포 직후og:imageURL을 브라우저 새 탭으로 한 번 여세요. curl이 200을 줘도 한글이 두부면content-length가 비정상적으로 작을 때가 많습니다.
크롤러는 JavaScript를 실행하지 않는다
facebookexternalhit, KakaoTalk-Scrap 같은 크롤러는 HTML만 읽습니다. 클라이언트에서 document.title이나 meta 태그를 주입하는 SPA는 카드가 안 나옵니다.
Next.js App Router에는 이와 관련된 미묘한 동작이 있습니다. 스트리밍 메타데이터입니다. Next 15.2부터 generateMetadata가 느리면 초기 UI를 먼저 보내고 메타 태그를 나중에 <body>에 붙입니다. Googlebot처럼 JS를 실행하는 봇은 괜찮지만, HTML만 읽는 봇에게는 문제가 되므로 Next가 User-Agent로 이런 봇들을 감지해서 그 경우에만 렌더링을 블로킹하고 <head>에 메타를 넣습니다.
즉 기본값은 안전합니다. 하지만 htmlLimitedBots 설정을 직접 건드렸거나, 리스트에 없는 크롤러(사내 메신저, 신생 플랫폼)를 상대한다면 카드가 빕니다. 커스텀 봇을 지원해야 하면 next.config에서 리스트를 확장하거나 스트리밍을 끄세요. 기본 리스트는 Next 저장소에 있습니다.
크롤러가 아예 못 들어오는 경우
"robots에 막힌 이미지" 한 줄로만 끝내기 쉽지만, 실제 사고 목록은 더 깁니다.
Vercel Deployment Protection. 프리뷰 배포나 보호가 켜진 프로덕션은 크롤러에게 401을 줍니다. "로컬에선 되는데 배포하면 빈 카드"의 가장 흔한 원인입니다. 프로덕션 도메인의 보호를 끄거나, OG 검증은 반드시 공개 URL로 하세요.
서명 URL / 만료 토큰. S3 presigned URL을 og:image에 넣으면 며칠 뒤 만료되면서 그때부터 공유되는 링크가 전부 빈 카드가 됩니다. 이미 캐시된 카드는 살아 있어서 발견이 늦습니다.
리다이렉트 체인. og:image가 301/302를 거치면 일부 크롤러가 포기합니다. 최종 URL을 직접 쓰세요.
용량 초과. Open Graph 이미지 8MB, Twitter 이미지 5MB가 상한입니다. ImageResponse PNG는 보통 50~200KB라 문제없지만, 디자이너가 준 4K PNG를 그대로 올리면 걸립니다.
미들웨어. 인증 미들웨어가 /api/og를 가로채면 크롤러가 로그인 페이지 HTML을 받습니다. matcher에서 OG 경로를 제외했는지 확인하세요.
재수집, 지금 실제로 쓸 수 있는 도구
X Card Validator(cards-dev.twitter.com/validator)는 프리뷰 기능이 제거됐습니다. 지금 링크를 걸면 독자가 빈 화면을 봅니다. 2026년 7월 기준 실제 상황입니다.
| 플랫폼 | 도구 | 상태 |
|---|---|---|
| Facebook / Instagram | Sharing Debugger | 정상. "Scrape Again"으로 강제 재수집 |
| 카카오톡 | 공유 디버거 | 정상. 로그인 후 "캐시 초기화" |
| X | 공식 검증기 | 프리뷰 제거됨. 작성창(composer)에 URL을 붙여 미리보기로 확인 |
| Slack | 없음 | 30분 내외 자동 만료. 같은 대화방에 1시간 내 재공유하면 언퍼링 안 됨 |
| Discord | 없음 | 자체 프록시 캐시. URL에 ?v= 붙이는 게 유일하게 확실 |
| Post Inspector | 정상 |
Slack과 Discord는 수동 무효화 수단이 없습니다. 그래서 ?v=updatedAt 패턴이 선택이 아니라 필수에 가깝습니다. 디버거로 비울 수 있는 플랫폼은 절반뿐입니다.
검증, 배포 URL을 크롤러 눈으로 보기
브라우저로 열어보는 건 검증이 아닙니다. 크롤러 User-Agent로 받아야 합니다. 프로젝트 루트에 아래 스크립트를 두고 배포 URL을 넘깁니다.
#!/usr/bin/env bash
# scripts/check-og.sh <url>
set -euo pipefail
URL="$1"
UA="facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"
html=$(curl -sL -A "$UA" --max-time 15 "$URL")
echo "=== 메타 태그 ==="
echo "$html" | grep -oE '<meta (property|name)="(og|twitter):[^"]*" content="[^"]*"' || {
echo "OG 태그 없음. 크롤러가 받은 HTML에 메타가 없습니다"; exit 1; }
img=$(echo "$html" \
| grep -oE 'property="og:image" content="[^"]*"' \
| head -n1 | sed 's/.*content="//; s/"$//')
echo
echo "=== og:image: $img ==="
case "$img" in
https://*) ;;
*) echo "실패: 절대 URL이 아닙니다 (metadataBase 확인)"; exit 1 ;;
esac
curl -sI -A "$UA" --max-time 15 "$img" \
| grep -iE '^(HTTP/|content-type|content-length|cache-control)'
통과 기준은 세 가지입니다. og:image가 https://로 시작하는 절대 URL일 것, 그 URL이 크롤러 UA로 200을 줄 것, content-type이 image/png 계열이고 content-length가 0이 아닐 것.
content-length가 비정상적으로 작으면(2~3KB) 한글이 렌더링 안 된 빈 이미지일 가능성이 높습니다. 스크립트만으로는 못 잡으니, 배포 후 이미지 URL을 브라우저 새 탭으로 한 번은 눈으로 확인하세요.
확인: ./scripts/check-og.sh https://example.com/vibe-coding/my-post가 절대 URL과 200을 찍는지 봅니다.
디자인은 마지막에, 제약부터
메타와 이미지 URL이 통과한 다음에 레이아웃을 손보세요. 카드는 모바일에서 폭 300~350px로 줄어듭니다. 1200px 캔버스의 68px 글자가 모바일에서 20px 정도로 보인다고 생각하고 크기를 잡으세요.
제목은 두 줄이 한계입니다. 세 줄 넘어가면 플랫폼이 카드 높이를 잘라서 아래가 사라집니다. 데이터에서 오는 제목 길이를 신뢰하지 말고 코드에서 자르세요.
const title = raw.length > 40 ? `${raw.slice(0, 39)}…` : raw
가장자리 60~80px는 여백으로 비웁니다. 일부 플랫폼이 카드를 살짝 크롭합니다. 배경과 글자 대비는 최소 4.5:1을 지키세요. 그라디언트 배경에 회색 글자를 얹으면 "이미지는 있는데 안 읽히는" 카드가 됩니다.
포맷은 PNG를 권합니다. ImageResponse의 기본 출력이고, WebP는 플랫폼별 지원이 여전히 고르지 않습니다. 이건 최적화할 자리가 아닙니다.
프레임워크가 달라도 확인하는 건 같다
Astro든 Cloudflare Worker든 정적 HTML이든, 크롤러가 받는 <head>에 절대 URL로 된 og:image가 있으면 카드는 만들어집니다. Next.js의 generateMetadata는 그걸 만드는 한 가지 방법일 뿐입니다.
바꿔 말하면 어디를 볼지도 프레임워크와 무관합니다. 배포 URL을 크롤러 UA로 curl해서 나온 HTML이 기준입니다. 로컬 dev 서버, 브라우저 개발자 도구의 Elements 탭(JS 실행 후 DOM), 프리뷰 배포는 전부 다른 것을 보여줄 수 있습니다.
운영, 제목을 고칠 때
DB에서 글 제목을 수정하면 세 군데가 따라가야 합니다. 페이지 HTML의 og:title, OG 이미지 안에 그려진 텍스트, 그리고 플랫폼에 이미 캐시된 카드.
?v=updatedAt 패턴을 쓰면 앞의 둘은 자동입니다. 세 번째는 이미 공유된 링크라 어쩔 수 없지만, 새로 공유되는 링크는 새 URL을 물고 나갑니다. 페이지 캐시 무효화(revalidatePath)와 함께 처리하는 발행 루프에 넣으세요.
PR 체크리스트에 넣을 문장 하나: "제목, 요약, 카테고리를 바꿨다면 배포 URL에서 check-og.sh를 돌린다."
AI에게 맡길 때 적어 줄 제약
"OG 이미지 예쁘게 만들어줘"만 넘기면 레이아웃 시안만 나옵니다. 아래처럼 제약을 먼저 적으세요.
app/api/og/route.tsx에 OG 이미지 라우트를 만들어라.
1200x630, next/og의 ImageResponse 사용, runtime = 'nodejs'.
제약:
- Satori는 display: grid 미지원
- 자식 2개 이상인 div에 display: flex 명시 필수
- 폰트는 ttf/otf만 가능하고 번들 500KB 이하
한글 렌더링을 위해 Google Fonts CSS2 API의 text= 파라미터로 서브셋을 받되,
구형 User-Agent를 보내 ttf를 받아라(woff2는 파싱 실패).
generateMetadata에서 openGraph.images에 /api/og?slug=...&v=<updatedAt>을 넣고,
alternates.canonical과 openGraph.url을 동일 경로로 맞춰라.
루트 레이아웃에 metadataBase가 없으면 추가하라.
이 정도를 적으면 첫 시도에서 도는 코드가 나옵니다. 안 적으면 display: grid를 쓴 코드가 나와서 두 시간을 태웁니다.
출처와 링크
- opengraph-image 파일 컨벤션 (Next.js): 기본 정적 최적화, 파일 크기 상한
- generateMetadata (Next.js):
metadataBase, URL 합성, 얕은 병합, 스트리밍 메타데이터 - Metadata and OG images (Next.js Getting Started): 현재 가이드 경로
- ImageResponse (Next.js): 번들 500KB, ttf/otf/woff만, grid 미지원
- Satori CSS 지원 범위: flex 제약
- Images in Link Shares (Meta): 권장 1200x630, 8MB 상한
- Facebook Sharing Debugger: 강제 재수집
- 카카오 공유 디버거: 캐시 초기화
- Open Graph protocol: 태그 규약
- DB만 바꾸고 발행하기: 페이지 캐시와 발행 루프
조사 기준: 2026년 7월. X 공식 검증기의 프리뷰 기능은 제거된 상태이며, 플랫폼 캐시 정책은 변경될 수 있으니 배포 후 실제 공유로 확인하세요.
FAQ
자주 묻는 질문
파일 컨벤션(opengraph-image.tsx)을 쓰면 안 되나요?
콘텐츠가 빌드 타임에 고정된다면 오히려 그게 낫습니다. 메타 태그를 Next가 알아서 넣어주고, 빌드 시 한 번만 생성되니 런타임 비용이 없습니다. DB에서 내용이 바뀌는 구조에서만 캐시 문제가 생깁니다.
Edge runtime을 써야 하나요?
nodejs 런타임을 권합니다. 로컬 파일에서 폰트를 읽거나 DB에 직접 붙을 수 있고, 요즘은 콜드스타트 차이도 크지 않습니다. Edge는 폰트를 fetch로만 가져올 수 있어 제약이 더 큽니다.
폰트 서브셋을 매 요청마다 fetch하면 느리지 않나요?
next: { revalidate: 86400 }으로 캐시되고, ?v= 덕분에 이미지 자체도 CDN에서 오래 살아 있어서 실제 생성 횟수는 글 하나당 몇 번 수준입니다. 걱정되면 자주 쓰는 글자를 미리 서브셋한 TTF를 assets에 두고 readFile로 읽으세요. 보통 300~400KB로 맞출 수 있습니다.
카드가 여전히 옛날 이미지이면 어디부터 보나요?
배포 HTML의 og:image URL이 실제로 바뀌었는지 먼저 보세요. 안 바뀌었으면 ?v= 로직 문제입니다. URL은 새데 열어보면 옛 이미지면 Next나 CDN 캐시입니다. 둘 다 새데 카드만 옛것이면 플랫폼 캐시이니 디버거로 재수집하세요.
검증 도구가 이미지를 가져올 수 없다고 하면요?
크롤러 UA로 curl해서 상태 코드를 보세요. 401이면 배포 보호, 403이면 미들웨어나 WAF, 404면 경로, 200인데 실패면 용량이나 content-type입니다. 서명 URL 만료도 같은 증상으로 나타납니다.
X Card Validator는 왜 쓰면 안 되나요?
프리뷰 기능이 제거되어 지금 열면 확인할 게 없습니다. X는 작성창에 URL을 붙여 미리보기로 확인하고, Facebook과 카카오는 공식 디버거로 재수집하세요. Slack과 Discord는 수동 무효화가 없어 ?v=가 사실상 필수입니다.
이 사이트처럼 Astro나 Worker여도 같은가요?
확인 기준은 같습니다. head에 절대 URL og:image가 있고 그 URL이 200을 주면 됩니다. Next 전용은 generateMetadata와 ImageResponse 구현 경로일 뿐이고, 디버깅은 항상 배포 URL을 크롤러 UA로 curl하는 일입니다.
RELATED TERMS
본문과 함께 보면 좋은 사전 용어
Google이 오픈소스로 공개한 AI 에이전트 CLI 도구로, 터미널에서 코드 이해, 파일 조작, 명령어 실행, 대규모 코드베이스 편집을 지원한다. Google의 Gemini 모델을 기반으로 하며, Gemini의 멀티모달 능력을 활용하여 텍스트뿐 아니라 이미지와 비디오 생성까지 가능하다는 것이 독특한 차별점이다. 예를 들어, '이 UI의 스크린샷을 보고 React 컴포넌트를 만들어줘'와 같은 시각적 입력 기반 코딩이 가능하다. 오픈소스 프로젝트로 커뮤니티 기여가 가능하며, Google Cloud 생태계(Cloud Functions, Firebase, Vertex AI 등)와 긴밀하게 통합된다. Gemini Code Assist(IDE 기반 도구)와도 연동되어, CLI에서 시작한 작업을 IDE에서 이어서 할 수 있다. Claude Code, Codex CLI와 함께 3대 CLI 코딩 에이전트를 형성하며, Google 계정만 있으면 무료로 사용할 수 있어 진입 장벽이 낮다.
프론트엔드·UI 모달 다이얼로그모달 다이얼로그는 사용자가 현재 화면 흐름을 잠시 멈추고 확인, 입력, 삭제 승인처럼 중요한 결정을 하도록 띄우는 UI 창이다. VIBE 코딩에서 AI에게 모달을 만들게 할 때는 열림·닫힘 상태, 포커스 이동, Esc 닫기, 배경 클릭 정책, 모바일 화면, 접근성 검증까지 함께 요구해야 실제 서비스에서 안전하게 동작한다.
AI 모델·프로바이더 제미나이Google DeepMind가 개발한 멀티모달 AI 모델 시리즈로, 텍스트·이미지·오디오·비디오·코드를 통합적으로 처리할 수 있는 것이 가장 큰 차별점이다. 다른 모델이 주로 텍스트 기반으로 동작하는 반면, Gemini는 스크린샷을 보고 UI 코드를 생성하거나, 다이어그램을 이해하고 관련 코드를 작성하는 등 시각적 입력을 코딩에 활용할 수 있다. Gemini 2.0 Flash(빠르고 저렴), Gemini Pro(고성능) 등 다양한 변형이 있으며, 용도에 따라 선택할 수 있다. Gemini Code Assist(IDE 기반 코딩 도구)와 Gemini CLI(터미널 에이전트)를 통해 코딩을 지원하며, Google Cloud 생태계(Cloud Functions, Firebase, BigQuery, Vertex AI 등)와 긴밀하게 통합되어 있어 Google Cloud 사용자에게 특히 유리하다. Google의 방대한 데이터와 인프라를 기반으로 하므로, 정보 검색 능력(Grounding with Google Search)이 뛰어나 최신 라이브러리나 API 정보를 반영한 코드 생성에 강점이 있다.
USEFUL LINKS
실제로 이어서 열어볼 즐겨찾기
Google이 종료한 서비스와 제품을 한눈에 모아, 플랫폼 의존성과 제품 지속성 리스크를 판단하게 해 주는 ‘Google 묘지’ 아카이브입니다.
VIBE 코딩 레퍼런스 웹사이트 해부도 · Website Anatomy MapAI와 웹사이트를 함께 만들 때 ‘그 부분’이 아니라 정확한 UI·웹 용어로 지시할 수 있게 돕는 영-한 시각 사전입니다.
VIBE 코딩 보안 테이텀 시큐리티 Threads쉬운 보안을 지향하는 한국어 보안 계정으로, AI·VIBE 코딩 흐름에서 놓치기 쉬운 보안 감각을 되짚는 데 유용합니다.