ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

๐Ÿ–ผ๏ธ Next.js App Router ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ & OG ์ด๋ฏธ์ง€ ์„ค์ • ์™„์ „ ๊ฐ€์ด๋“œ

Next.js App Router์—์„œ๋Š” ํŽ˜์ด์ง€๋ณ„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(SEO์šฉ ์ œ๋ชฉ, ์„ค๋ช…, OG ์ด๋ฏธ์ง€ ๋“ฑ)๋ฅผ ์‰ฝ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ ,
Open Graph ์ด๋ฏธ์ง€(์†Œ์…œ ๊ณต์œ  ์ธ๋„ค์ผ)๋„ ์ •์  ๋˜๋Š” ๋™์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โœ… 1. ์ •์  Open Graph ์ด๋ฏธ์ง€ ์„ค์ • ๋ฐฉ๋ฒ•

๐Ÿ“‚ ๋””๋ ‰ํ† ๋ฆฌ์— ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ถ”๊ฐ€ํ•˜๊ธฐ

ํด๋” ์•ˆ์— opengraph-image.png ๋˜๋Š” twitter-image.png๋ฅผ ๋„ฃ์œผ๋ฉด, ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ์ ‘๊ทผ ์‹œ ์ž๋™์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ ํด๋” ๊ตฌ์กฐ:

app/
โ”œโ”€โ”€ opengraph-image.png         ← ๋ฃจํŠธ ๋ฉ”ํƒ€ ์ด๋ฏธ์ง€
โ”œโ”€โ”€ twitter-image.png
โ”œโ”€โ”€ about/
โ”‚   โ”œโ”€โ”€ page.tsx
โ”‚   โ””โ”€โ”€ opengraph-image.png     ← about ํŽ˜์ด์ง€ ์ „์šฉ OG ์ด๋ฏธ์ง€
  • /about ํŽ˜์ด์ง€ ๊ณต์œ  ์‹œ: /about/opengraph-image.png๊ฐ€ OG ์ด๋ฏธ์ง€๋กœ ์„ค์ •๋จ
  • ์ด๋ฏธ์ง€ ํ˜•์‹: .jpg, .png, .gif ๋“ฑ

๐Ÿง  metadata ๊ฐ์ฒด๋กœ ๋ฉ”ํƒ€์ •๋ณด ์ง์ ‘ ์„ค์ •ํ•˜๊ธฐ

layout.tsx ๋˜๋Š” page.tsx ์•ˆ์— metadata ๊ฐ์ฒด๋ฅผ export ํ•˜๋ฉด, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// app/about/page.tsx
export const metadata = {
  title: 'ํšŒ์‚ฌ ์†Œ๊ฐœ - About Us',
  description: '์šฐ๋ฆฌ ํšŒ์‚ฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์„ธ์š”',
  openGraph: {
    title: 'About | MySite',
    description: '์šฐ๋ฆฌ๋Š” ์ด๋ ‡๊ฒŒ ์ผํ•ฉ๋‹ˆ๋‹ค.',
    images: ['/about/opengraph-image.png'],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'About Us',
    images: ['/about/twitter-image.png'],
  },
};
  • openGraph.images์™€ twitter.images๋Š” ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ ์„ค์ • ๊ฐ€๋Šฅ
  • SEO ๋ฐ ๊ณต์œ  ์ตœ์ ํ™”์— ๋งค์šฐ ์ค‘์š”

โš™๏ธ 2. ๋™์  OG ์ด๋ฏธ์ง€ ์ƒ์„ฑ (ImageResponse)

๋ฐ˜์‘ํ˜•

๋ผ์šฐํŠธ๋งˆ๋‹ค ๋‹ค๋ฅธ OG ์ด๋ฏธ์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, Next.js์˜ ImageResponse API๋ฅผ ์‚ฌ์šฉํ•ด ์„œ๋ฒ„์—์„œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

// app/posts/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';

export const size = {
  width: 1200,
  height: 630,
};

export default async function OGImage({ params }) {
  const post = await fetch(`https://api.example.com/posts/${params.slug}`).then(res => res.json());

  return new ImageResponse(
    (
{post.title}
    ),
    size
  );
}
  • ๊ฐ ๊ฒŒ์‹œ๊ธ€๋งˆ๋‹ค ๋™์ ์œผ๋กœ ์ œ๋ชฉ์ด๋‚˜ ์ธ๋„ค์ผ ํฌํ•จ ๊ฐ€๋Šฅ
  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค ์‹ค์‹œ๊ฐ„ ์ƒ์„ฑ๋จ

๐Ÿ“ ๋ฉ”ํƒ€ ์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ ๊ฐ€์ด๋“œ

ํ”Œ๋žซํผ ๊ถŒ์žฅ ํฌ๊ธฐ ๋น„๊ณ 

Open Graph (Facebook ๋“ฑ) 1200×630 px ๋น„์œจ 1.91:1
Twitter Card 1200×600 px summary_large_image ๊ธฐ์ค€

๊ฐ€๋Šฅํ•œ ํ•œ ์ด๋ฏธ์ง€ ์šฉ๋Ÿ‰์€ 1MB ๋ฏธ๋งŒ์œผ๋กœ ์œ ์ง€ํ•˜์„ธ์š”.


๐Ÿงช ํ™œ์šฉ ํŒ

  • Open Graph, Twitter ๋‘˜ ๋‹ค ์„ค์ • ๊ฐ€๋Šฅ (์„œ๋กœ ๋‹ค๋ฅธ ์ด๋ฏธ์ง€๋„ ๊ฐ€๋Šฅ)
  • SEO๋ฅผ ์œ„ํ•ด title, description, canonical๋„ ํ•จ๊ป˜ ๊ด€๋ฆฌ
  • ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ๋Š” public ๊ฒฝ๋กœ๋‚˜ CDN ๊ฒฝ๋กœ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ๋™์  ์ƒ์„ฑ ์‹œ Edge Runtime ์‚ฌ์šฉ ๊ฐ€๋Šฅ (๋น ๋ฅด๊ณ  ํšจ์œจ์ )

โœ… ์š”์•ฝ

ํ•ญ๋ชฉ ์„ค์ • ๋ฐฉ์‹ ์œ„์น˜/์‚ฌ์šฉ๋ฒ•

์ •์  OG ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ง์ ‘ ์ถ”๊ฐ€ opengraph-image.png, twitter-image.png
ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์„ค์ • metadata export layout.tsx, page.tsx
๋™์  ์ด๋ฏธ์ง€ ์ƒ์„ฑ ImageResponse() opengraph-image.tsx ํŒŒ์ผ๋กœ ์ƒ์„ฑ

 

Next.js, App Router, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์„ค์ •, OG ์ด๋ฏธ์ง€, Open Graph, Twitter ์นด๋“œ, SEO ์ตœ์ ํ™”, ๋™์  ์ธ๋„ค์ผ, ImageResponse, ํŽ˜์ด์ง€ ๊ณต์œ  ์ตœ์ ํ™”

โ€ป ์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2025/04   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•