[Gatsby.js] Trouble Shooting : SSG, SSR, DSG

Yerim·2024년 5월 21일

트러블슈팅

목록 보기
1/2
post-thumbnail

내 Gatsby 블로그엔 markdown 으로 구성된 페이지가 세 개 있다.

  • Diary 상세 페이지 (/diary/2022)
  • Project 상세 페이지 (/project/lime)
  • About 페이지 (/about)

gatsby.config.js 파일에서 blogSettings 값을 /diary 로 설정해준 뒤 /content/articles 경로에 폴더 이름을 pathname으로 지정해주고, 내부에 index.md 파일을 생성해 내용을 작성하면 되는데, (ex. /content/articles/2022/index.md)

-> 나는 기존 템플릿을 사용하지 않고 테마를 커스텀하여 새로 컴포넌트를 만들었기 때문에 이 로직을 사용할 수가 없었다 🥲

따라서 /pages 경로에 diary 폴더를 생성하고, 내부에 [title].tsx 파일을 생성하여 해당 파일에서 useEffect 로 md 파일을 가져오는 방식으로 구현을 하였다. (project도 동일)

pages
├── diary
│   ├── [title].tsx
│   └── index.tsx
├── project
│   ├── [title].tsx
│   └── index.tsx
├── about
│   └── index.tsx
...

로컬에서 실행한 결과 잘 동작하는 것을 확인하고,
배포를 진행했다..!

문제 - 1

처음 진입 시 위 사진처럼 Not Found 페이지가 잠깐 노출된 후 정상적인 페이지가 출력되었다.. (이후로 진입 시 괜찮음)

확인해보니 markdown 파일을 불러오는 과정에서 404 에러가 발생한 것을 발견하였고, 그러면 왜 Diary 페이지에서는 문제가 없었지..? 라는 의문이 생겼다.

문제 - 2

Diary 페이지에서는 처음 진입 시 이상이 없으나,
진입 후 새로고침을 하니 스타일이 하나도 적용되지 않는 문제를 발견했다...

해결과정

무엇이 문제인지 머리를 싸매던 도중에 배포하고 난 뒤 한 스크립트가 눈에 띄었다 🫣

CSR이 없다..!

빌드를 진행해보았다.

md을 사용하는 파일에서 static HTML을 만들지 못했다는 에러였다.
Gatsby는 기본적으로 SSG 방식을 사용하는데, useEffect 로 파일을 불러오고있어서 정적 HTML 파일을 만들지 못한 것이었다 😮🫢

그렇다면 SSR로 변경하면 에러가 사라질까? 라는 호기심에 SSR 방식으로 변경해보았다.

const ProjectDetailPage = ({ serverData }) => (
  <main>
    <ProjectDetailComponent markdown={serverData} />
  </main>
)

export default ProjectDetailPage

export async function getServerData(props) {
  const { params } = props;
  try {
    const res = await fetch(`https://yrim.me/assets/projects/${params.title}/index.md`)
    .then(res => { res.text() })
    .then(res => { return res })

    return {
      props: res,
    }
  } catch (error) {
    return {
      status: 500,
      headers: {},
      props: {}
    }
  }
}

변경 후 빌드해보니

위와 같이 SSR 표시가 생기며 빌드에 성공한 것을 확인할 수 있었다. 👍

그러나 Gatsby 공식 홈페이지에 SSG 혹은 DSG 방식을 지향한다고 되어 있어서 graphQL에 md 내용을 넣고, md 파일을 사용하는 페이지 컴포넌트에서 graphQL 데이터를 사용하는 방식으로 변경하여 SSG 형태가 되도록 변경해보았다.

gatsby.config.js 파일에서 폴더를 정할 수 있다고 하는데,
나는 테마를 사용하고 있기 때문에 node_modules 에서 테마 폴더로 들어가 gatsby.config.js 파일을 살펴보았다.

// gatsby.config.js
{
    resolve: `gatsby-source-filesystem`,
    options: {
		name: `content`,
		path: options.contentDirectory || path.join('.', 'content'),
	},
},

내가 지정할 수도 있고, 지정하지 않으면 /content 폴더로 설정되어 있었다.
내 프로젝트의 config 파일에도 /content 로 지정되어 있기 때문에 해당 폴더 로 md 파일들을 모두 옮겼다.

graphiQL 도구를 사용하여 데이터가 잘 들어갔는지 확인하였다.
markdown 파일의 경우 allMarkdownRemark 에 모두 저장된다.

각 페이지에서 필요한 쿼리를 짜주고, 빌드를 해보았다.

✨ 성공..!

배포하니 문제-2 는 확실히 해결되었고, 문제-1 은 여전히 간헐적으로 발생하는 것을 발견하였다.

Network 탭을 켜놓고, Diary 페이지와 Project 페이지를 비교해보니
Diary 페이지에서는 리스트 페이지에서 글 위에 커서를 올리면 page-data.json 파일을 미리 불러오지만, Project 페이지는 그렇지 않았다.

바로 prefetch 의 차이인 것을 눈치채고,
코드를 살펴보니 역시나 Diary 페이지는 prefetch 기능이 있는 Link 태그를 사용중이지만, Project 페이지는 a 태그를 사용하고 있었다.

Project 페이지도 a -> Link 태그로 변경해줌으로써 문제-1 도 완전히 해결하였다 ✨

해결된 줄 알았는데..
about 페이지에서 /project/OOO 링크를 넣을 일이 생겨 넣었더니 404 페이지가 또 노출되었다...

정적 페이지인데.. 못 찾을 수가 있나? 라고 생각하던 순간

pages
├── diary
│   ├── [title].tsx
│   └── index.tsx
├── project
│   ├── [title].tsx
│   └── index.tsx

빌드할 때 이렇게 라우터를 설정해두면 title 종류가 어떤게 있는지 어떻게 알고 미리 생성하지?

Dynamic Routing 기능이 분명히 있을 것이라 생각하고 찾아보니
2가지 방법이 있었다.

  1. Gatsby의 File System Route API인 collection routes 사용하기
  2. gatsby.node.js 에서 title 값을 가져오는 query를 짜고, createPages 를 통해 생성해주기 ⭐️

나는 2번 방식으로 구현해주었다.


// gatsby.node.js
exports.createPages = async function ({ actions, graphql }) {
  const { data: projectData } = await graphql(`
    query {
      allMarkdownRemark(
        filter: { fileAbsolutePath: { glob: "**/projectMd/**" } }
      ) {
        nodes {
          frontmatter {
            title
          }
        }
      }
    }
  `);
  projectData.allMarkdownRemark.nodes.forEach((node) => {
    const slug = node.frontmatter.title;
    actions.createPage({
      path: `/project/${slug}/`,
      component: require.resolve(`./src/templates/project/index.tsx`),
      context: { slug: slug },
    });
  });
};

Diary 데이터에 대해서도 위와 같이 쿼리와 템플릿을 적용해주고,
build를 해보니

✨ 정적인 페이지가 생성되었다..!
이전에 /project/[title]/ 로 빌드됐던 결과와는 확실히 차이가 있었다.

이렇게 문제-2 도 진짜 해결되었다 😁

회고

Gatsby.js 가 SSG 방식이라는 것을 인지하고 개발했다면 바로 원인을 찾을 수 있었던 문제인 것 같은데 CSR, SSR 방식에 익숙해서 생각을 못 한 것 같다 🥲🥲

그렇지만 이번 기회로 SSG, CSR, SSR의 차이를 확실히 구분할 수 있는 기회가 되었던 것 같아 매우 뿌듯했다 😊

또한 관련 문서들을 찾아보면서 DSG 개념도 많이 접하게 되었는데,
처음 보는 개념이라 흥미로웠고 같이 공부하는 계기가 되었다.

DSG는 "Deferred Static Generation" 의 약자로, 중요하지 않은 페이지 생성을 연기하여 빌드 시간을 단축하는 방법이라고 한다.
내 블로그는 소규모 프로젝트이기도 하고, 데이터의 양이 많지 않으니 추후에 데이터의 양이 많아진다면 적용해보는 것도 좋을 것 같다. 🔥

참고

profile
안녕하세요. 프론트엔드 개발자입니다.

0개의 댓글