Programming/Next.js

SEO 향상을 위한 Metadata API 적용하기

soyeori 2023. 8. 18. 23:22

Next.js는 최적화(Optimizations)를 위해 기본적으로 제공되는 기능들이 있다. 

 

공식문서를 참고하여 간략하게 소개하자면 먼저, 빌트인 컴포넌트를 사용하여 UI를 최적화하기 위해 복잡하게 구성해야 하는 요소들을 쉽게 만들어 줄 수 있다. Images, Link, Scripts와 같은 컴포넌트들이 이러한 역할을 한다.

 

다음으로 Metadata를 활용하여 웹사이트의 콘텐츠를 검색엔진에 더 잘 노출되도록 하고, 어떻게 보일 지를 커스텀하여 다양한 플랫폼에서 일관적인 사용자 경험을 줄 수 있는 방법이 있다. Next.js의 Meatadata API는 HTML의 <head> 요소를 수정할 수 있게 해 줌으로써 웹사이트 SEO를 향상시키기 위한 방법을 제공한다.

 

드림코딩 강의를 통해 Next.js(v13)과 React(v18)을 공부하며 만든 블로그에서 challenge를 하며 메타데이터를 적용하는 과정을 글로 작성하게 되었다.

 

메타데이터를 추가하는 방법에는 크게 두 가지가 있다. 

 

1️⃣ Config-based Metadata

: layout.js 또는 page.js에 Metadata를 생성해 주는 방법으로 데이터가 '정적'이냐 '동적'이냐에 따라 두 가지로 나뉜다. 또한, 포인트는 Server Components에서만 지원된다는 점이다. 

 

    ❒ Static Metadata : Metadata 객체를 생성하여 메타데이터를 정의하는 방법

    ❒ Dynamic Metadata : generateMetadata 함수를 사용

 

2️⃣ File-based Metadata : 특정 파일을 추가하여 메타데이터를 생성하는 방법

Static Metadata

- Next.js를 생성하면 src/app/layout.tsx 경로에 기본적으로 메타데이터(title, description)가 생성되어있다. 이 부분을 string 또는 template object를 사용하여 넣고 싶은 데이터를 정의할 수 있고, 메타데이터 Fields 옵션을 추가할 수 있다.

import type { Metadata } from "next";
...
export const metadata: Metadata = {
  //  Template Object
  title: {
    template: "%s | 소현의 블로그",
    default: "소현의 블로그", // 대체 제목 (required)
  },
  description: "welcome to my secret blog! 👑",
  authors: [{ name: "ParkSoHyun" }],
  generator: "Next.js",
  applicationName: "Blog",
  referrer: "origin-when-cross-origin",
  keywords: ["Next.js", "React", "JavaScript"],
};

이렇게 메타데이터를 정의해 주면 Next.js에서 HTML head태그 안에 <meta> 태그로 데이터를 생성해 준다.

 

 

앞서 Template Object로 정의한 title.template은 하위 경로의 페이지 제목이 있다면 그 제목을 접두사 또는 접미사를 더하여 템플릿 형태로  나타내는 역할을 한다. 반면에 제목이 정의되어 있지 않는 페이지는 title.default 값을 title로 보여준다.

블로그 내 About, Posts, Contact 페이지 각각에 서로 다른 옵션을 적용해 보았다.

 

✅ About 페이지 - 제목이 레이아웃에서 정의해 둔 템플릿 형태로 보여진다.

// app/about/page.tsx
import { Metadata } from "next";

export const metadata: Metadata = {
  title: "About me",
};

// <head> output
<title>About me | 소현의 블로그</title>

 Posts 페이지 - title.absolute를 사용하면 상위 경로에서 정의된 탬플릿을 무시한다.

// app/posts/page.tsx
import { Metadata } from "next";

export const metadata: Metadata = {
  title: {
    absolute: "블로그 포스트",
  },
};

// <head> output
<title>블로그 포스트</title>

 Contact 페이지 - 이번에는 아무런 메타데이터를 정의해 주지 않았다. 그러므로 title.default로 정의한 제목이 보여진다.

// app/contact/page.tsx
import { Metadata } from "next";

export const metadata: Metadata = {};

// <head> output
<title>소현의 블로그</title>

Dynamic metadata

그렇다면 동적으로 생성되는 데이터의 메타데이터는 어떻게 정의할 수 있을까? 바로 Next.js에서 제공하는 generateMetadata 함수를 사용하여 동적인 값을 불러와 메타데이터로 넣어줄 수 있다.

공식문서에서는 Fetch API로 불러온 데이터를 return값으로 넣어주는데 여기서는 getPostDetail 함수가 각 post에 해당하는 글을 찾아서 보여주는 로직이다. 이후 post 객체에 들어있는 title과 description 필드를 메타데이터로 반환한다.

// app/posts/[post]/page.tsx
import { Metadata } from "next";
...
interface Props {
  params: {
    post: string;
  };
}

export async function generateMetadata({params: { post }}: Props): Promise<Metadata> {
  const { title, description } = await getPostDetail(post);

  return {
    title,
    description,
  };
}
...

페이지를 동작시켜 보면 게시물을 클릭했을 때 제목과 내용이 각 글에 맞는 내용으로 동적으로 생성되는 것을 알 수 있다. 제목은 상위 컴포넌트 즉, layout.tsx에서 정의한 탬플릿 형태로 보여진다. 참고로 meta charset 태그와 viewport 태그는 정의하지 않아도 메타데이터에 추가되는 default 필드이다.

 

File-based Metadata

다음으로 메타데이터를 정의할 수 있는 방법으로 특정한 파일(special files)을 추가하는 방법이 있다. file-based metadata는 config-based metadata보다 높은 우선순위를 갖으며 config-based metadata를 재정의한다. 

메타데이터에 이용할 수 있는 여러 가지 파일들이 있지만 파비콘과 오픈그래프를 사용해 보기로 했다.

 favicon★

파비콘에 해당하는 이미지를 app/favicon.ico 경로에 생성해서 넣어주면 Next.js에서 자동으로 해드 태그에 파비콘을 넣어준다.

<!-- <head> output -->
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="any">

 openGraph

다음으로 오픈그래프를 추가해 주기 위해 OG이미지를 먼저 생성해 주었다. OG는 2가지 방법으로 만들어 주었는데, 이미지 같은 경우에는 조금 더 사용하기 쉬운 File-based Metadata 방법을 적용하여 app/ 경로에 이미지와 이미지를 대체할 수 있는 정보(opengraph-image.alt.txt)도 같은 경로에 넣어주었다.

 

이렇게만 하면 이미지에 대한 부분만 OG로 생성되기 때문에 2번째 방법으로 layout.tsx에 추가로 넣고 싶은 OG데이터를 넣어주었다.

import type { Metadata } from "next";
...
export const metadata: Metadata = {
...
  openGraph: {
    title: "소현의 블로그",
    description: "개발 관련 블로그",
    url: "https://blog-project-psi-sand.vercel.app",
    type: "website",
  },
};
<!-- <head> output -->
<meta property="og:title" content="소현의 블로그">
<meta property="og:description" content="개발 관련 블로그">
<meta property="og:url" content="https://blog-project-psi-sand.vercel.app">
<meta property="og:image:type" content="image/png">
<meta content="683" property="og:image:width">
<meta content="449" property="og:image:height">
<meta content="http://localhost:3000/opengraph-image.png?071101f06404077d" property="og:image">
<meta content="website" property="og:type">

 

Metadata와 OG까지 적용함으로써 블로그 웹사이트에 향상된 SEO와 웹 공유성을 더해주었다. Next.js는 이와 관련된 모든 기능을 간편하게 제공해 주고, 이미지 크기 등을 자동적으로 최적화해 주기 때문에 Next.js를 사용하는 또 하나의 장점으로 생각되는 부분이다.

Behavior

마지막으로 메타데이터를 생성하는 데에 두 가지 방법이 있기 때문에 어떻게 동작하는지 순서에 유의해서 사용해야 한다. 먼저 언급한 file-based metadata는 config-based metadata보다 높은 우선순위를 갖는다는 점과 함께 Metadata는 다음과 같은 순서대로 적용된다. 

 

✔️ Ordering

1️⃣ app/layout.tsx (Root Layout)
2️⃣ app/blog/layout.tsx (Nested Blog Layout)
3️⃣ app/blog/[slug]/page.tsx (Blog Page)

 

✔️ Merging

위의 순서대로 적용하면서 중복된 키가 있다면 순서대로 메타데이터를 대체한다. 즉, 하위 경로에서 제목이 변경된다면 그 제목으로 대체되고, 만약 OG데이터가 중복해서 있다면 해당 페이지는 나중에 정의된 메타데이터로 대체된다.

여기서 포인트는 Overwriting, 즉, 덮어씌워진다는 의미이다. 그래서 layout에서 적용한 OG를 blog페이지의 OG가 덮어씌운다면 기존 정의된 description 필드는 더 이상 나타나지 않는다. 

// 공식문서 example
// app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}

// app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}
 
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

 

만약 중복된 필드를 덮어씌우되, 설정하지 않은 다른 필드를 그대로 공유하고 싶다면 별도 변수를 만들어서  상속하는 방법이 있다. 이 부분은 공식문서로 대체한다. Inheriting fields

 

배포가 완료된 이후 카톡으로 웹사이트 링크를 공유하면 OG로 설정한 정보가 나타나는 것을 확인할 수 있다.

 

 


[Next.js] 공식문서 - Metadata