SEO(3) - Next.js 오픈그래프
📌시작하며
Next.js에서는 이전 SEO포스팅에서 정리했던 meta 태그들을 간단하게 만들 수 있는 방법을 제공한다. 공식문서 예제를 보고 응용해보면서 방법을 익혔는데, 의외로(?) 오픈그래프 쪽은 공식문서 내용이 부족하다고 느꼈다… 😅 그래서이번 포스팅에서는 나름 시행착오를 거치며 학습한 Next.js 오픈그래프 설정에 대해 정리해보고자 한다!
이 글은 아래와 같이 이어집니다.
✅정적 이미지 사용
만약 오픈그래프로 사용하고 싶은 이미지가 특정되어 있는 경우는 어떻게 할까?
먼저 파일 구조를 살펴보자!
1
2
3
4
app/
└── second/
├── opengraph-image.jpg
└── page.tsx
현재 second 페이지 폴더에 opengraph-image.jpg 파일을 넣어두었다. 아래 표에서 살펴볼 수 있는 포맷들로 이미지들을 지정해주면 된다.
File Convention | Supported File Types |
---|---|
opengraph-image | .jpg, .jpeg, .png, .gif |
twitter-image | .jpg, .jpeg, .png, .gif |
공식문서에서는 이렇게만 넣어두면 자동으로 적용되는 것 처럼 서술 되어있는데.. 안된다😅…
이 부분은 따로 page.tsx에서 처리해야 한다.
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
//page.tsx
import ogImage from "./opengraph-image.jpg"
export const metadata = {
metadataBase: new URL("배포페이지 주소"),
title: "2번째 페이지",
description: "2번째 페이지 설명",
openGraph: {
images: [
{
title: "2번째 페이지 오픈그래프",
description: "2번째 페이지 오픈그래프 설명",
url: ogImage.src,
width: ogImage.width,
height: ogImage.height,
},
],
},
}
export default function Second() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1>SecondPage</h1>
</main>
)
}
먼저 opengraph-image.jpg를 ogImage 란 이름으로 불러와서, url, width, height에 각각 지정해주었다.
이렇게 metadata를 지정해주면, 오픈그래프가 적절히 적용되는 것을 확인할 수 있다.
✅jsx 문법으로 이미지 만들어 넣기
이미 만들어져 있는 이미지를 사용하는 것이 아니라 jsx 문법을 이용해 이미지를 제작해 적용할 수 도 있다. 파일구조는 다음과 같이 지정해준다.
1
2
3
4
app/
└── third/
├── opengraph-image.tsx
└── page.tsx
이제 이 opengraph-image.tsx 파일에서 이미지를 만들어 줄 건데, 이 때 사용하는 것이 ImageResponse다.
➡️ImageResponse
ImageResponse 생성자를 사용하면 JSX 및 CSS를 사용해 동적 이미지를 생성할 수 있다. 이 ImageResponse는 HTML과 CSS를 SVG로 변환하는 Satori라는 라이브러리를 기반으로 한다. 참고로 이렇게 제작된 오픈그래프는 정말 ‘이미지’로 변환 되므로, 검색도구로 레이아웃이나 컬러를 수정할 수 없다.
Satori는 React Native와 동일한 Flexbox 레이아웃 엔진을 사용 하지만 완벽하게 모든 CSS를 사용할 수 없음에 유의한다.
ImageResponse는 14버전 부터 next/server가 아닌 next/og에서 import 해와야 한다!
간단하게 아래와 같이 이미지를 만들어보았다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
import { ImageResponse } from "next/og"
export const runtime = "edge"
// Image metadata
export const alt = "second"
export const size = {
width: 1200,
height: 630,
}
export const contentType = "image/png"
// Image generation
export default async function Image() {
const font = await fetch(
new URL(
"../../assets/fonts/NotoSansKR-SemiBold-Subset.woff",
import.meta.url
)
).then((res) => res.arrayBuffer())
return new ImageResponse(
(
<div
style=
>
third Page!
</div>
),
// ImageResponse options
{
...size,
fonts: [
{
name: "NotoSansKR-Light",
data: font,
weight: 600,
},
],
}
)
}
사용자가 원하는 폰트를 이용해 이미지를 제작하기 위해서는 assets 폴더에 폰트파일을 넣어서 아래와 같이 불러온 다음 적용해주면 된다.
1
2
3
const font = await fetch(
new URL("../../assets/fonts/NotoSansKR-SemiBold-Subset.woff", import.meta.url)
).then((res) => res.arrayBuffer())
이 때, ttf, woff는 사용가능하지만 woff2를 사용하고자 하니 배포시 오류가 발생했다. 또한 폰트 파일이 큰 경우 무료버전 vercel에서는 배포 오류가 발생하므로 주의하자.
실제 페이지에 오픈그래프나 트위터카드에 만든 이미지를 적용하려고 할 때는 아래와 같이 적용해주면 된다. (images 경로에 주의한다!)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export const metadata = {
openGraph: {
images: [
{
url: "http://test.com/third/opengraph-image",
},
],
},
twitter: {
images: [
{
url: "http://test.com/third/opengraph-image",
},
],
},
}
export default function Third() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1>ThirdPage</h1>
</main>
)
}
✅이모지 사용하기
바로 위 예제에 이어서, 이미지를 만들 때 이모지를 사용하고 싶다면, 다음과 같이 opengraph-image.tsx에 작성해주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { ImageResponse } from "next/og"
export default async function GET() {
return new ImageResponse(
(
<div
style=
>
👋, 🌎 안녕!
</div>
),
{
width: 1200,
height: 630,
// Supported options: 'twemoji', 'blobmoji', 'noto', 'openmoji', 'fluent' and 'fluentFlat'
// Default to 'fluent'
emoji: "noto",
}
)
}
이모지의 생김새의 경우 ‘twemoji’, ‘blobmoji’, ‘noto’, ‘openmoji’, ‘fluent’, ‘fluentFlat’ 에서 선택할 수 있고, 디폴트 값은 fluent로 지정되어 있다.
적용은 위의 예제와 동일하게 이미지 경로에 주의해서 작성해주면 된다.
✅동적으로 오픈그래프 만들기
가장 궁금했던 동적으로 오픈그래프 만들기 😎 이 방법을 이용해 블로그나 다른 페이지에 적절한 오픈그래프를 생성할 수 있다.
파일 구조는 다음과 같다.
1
2
3
4
5
6
7
8
9
src/
├── app/
│ ├── api/
│ │ └── og/
│ │ └── route.ts
│ └── fifth/
│ └── [id]/
│ └── page.tsx
└── data.json
먼저 동적으로 오픈그래프를 생성하기 위해 다음과 같은 json 파일을 만들어 주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//data.json
[
{
"id": 0,
"title": "짱구",
"og": "https://image.짱구이미지"
},
{
"id": 1,
"title": "신비아파트",
"og": "https://image.신비아파트이미지"
}
]
그 다음, Next.js의 api 기능을 활용해 다음과 같이 작성해주었다. 이때, 응답은 이미지 파일로 줄 수 있도록 ImageResponse를 활용한다.
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
31
32
33
34
35
36
37
38
import { ImageResponse } from "next/og"
import posts from "../../../data.json"
export const runtime = "edge"
const size = {
width: 1200,
height: 630,
}
export async function GET(request: Request, response: Response) {
try {
const { searchParams } = new URL(request.url)
const hasId = searchParams.has("id")
if (hasId) {
const id = Number(searchParams.get("id"))
const { og, title } = posts[id]
return new ImageResponse(
(
// ImageResponse JSX 작성
<div
style=
>
{title}
</div>
),
{
...size,
}
)
} else {
return new ImageResponse(<div>이미지없음</div>)
}
} catch (e: any) {
return new Response("실패", { status: 500 })
}
}
실제 활용할 때는, page.tsx에서 image url을 아래와 같이 작성한 api 엔드포인트를 활용해 id는 쿼리스트링으로 넘겨주면 된다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//page.tsx
import posts from "../../../data.json"
import { Metadata, ResolvingMetadata } from "next"
type Props = {
params: { id: string },
searchParams: { [key: string]: string | string[] | undefined },
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const id = Number(params.id)
const { title, og } = posts[id]
return {
title: title + " 설명" || "default title",
openGraph: {
title: title + "오픈그래프 제목" || "오픈그래프 제목",
description: title + "오픈그래프 설명" || "오픈그래프 설명",
images: [
{
url: `https://test.com/api/og?id=${id}`,
width: 800,
height: 600,
alt: title + "오픈그래프 이미지",
},
],
},
twitter: {
title: title + "트위터 제목" || "트위터 제목",
description: title + "트위터 설명" || "트위터 설명",
images: [
{
url: `https://test.com/api/og?id=${id}`,
width: 800,
height: 600,
alt: title + "트위터 이미지",
},
],
},
}
}
export default function Page({ params, searchParams }: Props) {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1>FifthPage {params.id}</h1>
</main>
)
}
이 방법을 활용하면 각 페이지에 적절한 오픈그래프를 생성할 수 있다!
📩마무리
Next.js를 공부할 때 보았던 공식문서는 대부분 설명이 자세했는데 오픈그래프는 설명이 좀 아쉬웠던 부분이 있다. 😅 그래서 이번엔 구글링, 영상, 직접 부딪혀보기(?) 등으로 오픈그래프 설정에 대해 학습할 수 있었다.
미리 이렇게 관련 예제 모든 방법을 실행해봐서 다행인게, 적용을 확인하기 위해서 계속 배포하고 확인하는 과정이 꽤나 번거로웠고 시행착오가 있었기에 다른 프로젝트에 바로 적용하려고 했으면 더 많은 시간이 소요되었을 것 같다… 🤔
어쨌든, 오픈그래프 적용 방법을 알았으니 포트폴리오에도 오픈그래프를 적절히 적용해서, SEO 개선과 SNS 공유시에, 훨씬 나은 이미지를 제공할 수 있을 듯 하다. 😎