본문 바로가기

[Next.js 13] SSR로 동적 meta태그 생성하기 (app router)

[Next.js 13] SSR로 동적 meta태그 생성하기 (app router)
[ 개발 환경 ]
- nextjs는 app router 구조로 개발 중입니다.
+ 다국적 언어 지원 라이브러리인 react-i18next도 함께 사용하고 있습니다.

- 참고 포스팅 : [Next.js 13] SSR로 동적 meta태그 생성하기 (pages router)

 

결과

 

 

동적 언어 값에 따른 정적 메타태그 

 

데이터 페칭 후의 동적 메타태그

 

메타태그란?

 

메타태그는 웹 브라우저가 웹 페이지에 대해서 알아야 할 정보들을 포함한 태그이다. 

  • HTML 문서의 head 섹션에 포함된다.
  • 검색 엔진과 다른 웹 크롤러에게 웹 페이지에 대한 정보를 제공하므로 SEO 최적화와 관련이 있다. 
    • 즉, 검색엔진이 웹 페이지가 어떤 주제를 다루고 있는지를 파악하여 검색 결과의 순위를 매기는데 영향을 미친다.
  • 페이지 제목, 설명, 키워드, 작성자 등의 정보를 포함시킬 수 있다.

 

메타 태그의 속성 

http-equiv = "항목 명"

  • 웹 브라우저 서버에 명령을 내림
  • name 속성을 대신해 사용할 수 있음 
  • content 속성의 정보 및 값을 위한 HTTP header를 제공 
  • html5에서는 charset으로 간편하게 설정함
    • html4.01 : <meta http-equiv = "content-type" content ="text/html; charcet = UTF-8">
    • html5 : <meta charset = "UTF-8">
    • HTML5 버전에서도 여전히 http-equiv 속성을 사용하여 문자 인코딩 방식을 명시하는 것을 여전히 허용하지만, charset 속성을 사용하면 더욱 간단하게 명시할 수 있다.

name = "정보 이름"

  • 지정하지 않으면 http-equiv와 같은 기능

content = "정보 값"

  • 메타 정보의 내용 
  • name, http-equiv와 연관된 값 

 

메타 태그의 종류

 

<title> 제목 </title>
# 페이지의 제목을 나타내며 검색 결과 페이지에 표시

<meta name="Keywords" content="Web, html, css" />
# 검색 엔진에 검색되는 단어를 지정

<meta name="Description" content="Web, html, css" />
# 검색 결과에 표시되는 문자를 지정 (페이지 내용을 요약하여 표시)

<meta name="viewport" content="width=device-width, initial-scale=1.0">
# 모바일 반응 페이지
## width=device-width : 브라우저를 장치 너비에 맞추어 표시
## initial-scale=1.0 : 초기 화면 배율 설정(100%)

<meta name="Robots" content="noindex, nofollow" />
# 검색 로봇을 제어, content 기본 값은 All로 색인 생성, 게재에 대한 제한 없음
## noindex: 검색 결과에 이 페이지 표시 X (반대는 index)
## nofollow: 이 페이지의 링크를 따라가지 X  (반대는 follow)
## noarchive: 검색 결과에 저장된 페이지 링크 표시 X (반대는 archive)

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
# 웹 페이지에 대한 문서 인코딩 방식을 지정 ( utf-8 )

<meta charset="UTF-8" />
# 인코딩 선언 : 문자 깨짐 현상 등을 방지

<meta http-equiv="Content-Style-Type" content="Text/javascript" />
# 웹 페이지에 쓰인 개발 언어를 정의

<meta http-equiv="X-UA-Compatible" content="IE=edge" />
# 웹 브라우저에 호환성을 지정

<meta http-equiv="Subject" content="기술 블로그" />
# 웹 페이지의 주제 지정 

# http-equiv 속성에는 이외에도 Title(제목), Author(제작자명), Publisher(제작사), Other Agent(웹 책임자),
  Generator(제작도구), Reply-To/Email, Filename, Location(지역위치), Distribution(배포자), 
  Copyright(저작권), Build(제작 년,월,일), Expires(캐쉬만료일), Refresh(새로고침), 
  imagetoolbar(그림 위 마우스 오버 시 이미지 관련 툴바 생기지 않음),
  Last-Modified, Cache-Control/Pragma(캐쉬가 되지 않음) - 항상 서버에 접속하여 최신 내용 가져옴, 
  Page-Enter(페이지 들어갈 때 장면 전환 효과) - ex) Box out, Circle in, Wipe up, Random dissove 등

 

 

동적으로 메타태그 설정하기

 

nextjs 공식문서를 보면 아래와 같이 정적 메타태그 생성과 동적 메타태그 생성 방법이 나와있다.

import { Metadata } from 'next'
 
// either Static metadata
export const metadata: Metadata = {
  title: '...',
}
 
// or Dynamic metadata
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}

 

동적 언어 값에 따른 정적 메타태그 

 

 getServerSideProps를 이용하여 서버에서 데이터를 가져온 후 동적 메타 태그를 생성하는 Page Router 방식과는 달리, App Router은 기본이 SSR이기에 많은 방식에서의 차이점을 확인할 수 있었다.

  • App Router은 기본이 SSR이며 CSR이 필요할 때에는 파일 최상단에 'use client'; 를 선언한다.
  • Pages Router은 기본이 CSR이며 SSR이 필요할 때에는 getServerSideProps 등 미리 정해진 SSR 함수를 사용한다.

 

App Router은 우선 바로 SSR에서 구현이 되는 코드를 작성할 수 있다는 점에서 굉장히 간편하고 효율적인 개발 경험을 할 수 있었다.

  • 언어에 따른 메타 태그는 여타 SSR로 동적 메타 태그를 구현하는 방식처럼 Params에서 동적 라우팅 값인 언어(en/ko)를 추출하여 그와 걸맞는 메타 태그를 생성하였다. 

 

최상단 layout.tsx
  • generateStaticParams : [lng]와 관련된 동적 경로를 생성하기 위한 params의 배열을 리턴한다.
    • 여기서 [lng]는 언어변환과 관련된 동적 라우팅 slug이다.
    • 나의 프로젝트는 첫 번째 경우에 해당된다.

import { Metadata } from 'next';
import { homeMetaData } from '@/constants/SEO/home';
import { languages } from '../i18n/settings'; // ['ko', 'en'] : 한/영 변환 기능을 위한 설정 

export async function generateStaticParams() {
  return languages.map((lng) => ({ lng })); // [{ lng : 'ko'}, { lng : 'en' }]
}

type MetaProps = {
  params: { lng: string };
};

// 동적 라우팅 lng 값을 받아옴 
export const generateMetadata = async ({ params: { lng } }: MetaProps): Promise<Metadata> => {
  return lng === 'ko' ? homeMetaData.metadataKO : homeMetaData.metadataEN;
};

 

@/constants/SEO/home.ts
  • metadataBase : 모든 경로의 디폴트 값
  • rel =
    • canonical : 같은 내용을 표기하는 다수의 URL이 존재할 때 대표되는 표준 페이지 설정
      • 검색 엔진은 중복되는 URL에 접근했을 때 표준 페이지를 선택해서 크롤링한다.
      • 즉, 표준 페이지로 지정된 URL이 검색 결과에 노출될 확률이 높아진다.
    • alternate : 다국어로 정의된 페이지/ 보조 페이지 정의 
      • 각 언어별 페이지를 설정해두면 검색엔진은 사용자의 언어와 일치하는 페이지를 검색 결과에 노출시킨다.
      • 데스크탑 / 모바일 페이지를 설정할 때도 사용된다. 
        • <link rel="alternate" href="https://m.blog.com/" media="only screen and (max-width: 640px" />

export const homeMetaData = {
// 한국어 메타 데이터
  metadataKO: {
    metadataBase: new URL('https://portfolio-doyu.vercel.app'),
    alternates: {
      canonical: '/',
      languages: {
        'ko-KR': '/ko',
        'en-US': '/en',
      },
    },
    title: {
      template: '🐱 %s | 도유의 블로그', // 동적 title +  prefix / suuffix 
      default: '🐱 도유의 블로그', // 동적 title 못 찾았을 때 들어가는 fallback 값이 됨 
    },
    description: '개발과 관련된 다양한 정보들을 기록하는 장소입니다.',
    icons: {
      icon: '/images/meta/favicon.ico', // 파일경로는 public부터, 실제경로는 metadataBase 
    },
    openGraph: {
      title: {
        template: '🐱 %s | 도유의 블로그',
        default: '🐱 도유의 블로그',
      },
      description: '개발과 관련된 다양한 정보들을 기록하는 장소입니다.',
      images: [
        {
          url: '/images/meta/opengraph-image.png',
          alt: '도유의 블로그 OG 이미지',
        },
      ],
    },
  },

// 영어 메타 데이터
  metadataEN: {
    metadataBase: new URL('https://portfolio-doyu.vercel.app'),
    alternates: {
      canonical: '/',
      languages: {
        'ko-KR': '/ko',
        'en-US': '/en',
      },
    },
    title: {
      template: "🐱 %s | Doyu's Blog",
      default: "🐱 Doyu's Blog",
    },
    description: 'A blog recording various insights related to development',
    icons: {
      icon: '/images/meta/favicon.ico',
    },
    openGraph: {
      title: {
        template: "🐱 %s | Doyu's Blog",
        default: "🐱 Doyu's Blog",
      },
      description: 'A blog recording various insights related to development',
      images: [
        {
          url: '/images/meta/opengraph-image.png',
          alt: "Doyu's Blog OG Image",
        },
      ],
    },
  },
};

 

데이터 페칭 후의 동적 메타태그

 

위의 내용과 별반 다를 게 없다.

  • 다른 점은 동적 라우팅은 현재 pageId를 추출하여 관련 데이터를 가져온다는 것이다.
type Props = {
  params: { pageId: string }; // params에서 현재 pageId 추출 
};

export const generateMetadata = async ({
  params: { pageId },
}: Props): Promise<Metadata> => {
  const recordMap = await notion.getPage(pageId);  // 해당 pageId의 전체 데이터 가져오기
  const title = getPageTitle(recordMap);   // 제목 데이터 추출 

  return {
    title,
    openGraph: {
      title,
    },
  };
};

 

레퍼런스

 

nextjs 공식문서

https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadatabase

https://nextjs.org/docs/app/api-reference/functions/generate-static-params  

 

메타 태그의 속성 

https://inpa.tistory.com/entry/HTML-%F0%9F%93%9A-meta-%ED%83%9C%EA%B7%B8-%EC%A0%95%EB%A6%AC

https://armadillo-dev.github.io/dev-diary/seo/dev-diary-rel-canonical-and-alternate-seo/

728x90
⬆︎