Next.js(Pages Router) v16에서 다국어 사이트맵 만드는 방법
규모에 맞게 언어별로 사이트맵 구성하기
문제
사이트맵은 검색 엔진이 웹사이트의 페이지를 발견하고 색인화하는 데 도움을 줍니다. 언어별로 수백 또는 수천 개의 페이지가 있는 다국어 사이트의 경우, 모든 로케일의 모든 URL을 나열하는 단일 사이트맵은 빠르게 관리하기 어려워집니다. 대규모 단일 사이트맵은 사이트맵 프로토콜에서 정의한 50,000개 URL 또는 50MB 크기 제한을 초과하여 유효하지 않게 될 수 있습니다. 제한 내에 있더라도, 한 언어의 콘텐츠가 변경될 때마다 거대한 파일을 재생성하고 검증하는 것은 비효율적입니다. 사이트가 성장하고 더 많은 언어나 페이지를 추가함에 따라 이 접근 방식은 확장성이 떨어집니다.
해결책
사이트맵 인덱스 파일을 사용하여 사이트맵을 계층 구조로 구성합니다. 인덱스 파일은 각각 단일 로케일에 대한 URL을 포함하는 별도의 언어별 사이트맵을 나열합니다. 이 구조는 개별 사이트맵 파일을 관리하기 쉽게 유지하고 프로토콜 제한 내에 있도록 합니다. 한 언어의 콘텐츠가 변경되면 해당 언어의 사이트맵만 재생성하면 됩니다. 이 접근 방식은 새로운 언어가 추가됨에 따라 자연스럽게 확장됩니다—각 언어는 인덱스에서 참조되는 자체 사이트맵을 갖게 됩니다. 검색 엔진은 먼저 인덱스를 크롤링한 다음 개별 언어 사이트맵으로 연결되는 링크를 따릅니다.
단계
1. 사이트맵 인덱스 페이지 생성
getServerSideProps를 사용하여 동적으로 사이트맵 인덱스를 생성하기 위해 pages 디렉토리에 페이지를 생성합니다.
import { GetServerSideProps } from "next";
const SITE_URL = "https://example.com";
const LOCALES = ["en", "es", "fr", "de"];
function generateSitemapIndex(locales: string[]): string {
const sitemapEntries = locales
.map((locale) => {
return `
<sitemap>
<loc>${SITE_URL}/sitemap-${locale}.xml</loc>
<lastmod>${new Date().toISOString()}</lastmod>
</sitemap>`;
})
.join("");
return `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${sitemapEntries}
</sitemapindex>`;
}
export const getServerSideProps: GetServerSideProps = async ({ res }) => {
const sitemap = generateSitemapIndex(LOCALES);
res.setHeader("Content-Type", "text/xml");
res.write(sitemap);
res.end();
return {
props: {},
};
};
export default function SitemapIndex() {}
인덱스는 <sitemapindex> 루트 요소를 사용하며, 각각 언어별 사이트맵을 가리키는 <loc> 자식을 포함하는 <sitemap> 항목을 가집니다. getServerSideProps 함수는 Content-Type 헤더를 text/xml로 설정하고 XML 응답을 직접 작성합니다.
2. 언어별 사이트맵 페이지 생성
각 언어별 사이트맵을 생성하기 위한 동적 라우트 페이지를 만듭니다.
import { GetServerSideProps } from "next";
const SITE_URL = "https://example.com";
interface PageData {
slug: string;
lastModified: string;
}
async function getPagesByLocale(locale: string): Promise<PageData[]> {
return [
{ slug: "about", lastModified: "2024-01-15" },
{ slug: "contact", lastModified: "2024-01-20" },
];
}
function generateSitemap(locale: string, pages: PageData[]): string {
const urlEntries = pages
.map((page) => {
return `
<url>
<loc>${SITE_URL}/${locale}/${page.slug}</loc>
<lastmod>${page.lastModified}</lastmod>
</url>`;
})
.join("");
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urlEntries}
</urlset>`;
}
export const getServerSideProps: GetServerSideProps = async ({
params,
res,
}) => {
const locale = params?.locale as string;
const pages = await getPagesByLocale(locale);
const sitemap = generateSitemap(locale, pages);
res.setHeader("Content-Type", "text/xml");
res.write(sitemap);
res.end();
return {
props: {},
};
};
export default function LocaleSitemap() {}
이 동적 라우트는 URL 매개변수에서 로케일을 추출하고 해당 언어의 URL만 포함하는 XML을 생성합니다. 각 사이트맵은 단일 로케일의 콘텐츠에 집중됩니다.
3. 로케일별 콘텐츠 가져오기
플레이스홀더 getPagesByLocale 함수를 실제 데이터 소스로 대체합니다.
async function getPagesByLocale(locale: string): Promise<PageData[]> {
const response = await fetch(
`https://api.example.com/pages?locale=${locale}`,
);
const data = await response.json();
return data.pages.map((page: any) => ({
slug: page.slug,
lastModified: page.updatedAt,
}));
}
이 함수는 지정된 로케일에 대한 페이지를 검색하기 위해 CMS, 데이터베이스 또는 API를 쿼리합니다. 사이트맵 생성기가 XML 항목으로 변환하는 구조화된 데이터를 반환합니다.
4. 각 사이트맵에 정적 페이지 추가
동적 콘텐츠와 함께 모든 언어에 존재하는 정적 라우트를 포함합니다.
function generateSitemap(locale: string, pages: PageData[]): string {
const staticPages = [
{ slug: "", lastModified: new Date().toISOString() },
{ slug: "about", lastModified: new Date().toISOString() },
];
const allPages = [...staticPages, ...pages];
const urlEntries = allPages
.map((page) => {
const path = page.slug ? `/${locale}/${page.slug}` : `/${locale}`;
return `
<url>
<loc>${SITE_URL}${path}</loc>
<lastmod>${page.lastModified}</lastmod>
</url>`;
})
.join("");
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urlEntries}
</urlset>`;
}
정적 페이지와 동적 페이지를 결합하여 각 언어 사이트맵이 완전하도록 보장합니다. 정적 페이지는 마지막 수정 날짜로 현재 타임스탬프를 사용합니다.
5. robots.txt에 인덱스 참조하기
검색 엔진이 발견할 수 있도록 robots.txt 파일에 사이트맵 인덱스 위치를 추가하세요.
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
인덱스 파일만 나열하면 됩니다; 검색 엔진은 개별 언어 사이트맵으로의 링크를 자동으로 따라갑니다. 이 파일을 public 디렉토리에 배치하세요.