Next.js에서 동적인 sitemap.xml 만들기

2022.11.01

xml icon

nextjs에 동적인 sitemap.xml 제공하기


사이트맵이 필요한데 자동으로 만들어지는거 없나?


이 블로그에도 구글에 노출되기 위해 사이트맵이 필요해서 적용하기로했다.(사실 기본적인거지만) 이 사이트맵이란 새로운 포스트가 등록될때마다 업데이트를 해줘야한다. 그래서 다른 블로그들은 이걸 어떻게 처리하고있는지 찾아봤다. 대부분의 경우 sitemap.xml을 생성해주는 스크립트가 있는데 문제는 그걸 직접 실행시켜서 /public/sitemap.xml 파일을 새로 만들어내는 방법이었다. vercel이나 gatsby 의 경우 어떤식으로 동작하는지는 모르겠지만 이 블로그 같은 경우 aws에 배포를한 상태고 /public 경로에있는 내용들을 업데이트 하려면 매번 새로 빌드를하고 다시 배포를 해야하는 상황이다. 그래서 nextjs에서 자동으로 동적인 사이트맵을 생성해주는 기능이 필요로했고 다음 같은 두가지 방법을 찾았다.

getServerSideProps를 이용한 방식


먼저 이 방법은 getServerSideProps의 res 객체를 이용한 일종의 트릭이다.

const Sitemap = () => {};

export const getServerSideProps = ({ res }) => {
  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      <!-- We'll render the URLs for our sitemap here. -->
    </urlset>
  `;

  res.setHeader("Content-Type", "text/xml");
  res.write(sitemap);
  res.end();

  return {
    props: {},
  };
};

export default Sitemap;
  1. /pages 폴더에 sitemap.xml.js 파일을 생성한다.
  2. 해당 컴포넌트는 페이지를 렌더링하지 않도록 빈 객체만 리턴한다.
  3. getServerSideProps의 파리미터 res 객체를 이용해 xml을 클라이언트로 보낸다.

getServerSideProps의 res 객체는 nodejs 환경의 response 객체이다. 그래서 next 서버에서 nodejs 백엔드 서버 처럼 xml파일 자체를 응답하는게 가능하다. 따라서 사용자가 /sitemap.xml 경로에 접근한다면 렌더링된 페이지가 아닌 동적으로 생성된 xml파일을 받게 된다.

실제 nextjs에서 사이트맵을 생성해주는 next-sitemap 이라는 패키지의 소스코드 도 똑같은 방식으로 구현되어있다.

//next-sitemap 패키지의 동적 xml 구현부분
import type { GetServerSidePropsContext } from 'next'

/**
 * Send XML response
 * @param ctx
 * @param content
 * @returns
 */
export const withXMLResponse = (
  ctx: GetServerSidePropsContext,
  content: string
) => {
  if (ctx?.res) {
    const { res } = ctx

    // Set header
    res.setHeader('Content-Type', 'text/xml')

    // Write the sitemap context to resonse
    res.write(content)

    // End response
    res.end()
  }

  // Empty props
  return {
    props: {},
  }
}

/api 경로를 이용한 방식


이 방법은 vercel 에서 직접 소개하고 있는 방법이다. nextjs 에서 /api 로 시작하는 경로는 next 서버 자체에서 api를 제공하기 위해 사용된다. next 자체가 마치 api 엔드포인트처럼 동작하도록 만들수있는데 아무튼 이 방법도 결국은 res 객체를 사용하는 방법이다.

next에서 api 경로를 작성하기 위해선 반드시 handler 함수를 내보내야한다. 이 핸들러 파라미터에 req 와 res 객체가 있다. getServerSideProps 방법과 마찬가지로 이 핸들러 안에서 동적으로 xml 파일을 생성후 res 객체로 응답해준다.

export default function handler(req, res) {

  res.statusCode = 200
  res.setHeader('Content-Type', 'text/xml')
    
    // Instructing the Vercel edge to cache the file
    res.setHeader('Cache-control', 'stale-while-revalidate, s-maxage=3600')
    
    // generate sitemap here
    const xml = `<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 
    <url>
      <loc>http://www.example.com/foo.html</loc>
      <lastmod>2021-01-01</lastmod>
    </url>
    </urlset>`

  res.end(xml)
}

/sitemap.xml 주소로 연결하기 위해 next.config.js 파일에 rewrites 규칙을 작성해준다.


module.exports = {
  async rewrites() {
    return [
      {
        source: '/sitemap.xml',
        destination: '/api/sitemap',
      },
    ]
  },
}

이렇게 설정해두면 사용자가 /sitemap.xml에 접근했을때 실제로는 /api/* 경로의 응답을 보게된다.


reference

Do you want something exciting?

© 2022. YSH All rights reserved.