글 추가할 때마다 사이트맵 파일을 손으로 고치는 게 너무 귀찮아서 자동화해버렸어요. 이것도 이 사이트(Frontend Note) 자체의 scripts/prerender.js 얘기예요. /sitemap.xml을 열어보시면 이 글 슬러그가 실제로 목록에 있는지 바로 확인하실 수 있어요.

정적 페이지랑 글 목록을 한 스크립트에서 합쳤어요

let sitemapXml = \`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url><loc>\${SITE_URL}/</loc><lastmod>2026-06-24</lastmod><priority>1.0</priority></url>
  <url><loc>\${SITE_URL}/resources</loc><lastmod>2026-06-24</lastmod><priority>0.8</priority></url>
  \`;

for (const post of posts) {
  sitemapXml += \`  <url>
    <loc>\${SITE_URL}/post/\${post.slug}</loc>
    <lastmod>\${post.updated ?? post.date}</lastmod>
    <priority>0.9</priority>
  </url>
\`;
}

About, Resources, Topics 같은 고정 페이지는 문자열 템플릿에 직접 나열하고, 글 페이지는 src/data/posts.jsposts 배열을 돌면서 post.slugpost.updated ?? post.date로 항목을 만들어요. 글을 새로 추가할 때 사이트맵 파일을 손으로 안 고쳐도 되고, posts 배열에 항목만 더하면 다음 빌드에 알아서 반영돼요.

같은 파일을 두 곳에 쓰는 이유

fs.writeFileSync(path.join(DIST_DIR, 'sitemap.xml'), sitemapXml, 'utf8');
// ...
fs.writeFileSync(path.join(PUBLIC_DIR, 'sitemap.xml'), sitemapXml, 'utf8');

만들어진 사이트맵은 실제 배포 결과물인 dist/sitemap.xml뿐 아니라 public/sitemap.xml에도 다시 써요. Vite가 기본적으로 public 폴더를 그대로 dist로 복사해서, 다음 빌드 때 public/sitemap.xml이 최신이 아니면 잠깐이라도 예전 버전이 복사될 수 있거든요. 두 곳에 같이 써두면 public/sitemap.xml도 항상 마지막 빌드 기준 최신으로 커밋에 남아서, 저장소만 봐도 지금 글 목록이랑 사이트맵이 어긋나 있는지 바로 비교할 수 있어요.

이 방식의 한계

lastmod 값은 코드에 적힌 post.updated 필드를 그대로 믿어요. 글 내용을 고치고도 updated 필드를 안 바꾸면, 사이트맵은 실제 수정일이랑 다른 날짜를 검색 엔진한테 알리게 되는 거예요. 이 스크립트 자체는 updated 값이 실제로 맞는지 검증 안 하니까, 이건 결국 글 고칠 때마다 updated도 같이 바꾸는 제 습관에 달린 문제예요.