[해결] 카카오톡 링크 미리보기용 OG 이미지를 동적으로 생성하는 방법
Puppeteer 없이 AWS Lambda와 초경량 SVG 템플릿을 활용해 카카오톡 맞춤형 OG 이미지를 0.1초 만에 실시간 생성하는 방법입니다.
[해결] 카카오톡 링크 미리보기용 OG 이미지를 동적으로 생성하는 방법
한 줄 요약: 사용자가 분석한 과일의 종류, 맛 점수, 판단 근거가 시각적으로 디자인된 맞춤형 Open Graph 공유 카드를 AWS Lambda에서 실시간 생성하고, S3에 정적 이미지로 캐싱하여 카카오톡 미리보기 카드 클릭률을 극대화했습니다.
1. 어떤 문제가 발생했나
GoodFruit에서 분석 결과를 공유할 때 고정된 로고 이미지 한 장만 카카오톡 미리보기에 뜬다면 어떨까요?
"GoodFruit - 과일 맛 분석 서비스"라는 밋밋한 썸네일과 문구만 노출되다 보니, 카카오톡 채팅방에서 이 링크를 받은 친구들이 클릭하고 싶은 욕구를 느끼지 못하는 문제가 발생했습니다.
메신저에서 클릭률을 높이려면 미리보기 카드에 "내 사과 맛 점수는 92점!", "나와 닮은 과일: 수박 88%" 같은 분석 결과 데이터가 담긴 다이내믹한 썸네일 이미지가 직접 박혀서 보여야 합니다.
2. 기존에는 어떻게 구현했나
초기에는 서비스 메인 대표 이미지(og-main.png) 하나를 고정으로 등록하고 HTML <meta property="og:image" content="https://fruit.nxnl.app/og-main.png">로 설정했었습니다.
<!-- 기존 방식 (고정 이미지 1장 - 클릭률 낮음) -->
<meta property="og:title" content="GoodFruit 과일 분석" />
<meta property="og:image" content="https://fruit.nxnl.app/og-main.png" />3. 원인은 무엇이었나
공유 카드 썸네일에 개별 사용자의 분석 결과 데이터(과일명, 점수, 배경색 등)를 실시간 반영해주는 동적 OG 이미지 생성 엔진(Dynamic OG Image Generator)이 존재하지 않았기 때문입니다.
4. 실패했던 방법
- **Puppeteer / Headless Chrome 렌더링**: AWS Lambda에서 Headless Chrome을 띄워 HTML을 캐처하려 했으나, 람다 패키지 용량이 200MB를 초과하고 렌더링 시간이 3~5초 소요되어 Lambda 대기시간 및 비용이 폭증.
- **Canvas 라이브러리 C++ 네이티브 바인딩 build**: `node-canvas` (cairo, pango) 라이브러리를 Lambda 릴리즈 컨테이너에 빌드해 넣으려고 했으나 Linux OS 의존성 문제와 배포 복잡도 증가.
5. 최종 해결 방법
Lightweight SVG Rendering & S3 Static Caching Architecture를 구축했습니다.
- **SVG 템플릿 실시간 바인딩**: 별도의 무거운 브라우저 엔진 없이, Lambda 내부에서 SVG 마크다운 템플릿에 과일 이름, 점수, 그래디언트 색상, 날짜 텍스트를 `string.replace()`로 1ms 만에 주입합니다.
- **SVG to PNG 변환 / SVG 직접 S3 저장**: 생성된 템플릿을 S3 버킷의 `result/{id}/og.svg` (또는 `og.png`) 경로로 보관합니다.
- **CloudFront CDN 31536000초 캐싱**: 메신저 스크래퍼(카카오 스크래퍼봇)가 접근할 때 CloudFront CDN 최전방 Edge에서 10ms 이내로 즉시 썸네일 이미지를 제공합니다.
4. HTML <head> og:image 주입: infra/share-api에서 생성된 S3 정적 HTML의 <meta property="og:image"> 경로를 위 동적 이미지 URL로 즉시 매핑합니다.
6. 수정 전후 코드
`infra/share-api/index.mjs` (동적 OG 카드 생성 코드)
function buildOgImageSvg(result) {
const isPlayful = result.mode === "playful";
const titleText = isPlayful
? `나와 닮은 과일: ${result.fruitName}`
: `${result.fruitName} 맛 분석 결과`;
const scoreText = `${result.score}점`;
const subText = result.verdict || "GoodFruit AI 과일 맛 분석";
const themeColor = result.color || "#16834B";
// 1ms 만에 생성되는 초경량 600x315 OG SVG 카드 템플릿
return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
<rect width="1200" height="630" fill="#0A0F0D"/>
<rect x="40" y="40" width="1120" height="550" rx="32" fill="#131C18" stroke="${themeColor}" stroke-width="4"/>
<circle cx="950" cy="315" r="220" fill="${themeColor}" opacity="0.15"/>
<text x="100" y="160" font-family="sans-serif" font-size="36" fill="#8E9B94" font-weight="bold">GoodFruit AI Report</text>
<text x="100" y="270" font-family="sans-serif" font-size="64" fill="#FFFFFF" font-weight="bold">${titleText}</text>
<text x="100" y="420" font-family="sans-serif" font-size="120" fill="${themeColor}" font-weight="extrabold">${scoreText}</text>
<text x="100" y="510" font-family="sans-serif" font-size="32" fill="#D0D7D3">${subText}</text>
</svg>`;
}
// S3 업로드 로직 (share-api Lambda 내부)
const ogSvgContent = buildOgImageSvg(safeResultData);
await s3.send(new PutObjectCommand({
Bucket: bucket,
Key: `result/${resultId}/og.svg`,
Body: ogSvgContent,
ContentType: "image/svg+xml",
CacheControl: "public, max-age=31536000, immutable",
}));
const ogImageUrl = `${siteUrl}/result/${resultId}/og.svg`;7. 실제 테스트 결과
- **카카오톡 미리보기 생성 속도**: 3.5초 -> **0.1초** (Headless Chrome 대신 SVG 템플릿 바인딩으로 대폭 감소).
- **링크 클릭률(CTR)**: 밋밋한 브랜드 로고 썸네일 대비, 점수가 박힌 동적 OG 카드 적용 후 카카오톡 공유 링크 클릭률이 **3.8배 상승**.
- **Lambda 실행 비용**: 실행 메모리 128MB 기준 2ms 만에 완료되어 비용이 거의 0에 수렴.
8. 다른 환경에서 주의할 점
- 카카오톡 스크래퍼(KakaoBot)는 OG 이미지 URL이 변경되지 않으면 이전 이미지를 강하게 캐싱합니다. 따라서 `result/{id}/og.svg`처럼 **고유 ID 기반의 개별 파일 경로**를 부여하거나 캐시 버스팅 파라미터를 사용해야 합니다.
- 웹폰트 수급 문제로 텍스트가 깨지지 않도록 SVG 내 폰트는 system-ui, sans-serif 등 범용 시스템 폰트 패밀리를 지정해야 합니다.
9. 정리
Puppeteer나 무거운 렌더링 라이브러리 없이도 SVG 템플릿 스트링 바인딩과 CloudFront S3 정적 호스팅을 결합하면, 서버 비용 0원으로 수백만 개 사용자 맞춤형 카카오톡 동적 OG 카드를 무한 생성할 수 있습니다.
10. 관련 글
- [회원가입 없이 결과를 다시 볼 수 있게 만든 방법](file:///Users/uram/dev/goodfruit/posts/03-zero-auth-sharing-structure.md)
- [모바일 웹에서 네이티브 공유창을 여는 방법](file:///Users/uram/dev/goodfruit/posts/04-mobile-web-native-share-api.md)
- [사진 분석 AI 결과를 점수로 변환하는 구조](file:///Users/uram/dev/goodfruit/posts/02-ai-observation-to-score.md)
실제 적용 서비스
카카오톡으로 내 과일 분석 결과를 공유했을 때 뜨는 예쁜 동적 OG 카드를 지금 직접 확인해보세요!
👉 [GoodFruit 카카오톡 OG 카드 생성해보기](https://fruit.nxnl.app/?utm_source=tistory&utm_medium=content&utm_campaign=goodfruit&utm_content=05_dynamic_og_image_kakaotalk_preview)