• Home
  • About
  • Posts
  • 1. 튜토리얼 방식의 한계
  • 2. CreatePage
  • 2.1. gatsby-node
  • 2.2. template 컴포넌트
  • 3. 참조

07. Gatsby 커스텀 페이지 생성

📅 2023-06-27
🖋️ Byongho96
  • gatsby
  • 커스텀 페이지

1. 튜토리얼 방식의 한계

Gatsby 튜토리얼 Part6 를 보면 file system routes라고 해서 파일시스템에 있는 파일들에 대해서 동적으로 페이지를 생성하는 방법을 소개한다.

예를 들어 아래 그림처럼 src/pages/아래에 {mdx.frontmatter_slug}라는 파일을 만들면, 파일 시스템이 관측하는 모든 mdx의 문서에 대해서 {mdx.frontmatter_slug}라는 url로 페이지를 만들어 준다. 더 자세하게 말하면, gatsby-config파일에서 gatsby-source-filesystem가 관측하는 경로의 모든 mdx파일에 대해서 페이지를 만들어준다

file-system-routes.png

위 방법으로 간단하게 모든 파일에 대응되는 페이지를 만들 수 있었지만, 나한테는 적합하지 않았다. 왜냐하면 나는 모든 파일이 아닌 필터링된 일부 파일에 대해서만 페이지를 만들고 싶었기 때문이다. 개인적인 상황을 좀 더 구체적으로 말하자면, 내 *.md 파일들의 frontmatter는 다음처럼 구성되어 있다. 나는 이 중 isCompleted: true인 파일들에 대해서만 페이지를 만들고 싶었다. 내가 의욕이 많은 편이라 isCompleted가 false 문서가 많다

---
title: ''
updatedAt: 'YYYY-MM-DD'
createdAt: 'YYYY-MM-DD'
isCompleted: boolean
description: ''
tags: ['']
reference: ''
---

2. CreatePage

Gatsby는 나 같은 사람들을 위해서 또 node API를 만들었다. node API 중 createPages를 이용하면, 프로그래밍적으로 내가 원하는 커스텀 페이지를 만들 수 있다.

2.1. gatsby-node

페이지를 만드는 작업은 빌드 시에 일어나기 때문에, 당연히 createPages는 Node API 이다. 그리고 Node API이기 때문에 gatsby-node파일에 작성한다.

사용 예시가 공식문서에 잘 정리되어 있어, 나는 그냥 따라서 코드를 작성했다.

한 가지 특별히 설명하고 싶은 거는 pageContext이다. 마지막 createPage함수에 보면 path, component, context가 인자로 들어간다. 각각의 인자의 특징은 다음과 같다.

  • path
    • 페이지의 url 경로.
  • component
    • 렌더링할 페이지 컴포넌트.
  • context
    • 페이지에 pageContext라는 이름으로 prop된다.
    • 페이지 query에서 인자로 바로 참조할 수 있다.
// createPages API로 커스텀 페이지 생성
exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions

  // 페이지를 만들 md 데이터 수집
  // 조건 1. frontmatter.isCompleted == true
  // 조건 2. posts 디렉토리 내부 && README 파일 제외
  const posts = await graphql(
    `
      query AllPost {
        allMarkdownRemark(
          filter: {
            frontmatter: { isCompleted: { eq: true } }
            fileAbsolutePath: { regex: "/^(?!.*README).*posts.*$/" }
          }
        ) {
          nodes {
            id
            parent {
              ... on File {
                id
                name
                relativeDirectory
                relativePath
              }
            }
          }
        }
      }
    `
  )

  // 에러 처리
  if (posts.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  // 사용할 템플릿(페이지) 컴포넌트
  const postTemplate = path.resolve(`src/templates/post/index.tsx`)

  // graphQL로 불러온 데이터를 순회하며 페이지 생성
  posts.data.allMarkdownRemark.nodes.forEach((node) => {
    const path = 'posts/' + node.parent.relativePath // url 경로 커스텀
    const relativeDirectory = node.parent.relativePath // 추가로 내려주고 싶은 pageContext
      .split('/')
      .slice(0, -1)
      .join('/')
    const regex = new RegExp(
      `^(?!.*README).*${relativeDirectory}.*$`
    ).toString()

    // 페이지 생성
    createPage({
      path: path, // url 경로
      component: postTemplate, // 페이지 템플릿
      context: {
        // pageContext
        siblingPostsPathRegex: regex,
        ...node,
      },
    })
  })
}

2.2. template 컴포넌트

그러면 템플릿 컴포넌트에서는 다음과 같이 코드를 작성할 수 있다.

앞서 말한 것처럼 gatsby-node파일에서 작성한 코드에서, createPage함수에 context에 내려준 값은 페이지에 pateContext라는 prop으로 내려간다. 그 뿐 아니라, pageContext의 내부 값은 페이지 query에서 바로 참조할 수 있다.

그래서 나는 pageContext로 prop받은 markdownRemark의 id와 형제 포스트를 검색하기 위해 커스텀해서 내려준 정규식 siblingPostsPathRegex을 사용해서, 세부 정보를 위한 query를 만들었다.

import * as React from 'react'
import type { PageProps } from 'gatsby'
import { graphql } from 'gatsby'

const PostPage: React.FC<PageProps> = ({ pageContext, data }) => {
  console.log(pageContext) // createPage 함수에서 내려준 context

  return <div dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }} />
}

// pageContext의 내부 값은 페이지 query에서 바로 참조 가능
export const query = graphql`
  query PostDetail ($id: String!, $siblingPostsPathRegex: String!) {
    markdownRemark(id: { eq: $id }) {
      id
      html
      tableOfContents
      frontmatter {
        ...
      }
    }
    allMarkdownRemark(
      filter: {
        frontmatter: { isCompleted: { eq: true } }
        fileAbsolutePath: { regex: $siblingPostsPathRegex }
      }
    ) {
      nodes {
        ...
      }
    }
  }
`

export default PostPage

3. 참조

  • Gatsby Getting Started Part6 -Gatsby Programatically Create Pages from Data
이전 포스트

06. Gatsby 네비게이션 레이아웃 설정

다음 포스트

08. 폴더 구조 기반 Gatsby 카테고리 바 생성

작성자 프로필
전체 글 (127)
  • Animation
    • Backend
      • Django
      • Spring
    • DevOps
      • AWS
      • CI&CD
      • Docker
      • Git
      • Gunicorn
      • Kubernetes
      • Nginx
    • Frontend
      • Gatsby
      • React
      • Vue
    • Knowledge
      • .etc
      • Algorithm
      • Data Structure
      • Database
      • Design Pattern
      • Interview
      • Network
      • Web
    • Language
      • CSS
      • HTML
      • Java
      • JavaScript
      • Linux
      • Python

    Copyright © 2023 Byongho96  & Powered by Gatsby