article content thumbnail

Next.js App Router 에서 동적 사이트맵(Sitemap) 만들기

Next.js의 generateSitemap() 와 sitemap.ts를 이용해서 동적 sitemap을 만들어보자!

palms.blog 는 SEO가 무척 중요한 만큼, SEO 최적화를 위해 동적 sitemap을 구현했다.



사실 Next.js 에 이미 동적 사이트맵을 만들어주는 Function generateSitemaps 가 나와있어서 편리하게 구현했다.



generateSitemaps 공식문서



하나의 사이트맵 마다 URL이 50,000개로 제한되기 때문에,

우선 각 블로그마다 사이트맵을 별도로 두는 방식을 선택했다.






Next.js에서 sitemap을 js나 ts 코드로 작성하려면 파일 이름을 sitemap.js 또는 sitemap.ts 로 지정해야 한다. (File Convention)



아래 코드는 모두 📁 app/sitemap.ts 한 파일의 코드다.



step 1️⃣ : 사이트맵 동적 생성

먼저 generateSitemaps() 함수 안에서 각각 unique한 id값을 가진 객체 배열을 사이트맵 개수만큼 return 해줘야 한다.



예를 들어 사이트맵이 네개라면 return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]



나는 모든 블로그 리스트를 fetch했고,

각 블로그의 블로그 url은 고유하기 때문에 이 값을 id값으로 사용했다.

export async function generateSitemaps() {
  const { data: blogListInfo } = await getBlogListInfo(); // 모든 블로그 list fetchreturn blogListInfo.map(({ blogUrl }) => ({
    id: blogUrl,
  }));
}

return [{id: 'official'}, {id: 'seohyun'}, {id: 'duckduck'} ... ] 꼴이 return 되는 것!



이 id는 사이트맵이 존재하는 url에 사용되게 된다.

실제 사이트맵의 url은 /.../sitemap/[id].xml 즉, 내가 app/sitemap.ts에 파일을 두었으니 실제 사이트맵 url은 {DOMAIN}/sitemap/[id].xml이 된다.



❗ 개발을 할 때는 경로가 /.../sitemap.xml/[id] 즉, app/sitemap.ts에 위치하는 경우 http://localhost:3000/sitemap.xml/[id] 에서 확인할 수 있다는 점 참고!



여담이지만..

중간에 generateSitemaps() 함수가 이상하다는걸 발견했었다..

아무리 이런 저런 방법으로 시도해봐도 난 공식 문서대로 했는데 분명 뭔가 동작하는 로직에서 에러가 나서 이건 아무리 봐도 함수에 문제가 있는게 맞다!!!! 하는 생각이 들었다. 그래서 이걸 고쳐서 contribute해보는 꿈에 잠시 부풀었지만..

그 순간에 생각이 번뜩 든건 이걸 누가 안 고쳐놨을리 없다..^^

그래서 next.js를 업데이트 해보니 잘만 돌아가더라 . . . 😭

또 하나 배운다,,



step 2️⃣ : url 동적 생성 로직 짜기

그럼 아래 sitemap 컴포넌트는 위 generateSitemap이 반환한 각 객체에 대하여 id 값을 전달 받아 실행된다. 그래서 우리는 이 id값을 이용해 동적으로 사이트맵을 생성하는 로직을 짜면 된다.



원래 sitemap.xml 의 형식은 다음과 같다.

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://www.example.com/foo.html</loc>
    <lastmod>2022-06-04</lastmod>
  </url>
</urlset>



sitemap 컴포넌트에서는 이런식으로 넣고 싶은 값들을 객체 배열로 return 해주면 위처럼 정적인 사이트맵을 만들어준다.

(*url, lastModified만 필수값)

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://acme.com',
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 1,
    },
    {
      url: 'https://acme.com/about',
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
  ]
}



그래서 내가 코드는 다음과 같다

아티클, 페이지, 멤버, 카테고리 url을 모두 만들어야 하지만

아티클 url 만 코드를 예로 들어 보겠다.

import { MetadataRoute } from 'next';

export default async function sitemap({ id }: { id: string }): Promise<MetadataRoute.Sitemap> {

  // 모든 아티클 정보 받아오기const { data: articles } = await getBlogArticleList(id);

  // 모든 아티클 url 생성const articleUrlList = articles.map(({ articleUrl }) => ({
    url: `https://${id}.palms.blog/${articleUrl}`,
    lastModified: new Date(),
  }));

  return [
    ...articleUrlList,
  ];
}



step 3️⃣

sitemap.ts 는 default로는 애플리케이션이 빌드될 때 정적으로 파일을 생성한다.

그런데 palms.blog의 경우 매번 업데이트되는 아티클, 멤버 등의 정보들을 동적으로 반영해서 생성해야 하기 때문에 항상 동적으로 렌더링 되도록 아래 코드를 추가했다.

export const dynamic = 'force-dynamic'; 



step 4️⃣ : 사이트맵 색인 파일 만들기

우리는 여러 개의 사이트맵을 생성하기 때문에, 이 사이트맵이 각각 어느 위치에 있는지 한 곳에서 알려줄 사이트맵 색인 파일도 필요하다.



그래서 구글 써치 콘솔에는 이 파일만 제출하면 알아서 각 사이트맵을 찾아가 인식할 수 있다.



❗ 주의할 점은 사이트맵 색인 파일에서 참조되는 사이트맵은 사이트맵 색인 파일과 동일한 디렉터리에 있거나 사이트 계층 구조에서 더 낮은 위치에 있어야 한다는 점!



📁 app/sitemap.xml

app 디렉토리 바로 하위에 사이트맵 색인(index)이 담긴 sitemap.xml 파일을 두었다.



각 사이트맵의 위치는 https://palms.blog/sitemap/sitemap/{블로그이름}.xml 꼴이기 때문에, 아래처럼 모든 블로그에 대해 사이트맵 위치를 넣어주었다.



지금은 블로그 생성 속도가 빠르지 않아서 정적으로 직접 수정하는 방식을 선택했지만, 후에는 인덱스도 동적으로 생성 가능하도록 변경해보려고 한다.

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <sitemap>
        <loc>https://palms.blog/sitemap/official.xml</loc>
    </sitemap>
    <sitemap>
        <loc>https://palms.blog/sitemap/seohyun.xml</loc>
    </sitemap>
    <sitemap>
        <loc>https://palms.blog/sitemap/duckduck.xml</loc>
    </sitemap>
</sitemapindex>

(실제로는 훨씬 많은 사이트맵이 포함되어 있다!)



step 5️⃣ : 구글 써치 콘솔 등록하기

만들어 놓고 써치 콘솔에 등록을 안하면 무용지물..

여기서 본인 도메인을 치고

사이트맵이 위치한 url을 넣어주면 된다!

나의 경우에는 사이트맵 색인의 위치를 넣어주었다.



그리고 나서 처음에는 들어가보면 실패가 마구마구 뜨는데,

시간이 좀 지나고 나서 다시 들어가보면 모두 성공! 🟢



최신 아티클
Article Thumbnail
김서현
|
2024.06.30
[Trouble Shooting] 붙여넣기된 base64 이미지를 CDN 주소로 갈아끼우자! (feat. tiptap)
매우 느린 렌더링 속도의 원인이었던 base64 이미지 처리를 tiptap 라이브러리 환경에서 해결해 나가는 과정 기록하기
Article Thumbnail
김서현
|
2024.06.18
SSR 환경의 반응형 깜빡임 현상 해결하기
Next.js SSR 환경에서 모바일로 접근했을 때 일어나는 깜빡임 현상을 해결해보자
Article Thumbnail
김서현
|
2024.06.05
SEO 100점 만들기 마지막 단계 - robots.txt
Next.js의 middleware를 이용해 구현한 서브도메인(블로그)에서 생긴 robots.txt 관련 에러 해결하기