블로그를 개발하면서 SEO(검색 엔진 최적화)는 필수로 챙겨야 할 요소라고 생각했다. 또 블로그에 새로운 페이지가 업데이트될 때마다 Google이나 다른 검색 엔진에서 어떤 과정을 거치게 될까 궁금해 찾아보다 사이트맵이라는 개념을 알게 되었다. 이 글에서는 next-sitemap 라이브러리를 사용해서 사이트맵을 생성하고, Google Search Console에 도메인을 등록하는 과정을 정리한 내용이다.
사이트맵(sitemap)이란?
간단히 말하자면 내 사이트에 어떤 페이지들이 있는지 검색 엔진에게 알려주는 역할을 한다. 어떤 페이지들이 있고, 언제 수정되었는지, 페이지의 우선순위는 어떤지 등의 정보를 포함하고 있어 검색 엔진 크롤러가 내 사이트를 더 빠르고 효율적으로 탐색할 수 있도록 돕는다.
robots.txt란?
robots.txt는 크롤러에게 어떤 페이지에 접근해도 되는지, 어떤 경로는 접근하지 말아야 하는지를 알려주는 역할을 한다. 또한 사이트맵 파일의 위치를 명시해 검색 엔진이 더 효율적으로 내 사이트를 탐색하도록 돕는다.
# *
User-agent: *
Allow: /
# Host
Host: https://nullisdefined.site
# Sitemaps
Sitemap: https://nullisdefined.site/sitemap.xml
next-sitemap 적용하기
먼저 next-sitemap라이브러리를 설치한다.
npm install next-sitemap --save-dev설정 파일 생성
프로젝트 루트 디렉터리에 next-sitemap.config.js 파일을 만들고 설정을 추가한다.
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: "https://nullisdefined.site",
generateRobotsTxt: true, // robots.txt 자동 생성
};
위 설정을 통해 sitemap.xml과 robots.txt를 자동으로 생성할 수 있다.
블로그 글 동적 경로 추가하기
이 블로그는 Markdown 파일로 글을 작성하고 Next.js에서 동적으로 불러오는 구조다. 따라서 블로그 글들의 URL을 사이트맵에 추가하려면 추가적인 작업이 필요했다.
문제점: 빌드되지 않은 src 파일 접근
next-sitemap은 실행될 때 Node.js에서 src 폴더의 TypeScript 파일을 로드하려 했는데, 빌드가 안 된 상태라서 MODULE_NOT_FOUND 에러가 발생했다. 처음에는 설정이 잘못된 줄 알고 next-sitemap의 옵션을 바꿔보았지만 해결되지 않았다. 나중에야 Node.js가 TypeScript 파일을 직접 실행할 수 없으며 빌드된 JavaScript 파일만 불러올 수 있다는 점을 깨달았다. 이 시행착오를 통해 Node.js 스크립트 실행 시 빌드된 파일만 접근해야 한다는 점을 배웠고, 이를 해결하기 위해 별도의 스크립트를 작성하게 되었다.
블로그 데이터 추출 스크립트
scripts/posts-data.js파일을 만들어 Markdown 파일에서 데이터를 추출했다.
const path = require("path");
const fs = require("fs");
const matter = require("gray-matter");
const POSTS_PATH = path.join(process.cwd(), "src/content/posts");
const SERIES_PATH = path.join(process.cwd(), "src/content/posts/series");
function getPostList(basePath) {
const allPosts = [];
const processDirectory = (dirPath, categoryPath = []) => {
const files = fs.readdirSync(dirPath);
for (const file of files) {
const fullPath = path.join(dirPath, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
processDirectory(fullPath, [...categoryPath, file.toLowerCase()]);
continue;
}
if (!file.endsWith(".md")) continue;
const fileContent = fs.readFileSync(fullPath, "utf-8");
const { data } = matter(fileContent);
if (data.draft) continue;
const urlCategory = categoryPath.join("/");
allPosts.push({
urlCategory,
slug: path.basename(file, ".md"),
date: data.date || new Date().toISOString(),
});
}
};
processDirectory(basePath);
return allPosts;
}
module.exports = {
getPostList: () => getPostList(POSTS_PATH),
getSeriesPostList: () => getPostList(SERIES_PATH),
};
이제 이 스크립트를 next-sitemap.config.js에서 사용하여 동적 경로를 추가했다.
const { getPostList, getSeriesPostList } = require("./scripts/posts-data");
module.exports = {
siteUrl: "https://nullisdefined.site",
generateRobotsTxt: true,
exclude: ["/devlog/admin/*"],
sitemapSize: 5000,
async additionalPaths() {
const posts = getPostList();
const seriesPosts = getSeriesPostList();
const allPosts = [...posts, ...seriesPosts];
return allPosts.map((post) => ({
loc: `/devlog/${post.urlCategory}/${post.slug}`,
lastmod: post.date,
changefreq: "weekly",
priority: 0.8,
}));
},
};
배포 자동화하기
사이트맵 생성 작업을 배포 과정에서 자동으로 실행되도록 스크립트를 설정했다.
"scripts": {
"build": "next build",
"postbuild": "next-sitemap"
}Vercel이나 CI/CD 과정에서 자동으로 npm run build 이후 postbuild가 실행되어 사이트맵이 생성된다.
Google Search Console에 등록하기
- Google Search Console에 접속하여 내 블로그 도메인을 등록한다.
- 도메인 소유권 확인 절차를 완료한다.
- 등록이 완료되면 Sitemaps 메뉴에서
https://도메인/sitemap.xml경로를 제출한다. - Google에서 사이트맵을 크롤링하고 내 블로그의 페이지를 인식하기 시작한다.


데이터를 처리하는데 시간이 조금 걸리는 듯하다
마치며
이번 작업을 통해 사이트맵과 SEO를 효과적으로 관리하는 방법을 익힐 수 있었다. next-sitemap 라이브러리를 활용해 정적 페이지와 동적 경로를 손쉽게 사이트맵에 포함시킬 수 있었고, 빌드된 JS 파일만 접근 가능한 구조라는 점을 알게 되면서, 이를 해결하기 위해 별도의 스크립트를 작성한 경험도 값진 배움이 되었다.
이 프로젝트의 모든 소스 코드는 GitHub에 공개되어 있습니다. 코드 품질 개선이나 새로운 기능 제안에 대한 피드백은 언제나 환영합니다.