[ 개발 환경 ]
- 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" />
- canonical : 같은 내용을 표기하는 다수의 URL이 존재할 때 대표되는 표준 페이지 설정
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/
'📌 PROJECT > 2309 다국어 지원 포트폴리오' 카테고리의 다른 글
[custom hook] 페이지 최상단으로 이동하는 버튼 (0) | 2023.10.29 |
---|---|
[custom hook] 스크롤을 드래그하며 페이지 이동 구현 (0) | 2023.10.22 |
pnpm을 이용한 모노레포 마이그레이션 (0) | 2023.10.18 |
NotionAPI로 블로그 만들기 (3) Next.js App Router 미들웨어로 redirection 설정하기 (0) | 2023.10.15 |
NotionAPI로 블로그 만들기 (2) react-notion-x로 노션 데이터베이스 불러오기 (Next.js App Router) (0) | 2023.10.11 |