본문 바로가기
Programming/Javascript

개츠비(Gatsby) 마크다운 블로그 만들기 시작하기

by Wilkyway 2023. 7. 26.
반응형

 

정적사이트 생성기(SSG, Static Site Generator)에 대해 공부하다보니 다시 Gatsby에 대한 흥미가 돌기 시작해서 오랫만에 Gatsby로 프로젝트를 만들어봤습니다. 예전에 이걸 공부했던 적이 있었나 싶을 정도로 하나도 기억이 안나더군요. React에 대한 두려움으로 React 계통의 프레임워크들은 무조건 멀리했었던 것 같기도 하고, 사실은 Gatsby, GraphQL에 대해 정확히 공부한적이 없었던 것 같기도 하네요. 그래서 오늘은 설치하고 마크다운 문서 표기하기까지 알게된 내용을 정리해볼까 합니다.

 

[ 목차 ]

     

    1. Gatsby란?

    Gatsby는 서두에 말했듯이 React에 기반한 정적사이트 생성기(SSG, Static Site Generator)입니다. 예전부터 Ruby on Rails로 작성된 Jekyll 이 가장 많이 사용되었었는데, React에 기반한 점, GraphQL을 사용하여 이미지, 컨텐츠 등 데이터를 빠르고 쉽게 반영할 수 있는점 등으로 매우 큰 인기를 끌고 있습니다. 거기다 다양한 테마를 적용한 스타터 킷이 존재하여 사용자 편의성을 지원하고 있습니다. 이전 포스트(정적 사이트 생성기 (SSG, Static Site Generator) 로 나만의 블로그를 만들어보자 (tistory.com))에서도 소개했듯이 다양한 SSG가 존재하지만 그 중에서도 Gatsby는 만들어진 이래 항상 최상위권을 유지하고 있습니다. 

      이제 한단계씩 시작해보겠습니다. 이번 프로젝트에서도 마크다운 문서들을 이용해 각 URL별로 문서를 표시하는 기능을 만들어보겠습니다.

     

     

    2. 설치

    아래의 명령어로 글로벌 설치를 합니다.

    npm i -g gatsby-cli

     

    3. 프로젝트 생성

    gatsby new [프로젝트(폴더) 이름]
    
    //현재 폴더에 프로젝트를 구성할 경우 폴더명(".") 입력
    gatsby new .  // 개인적으로 이 방법 자주씀

     참고로, 스타터킷으로 시작하는 것도 좋은 방법입니다.(Starters Library | Gatsby (gatsbyjs.com)) 하지만 저는 아직 실력이 부족하여 남이 써놓은 코드를 아직 수정을 못하다보니, 직접 하나씩 만들어가고 있습니다.

     

    4. 결과 보기

    package.json에서 "script"부분을 조금 수정하여, develop 대신 dev로 간단히 바꿔놓았습니다.

    //from
    "develop": "gatsby develop"
    
    // to
    "dev": "gatsby develop",

    이제 아래의 명령어로 시작하면..

    npm run dev

    아래와 같이 2개의 URL이 주어지는데, 첫번째 URL이 결과 화면이고, 두번째는 graphql 관련 페이지입니다.

     

     

     

    5. Index페이지에서 마크다운 문서 리스트 보이기

    다음으로 Index페이지에 마크다운 문서들의 목록을 보여주도록 구성하겠습니다. 우선 마크다운 샘플 문서를 3개정도 만들어줍니다. 위치는 루트 디렉토리 내에 /content/posts폴더를 새로 만들어줍니다.

     

    [content/posts/post1.md] 새 폴더와 함께 새 파일 생성

    ---
    title: My Markdown Page
    date: "2023-07-25T22:12:03.284Z"
    description: This is an example of a Markdown page.
    slug: page1
    ---
    
    # My Markdown Page
    
    This is an example of a Markdown page. You can use Markdown syntax to format the content of your pages. 
    
    ## Subheading
    
    Here's another section with a subheading. You can use various formatting options such as **bold**, *italic*, and `code` to add emphasis.

    비슷하게 post2.md, post3.md파일을 생성해줍니다.

    slug는 추후 우리가 접속하게될 url주소입니다. post2.md에는 slug: page2, post3.md에는 slug: page3라고 작성합니다.

     

    [gatsby-config.js파일 plugin설정]

    gatsby-config.js파일에서 두가지 설정을 해주어야 합니다.

    첫째는 `gatsby-source-filesystem` 플러그인으로 posts폴더를 인식시켜주는 것이고,

    둘째는 `gatsby-transformer-remark` 를 이용해 마크다운 문서를 HTML로 변환시켜주는 것입니다.

     

    <posts 폴더 인식>

    gatsby-config.js파일의 plugin 부분을 보면 `gatsby-source-filesystem`을 이용해 images폴더를 설정한 부분이 있습니다. 이미 해당 플러그인이 설치가 되있다는 얘기입니다. package.json에서도 확인이 가능합니다. 그 아래쪽에 한단락을 복사해서 posts폴더를 인식할 수 있도록 설정을 추가합니다.

    module.exports = {
      siteMetadata: {
        title: `Gatsby Default Starter`,
        description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
        author: `@gatsbyjs`,
        siteUrl: `https://gatsbystarterdefaultsource.gatsbyjs.io/`,
      },
      plugins: [
        `gatsby-plugin-image`,
        {
          resolve: `gatsby-source-filesystem`,
          options: {
            name: `images`,
            path: `${__dirname}/src/images`,
          },
        },
        { // 추가 여기서부터
          resolve: `gatsby-source-filesystem`,
          options: {
            name: `posts`,
            path: `${__dirname}/content/posts`,
          },
        }, // 여기까지
        `gatsby-transformer-sharp`,
        `gatsby-plugin-sharp`,
        {
          resolve: `gatsby-plugin-manifest`,
          options: {
            name: `gatsby-starter-default`,
            short_name: `starter`,
            start_url: `/`,
            background_color: `#663399`,
            // This will impact how browsers show your PWA/website
            // https://css-tricks.com/meta-theme-color-and-trickery/
            // theme_color: `#663399`,
            display: `minimal-ui`,
            icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
          },
        },
      ],
    }

    <마크다운 문서 변환>

    마크다운 문서 변환을 위해서는 `gatsby-transformer-remark` 플러그인을 우선 설치해야합니다.

    npm i gatsby-transformer-remark

    다음으로 gatsby-config.js파일에 해당 플러그인이 적용되도록 한줄 추가해넣습니다.

    <src/pages/index.js 파일 수정>

    index.js파일을 아직은 잘 모르겠지만, 아래와 같이 수정합니다. 아래부분은 GraphQL을 이용해 내가만든 컨텐츠(md파일들)로부터 정보를 가져오는 부분입니다.

    import React from "react"
    import { Link, graphql } from "gatsby"
    // import { css } from "@emotion/core"
    // import { rhythm } from "../utils/typography"
    import Layout from "../components/layout"
    
    export default ({ data }) => {
      console.log(data)
      return (
        <Layout>
          <div>
            <h1>
              Amazing Pandas Eating Things
            </h1>
            <h4>{data.allMarkdownRemark.totalCount} Posts</h4>
            {data.allMarkdownRemark.edges.map(({ node }) => (
              <div key={node.id}>
                <h3>
                  {node.frontmatter.title}{" "}
                  <span>
                    — {node.frontmatter.date}
                  </span>
                </h3>
                <p>{node.excerpt}</p>
              </div>
            ))}
          </div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query {
        allMarkdownRemark {
          totalCount
          edges {
            node {
              id
              frontmatter {
                title
                date(formatString: "DD MMMM, YYYY")
              }
              excerpt          
            }
          }
        }
      }
    `

    <결과>

    아래와 같이 결과와 내용으로 구성된 리스트가 잘 나타납니다.

     

    6. 마크다운 문서 보이기

    리스트는 나오는데, 각 포스트별 페이지도 없고 링크도 안됩니다. 이 부분을 추가해보도록 하겠습니다.

     

    </src/templates/md-files.js>

    우선 md-files.js라고 템플릿 파일을 하나 생성합니다. GraphQL로부터 값들을 가져와 화면에 뿌려주는 역할을 합니다. 

    import React from "react"
    import { graphql } from "gatsby"
    import Layout from "../components/layout"
    
    export default function Template({ data }) {
      // console.log(data)
      const { markdownRemark } = data
      const { frontmatter, html } = markdownRemark
      return (
        <Layout>
          <div className="blog-post-container">
            <div className="blog-post">
              <h1>{frontmatter.title}</h1>
              <h2>{frontmatter.date}</h2>
              <div dangerouslySetInnerHTML={{ __html: html }} />
            </div>
          </div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) { 
          html
          frontmatter {
            date(formatString: "MMMM DD, YYYY")        
            title
          }
        }
      }
    `

    markdownRemark(fields: {slug: {eq: $slug} }) 부분은 slug를 키 값으로 하여 각 포스트의 정보를 가져오라는 의미입니다.

     

    <gatsby-node.js>

    page를 생성해주는 템플릿 파일을 만들었으면, 이제 적용해주어야 합니다. 아래와 같이 gatsby-node.js를 작성하여 실행되도록 합니다. createPages는 gatsby에서 페이지를 생성할 때 실행되고, onCreateNode함수는 노드가 생성될 때마다 실행됩니다. onCreateNode 함수에서 마크다운 파일을 읽어 Slug 값을 추출하고, createNodeField 함수를 사용하여 노드에 Slug 값을 추가합니다. 

    const path = require('path');
    
    exports.createPages = ({ graphql, actions }) => {
      const { createPage } = actions;
    
      const blogPostTemplate = path.resolve('src/templates/md-files.js');
    
      return graphql(`
        {
          allMarkdownRemark {
            edges {
              node {
                fields {
                  slug
                }
              }
            }
          }
        }
      `).then((result) => {
        if (result.errors) {
          return Promise.reject(result.errors);
        }
    
        result.data.allMarkdownRemark.edges.forEach(({ node }) => {
          createPage({
            path: node.fields.slug,
            component: blogPostTemplate,
            context: {
              slug: node.fields.slug,
            },
          });
        });
      });
    };
    
    exports.onCreateNode = ({ node, getNode, actions }) => {
      const { createNodeField } = actions;
    
      if (node.internal.type === `MarkdownRemark`) {
        const slug = path.basename(node.fileAbsolutePath, '.md');
    
        createNodeField({
          node,
          name: `slug`,
          value: `/${slug}`,
        });
      }
    };

     

    < /src/pages/index.js >

    index.js파일도 링크가 가능하도록 추가 수정해줍니다.

    //~~~
    export default ({ data }) => {
      console.log(data)
      return (
        <Layout>
          <div>
            <h1>
              Amazing Pandas Eating Things
            </h1>
            <h4>{data.allMarkdownRemark.totalCount} Posts</h4>
            {data.allMarkdownRemark.edges.map(({ node }) => (
              <div key={node.id}>
                <h3>
                  <Link to={node.fields.slug}> <!-- 추가 -->
                    {node.frontmatter.title}{" "}
                    <span>
                      — {node.frontmatter.date}
                    </span>
                  </Link> <!-- 추가 -->
                </h3>
                <p>{node.excerpt}</p>
              </div>
            ))}
          </div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query {
        allMarkdownRemark {
          totalCount
          edges {
            node {
              id
              frontmatter {
                title
                date(formatString: "DD MMMM, YYYY")
              }
              fields{ <!-- 여기부터 추가 -->
                slug
              } <!-- 여기까지 -->
              excerpt          
            }
          }
        }
      }
    `

    <결과>

    링크가 가능하도록 변경되었고,,,

     

    클릭하면 개별 페이지 링크로 전환되는 것까지 확인이 됩니다.

    반응형

    댓글