[1] Sitemap 생성하기

  1. Sitemap은 쉽게 말해서 검색 엔진이 크롤링을 함으로써 사용자들이 방문할 수 있는 페이지의 목록을 나타낸 것

    1. 검색 엔진이 크롤링할 수 있도록 색인을 생성하기 전에 Sitemap을 제출하도록 하고 있음.
    2. 개발자는 검색 엔진에 노출되었으면 하는 페이지만을 Sitemap에 추가해 크롤링 속도를 개선할 수 있으므로 검색 엔진 최적화에 있어서 중요한 역할을 담당하고 있음
  2. 예시

    <urlset
    	xmlns="<http://www.sitemaps.org/schemas/sitemap/0.9>"
    	xmlns:news="<http://www.google.com/schemas/sitemap-news/0.9>"
    	xmlns:xhtml="<http://www.w3.org/1999/xhtml>"
    	xmlns:mobile="<http://www.google.com/schemas/sitemap-mobile/1.0>"
    	xmlns:image="<http://www.google.com/schemas/sitemap-image/1.1>"
    	xmlns:video="<http://www.google.com/schemas/sitemap-video/1.1>"
    >
    	<url>
    		<loc><https://ji5485.github.io/></loc>
    		<changefreq>daily</changefreq>
    		<priority>0.7</priority>
    	</url>
    	<url>
    		<loc><https://ji5485.github.io/about/></loc>
    		<changefreq>daily</changefreq>
    		<priority>0.7</priority>
    	</url>
    	<url>
    		<loc><https://ji5485.github.io/portfolio/></loc>
    		<changefreq>daily</changefreq>
    		<priority>0.7</priority>
    	</url>
    </urlset>
    

[2] Sitemap 생성을 위한 라이브러리 세팅

  1. Gatsby와 같은 정적 사이트 생성기의 경우에는 마크다운 파일을 추가함으로써 계속해서 다른 페이지가 생성되기 때문에 직접 Sitemap을 작성하기에는 큰 어려움이 있음.

  2. 프로젝트 빌드 시에 자동으로 생성되는 페이지에 대해서 Sitemap에 추가해주는 라이브러리인 gatsby-plugin-sitemap 을 사용

    npm i gatsby-plugin-sitemap
    
  3. gatsby-config.js 파일에서 설정 코드를 추가

    module.exports = {
      siteMetadata: { ... },
      plugins: [
        ...,
        'gatsby-plugin-sitemap',
      ],
    };
    
    1. 간략하게 추가할 수 있는 옵션은 sitemap 생성 경로와 이름을 설정할 수 있고, 필요한 URL만 추가할 수 있도록 지정할 수 있음.
    2. 필요하다면 공식 문서를 확인
  4. 프로젝트를 npm run build 하여 Sitemap이 잘 생성되는지 확인

    1. 아래와 같은 에러가 뜸. 서버 사이드 렌더링 작업 시에 볼 수 있는 에러

      WebpackError: ReferenceError: IntersectionObserver is not defined
      
    2. IntersectionObserver는 브라우저 API이기 때문에 클라이언트 렌더링 시에 사용할 수 있는 API

    3. 로컬 서버를 실행하게 되면 Gatsby는 일반 React 애플리케이션과 동일하게 클라이언트 사이드 렌더링을 통해 화면을 보여주기 때문에 IntersectionObserver나 window 객체와 같이 브라우저에서 제공하는 요소를 아무 문제 없이 사용할 수 있음!

    4. 하지만, Gatsby는 기본적으로 빌드 시에는 Node.js 환경에서 진행되기 때문에 위와 같은 브라우저 API를 사용할 수 없음.

[3] 빌드 시 발생되는 에러 해결 방법

  1. API또는 객체가 undefined인지 확인하는 조건문을 추가해주는 방법

    if (IntersectionObserver === undefined) return
    if (window === undefined) return
    
  2. useEffect를 사용하는 방법. (useEffect 내부에 있는 코드는 클라이언트 렌더링 시에 실행되기 때문에 빌드 시에는 실행되지 않음.)

    useEffect를 통해 useInfiniteScroll.tsx 수정.

    import { MutableRefObject, useState, useEffect, useRef, useMemo } from 'react'
    import { PostListItemType } from 'types/PostItem.types'
    
    export type useInfiniteScrollType = {
      containerRef: MutableRefObject<HTMLDivElement | null>
      postList: PostListItemType[]
    }
    
    const NUMBER_OF_ITEMS_PER_PAGE = 10
    
    const useInfiniteScroll = function (
      selectedCategory: string,
      posts: PostListItemType[],
    ): useInfiniteScrollType {
      const containerRef: MutableRefObject<HTMLDivElement | null> =
        useRef<HTMLDivElement>(null)
      const observer: MutableRefObject<IntersectionObserver | null> =
        useRef<IntersectionObserver>(null)
      const [count, setCount] = useState<number>(1)
    
      const postListByCategory = useMemo<PostListItemType[]>(
        () =>
          posts.filter(
            ({
              node: {
                frontmatter: { categories },
              },
            }: PostListItemType) =>
              selectedCategory !== 'All'
                ? categories.includes(selectedCategory)
                : true,
          ),
        [selectedCategory],
      )
    
      useEffect(() => {
        observer.current = new IntersectionObserver((entries, observer) => {
          if (!entries[0].isIntersecting) return
    
          setCount(value => value + 1)
          observer.unobserve(entries[0].target)
        })
      }, [])
    
      useEffect(() => setCount(1), [selectedCategory])
    
      useEffect(() => {
        if (
          NUMBER_OF_ITEMS_PER_PAGE * count >= postListByCategory.length ||
          containerRef.current === null ||
          containerRef.current.children.length === 0 ||
          observer.current === null
        )
          return
    
        observer.current.observe(
          containerRef.current.children[containerRef.current.children.length - 1],
        )
      }, [count, selectedCategory])
    
      return {
        containerRef,
        postList: postListByCategory.slice(0, count * NUMBER_OF_ITEMS_PER_PAGE),
      }
    }
    
    export default useInfiniteScroll
    
  3. 프로젝트 빌드를 진행한 후, 새로 생성된 public/sitemap/sitemap-index.xml 파일을 확인

  4. 빌드 성공 후 sitemap-index.xml 와 sitemap-0.xml 이런식으로 생성이 되는데 이렇게 사이트맵을 생성하는 이유는 몇몇 웹 마스터 도구에서 특정 개수(대략 5만) 이상의 URL이 담긴 사이트맵을 인식하지 못한다는 문제가 발생하기 때문

    1. 이를 해결하기 위해 모든 URL을 단위 개수만큼 나눈 후, 나눠진 개수만큼 sitemap-0.xml, sitemap-1.xml, ...의 사이트맵을 생성
    2. 생성된 사이트맵을 모두 sitemap-index.xml 파일에서 참조
    3. 이런 원리에 의해 URL의 개수가 아무리 많다고 하더라도 웹 마스터 도구에서 인식하게 만들 수 있는 것
  5. 생성된 public/sitemap/sitemap-0.xml 예시

    <?xml version="1.0" encoding="UTF-8"?>
    <urlset
      xmlns="<http://www.sitemaps.org/schemas/sitemap/0.9>"
      xmlns:news="<http://www.google.com/schemas/sitemap-news/0.9>"
      xmlns:xhtml="<http://www.w3.org/1999/xhtml>"
      xmlns:mobile="<http://www.google.com/schemas/sitemap-mobile/1.0>"
      xmlns:image="<http://www.google.com/schemas/sitemap-image/1.1>"
      xmlns:video="<http://www.google.com/schemas/sitemap-video/1.1>"
    >
      <url>
        <loc><https://my-website-link.com/test/></loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>
      <url>
        <loc><https://my-website-link.com/></loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
      </url>
    </urlset>
    

    a. URL의 호스트 부분에 해당되는 my-website-link.com 부분은 gatsby-config.js 파일 내에서 siteMetadata 내부에 있는 siteUrl 프로퍼티를 참조하기 때문에 프로젝트 배포 후 변경 (나중에)


참고자료