Gatsby 웹사이트에서 고급 GraphQL 사용
게시 됨: 2022-03-102015년 GraphQL이 출시되기 전에는 REST(Representational State Transfer)가 API와 인터페이스하는 주요 방법이었습니다. 따라서 GraphQL의 도입은 소프트웨어 개발의 주요 변화였습니다.
최신 정적 사이트 생성기인 Gatsby는 GraphQL을 활용하여 데이터를 프레임워크로 가져오고 조작하기 위한 간결한 방법론을 제공합니다. 이 기사에서는 GraphQL과 Gatsby에서 고급 데이터 소싱 및 변환을 구축 및 구현하여 GraphQL을 Gatsby 웹 사이트에 통합하는 방법을 자세히 살펴보겠습니다. 그 결과 출판 회사가 저자의 콘텐츠를 공유하는 데 사용할 수 있는 게시자의 블로그가 만들어졌습니다.
GraphQL이란 무엇입니까?
이름에서 QL 을 사용하는 GraphQL은 소스에서 데이터를 가져오는 방식에 유연성과 효율성을 제공하기 위해 만들어진 도구 세트와 결합된 쿼리 언어 입니다. GraphQL을 사용하면 클라이언트/소비자가 필요한 데이터를 정확히 요청할 수 있습니다. 서버/제공자는 쿼리에 지정된 요구 사항과 일치하는 JSON 응답 서명으로 응답합니다. 이를 통해 데이터 요구 사항을 선언적으로 표현할 수 있습니다.
GraphQL을 사용하는 이유
정적 사이트 생성기인 Gatsby는 정적 파일을 저장하므로 데이터 쿼리가 거의 불가능합니다. 단일 블로그 게시물 페이지처럼 동적이어야 하는 페이지 구성 요소가 종종 있으므로 마크다운 파일에 블로그 게시물을 저장하는 것처럼 소스에서 데이터를 가져와 필요한 형식으로 변환해야 할 필요가 있습니다. 일부 플러그인은 다양한 소스의 데이터를 제공하므로 소스에서 필요한 데이터를 쿼리하고 변환할 수 있습니다.
gatsby.org의 목록에 따르면 GraphQL은 Gatsby에서 다음과 같은 용도로 유용합니다.
- 상용구 제거
- 프론트엔드 복잡성을 쿼리에 푸시
- 현대 애플리케이션의 항상 복잡한 데이터를 위한 완벽한 솔루션 제공
- 마지막으로 코드 팽창을 제거하여 성능을 향상시킵니다.
GraphQL 개념
Gatsby는 널리 사용되는 GraphQL과 동일한 아이디어를 유지합니다. 이러한 개념 중 일부는 다음과 같습니다.
스키마 정의 언어
GraphQL SDL은 GraphQL에 통합된 유형 시스템이며 이를 사용하여 데이터에 대한 새로운 유형을 생성할 수 있습니다.
국가에 대한 유형을 선언할 수 있으며 해당 속성에는 이름, 대륙, 인구, gdp 및 주의 수가 포함될 수 있습니다.
아래 예와 같이 Aleem 이라는 이름으로 새 유형을 만들었습니다. hobbies
는 문자열의 배열로 필수는 아니지만 국가, 결혼 여부, 게시물은 ! 여기에는 게시물이 다른 유형인 Post 를 참조하는 것도 포함됩니다.
type Author { name: String!, hobbies: [String] country: String! married: Boolean! posts: [Post!] } type Post { title: String! body: String! } type Query { author: Author } schema { query: Query }
쿼리
쿼리 를 사용하여 GraphQL 소스에서 데이터를 가져올 수 있습니다.
아래와 같은 데이터 세트를 고려하면
{ data: { author: [ { hobbies: ["travelling", "reading"], married: false, country: "Nigeria", name: "Aleem Isiaka", posts: [ { title: "Learn more about how to improve your Gatsby website", }, { title: "The ultimate guide to GatsbyJS", }, { title: "How to start a blog with only GatsbyJS", }, ], }, ], }, };
데이터에서 국가와 게시물을 가져오는 쿼리를 가질 수 있습니다.
query { authors { country, posts { title } } }
우리가 받게 될 응답은 제목만 있는 블로그 게시물의 JSON 데이터를 포함해야 합니다.
[ { country: “Nigeria”, posts: [{...}, {...}, {...}] }, { country: “Tunisia”, posts: [] }, { title: “Ghana”, posts: []}, ]
쿼리 조건으로 인수를 사용할 수도 있습니다.
query { authors (country: “Nigeria”) { country, posts { title } } }
어떤 반환해야
[ { country: “Nigeria”, posts: [{...}, {...}, {...}] } ]
Post 유형의 게시물과 같이 중첩 필드도 쿼리할 수 있습니다. 제목만 요청할 수 있습니다.
query { authors(country: 'Nigeria') { country, posts { title } } }
그리고 국가를 반환하는 나이지리아 와 일치하는 모든 작성자 유형을 반환해야 하며 제목 필드만 있는 개체가 포함된 게시물 배열을 반환해야 합니다.
GraphQL을 사용한 개츠비
GraphQL이 변환할 수 있는 데이터를 제공하는 서버/서비스의 오버헤드를 피하기 위해 Gatsby는 빌드 시 GraphQL 쿼리를 실행합니다. 데이터는 빌드 프로세스 동안 구성 요소에 제공되어 서버 없이 브라우저 내에서 쉽게 사용할 수 있습니다.
그래도 Gatsby는 브라우저에서 GraphiQL과 같은 다른 GraphQL 클라이언트가 쿼리할 수 있는 서버로 실행할 수 있습니다.
GraphQL과 상호작용하는 Gatsby 방식
Gatsby가 GraphQL과 상호 작용할 수 있는 곳은 gatsby-node.js API 파일과 페이지 구성 요소를 통해 두 곳이 있습니다.
gatsby-node.js
createPage API는 함수에 전달된 첫 번째 인수의 항목의 일부로 graphql
도우미를 받는 함수로 구성할 수 있습니다.
// gatsby-node.js source: https://www.gatsbyjs.org/docs/node-apis/#createPages exports.createPages = async ({ graphql, actions }) => { const result = await graphql(` query loadPagesQuery ($limit: Int!) { allMarkdownRemark(limit: $limit) { edges { node { frontmatter { slug } } } } }`) }
위의 코드에서 Gatsby의 데이터 계층에서 마크다운 파일을 가져오기 위해 GraphQL 도우미를 사용했습니다. 그리고 이것을 주입하여 페이지를 만들고 Gatsby 데이터 계층 내부의 기존 데이터를 수정할 수 있습니다.
페이지 구성 요소
/pages 디렉토리 내부의 페이지 구성 요소 또는 createPage
API 작업에 의해 렌더링된 템플릿은 gatsby
모듈에서 graphql
를 내보낼 수 pageQuery
. 차례로 Gatsby는 해결된 데이터를 포함하는 페이지 구성 요소의 props에 새 prop data
를 주입합니다.
import React from "react"; import { graphql } from "gatsby"; const Page = props => { return
{JSON.stringify(props.data)}; }; 내보내기 const pageQuery = graphql` 쿼리 { ... } `; 기본 페이지 내보내기;
다른 구성 요소에서
다른 구성 요소는 gatsby
모듈에서 graphql
및 StaticQuery
구성 요소를 가져오고 Graphql 도우미를 구현하는 쿼리 소품을 전달하는 <StaticQuery/>
를 렌더링하고 반환된 데이터를 가져오기 위해 렌더링할 수 있습니다.
import React from "react"; import { StaticQuery, graphql } from "gatsby"; const Brand = props => { return ( <div> <h1>{data.site.siteMetadata.title}</h1> </div> ); }; const Navbar = props => { return ( <StaticQuery query={graphql` query { site { siteMetadata { title } } } `} render={data => <Brand data={data} {...props} />} /> ); }; export default Navbar;
현대적이고 고급스러운 Gatsby 출판 블로그 구축
이 섹션에서는 태그 지정, 분류, 페이지 매김 및 작성자별 기사 그룹화를 지원하는 블로그를 만드는 과정을 살펴보겠습니다. 우리는 Gatsby 생태계의 플러그인을 사용하여 몇 가지 기능을 가져오고 GraphQL 쿼리의 논리를 사용하여 여러 저자 출판을 위한 게시자 블로그를 만들 것입니다.
우리가 만들 블로그의 최종 버전은 여기에서 찾을 수 있으며 코드는 Github에서 호스팅됩니다.
프로젝트 초기화
다른 Gatsby 웹 사이트와 마찬가지로 스타터에서 초기화합니다. 여기서는 고급 스타터를 사용하지만 사용 사례에 맞게 수정했습니다.
먼저 이 Github 리포지토리를 복제하고 작업 분기를 dev-init로 변경한 다음 프로젝트 폴더에서 npm run develop
development를 실행하여 개발 서버를 시작하여 https://localhost:8000에서 사이트를 사용할 수 있도록 합니다.
git clone [email protected]:limistah/modern-gatsby-starter.git cd modern-gatsby-starter git checkout dev-init npm install npm run develop
https://localhost:8000을 방문하면 이 분기의 기본 홈페이지가 표시됩니다.
블로그 게시물 콘텐츠 만들기
프로젝트 저장소에 포함된 일부 게시물 콘텐츠는 dev-blog-content 분기에서 액세스할 수 있습니다. 콘텐츠 디렉토리의 구성은 /content/YYYY_MM/DD.md
와 같으며 생성된 월별로 게시물을 그룹화합니다.
블로그 게시물 콘텐츠에는 title
, date
, author
, category
, tags
가 전면 내용으로 포함되며, 이를 통해 게시물을 구별하고 추가 처리를 수행하고 나머지 콘텐츠는 게시물 본문입니다.
title: "Bold Mage" date: "2020-07-12" author: "Tunde Isiaka" category: "tech" tags: - programming - stuff - Ice cream - other --- # Donut I love macaroon chocolate bar Oat cake marshmallow lollipop fruitcake I love jelly-o. Gummi bears cake wafer chocolate bar pie. Marshmallow pastry powder chocolate cake candy chupa chups. Jelly beans powder souffle biscuit pie macaroon chocolate cake. Marzipan lemon drops chupa chups sweet cookie sesame snaps jelly halvah.
게시물 콘텐츠 표시
Markdown 게시물을 HTML로 렌더링하기 전에 일부 처리를 수행해야 합니다. 먼저 파일을 Gatsby 저장소에 로드하고 MD를 HTML로 구문 분석하고 이미지 종속성을 연결하는 등의 작업을 수행합니다. 이를 용이하게 하기 위해 우리는 Gatsby 생태계의 많은 플러그인을 사용할 것입니다.
프로젝트 루트에서 gatsby-config.js를 다음과 같이 업데이트하여 이러한 플러그인을 사용할 수 있습니다.
module.exports = { siteMetadata: {}, plugins: [ { resolve: "gatsby-source-filesystem", options: { name: "assets", path: `${__dirname}/static/`, }, }, { resolve: "gatsby-source-filesystem", options: { name: "posts", path: `${__dirname}/content/`, }, }, { resolve: "gatsby-transformer-remark", options: { plugins: [ { resolve: `gatsby-remark-relative-images`, }, { resolve: "gatsby-remark-images", options: { maxWidth: 690, }, }, { resolve: "gatsby-remark-responsive-iframe", }, "gatsby-remark-copy-linked-files", "gatsby-remark-autolink-headers", "gatsby-remark-prismjs", ], }, }, ], };
우리는 일부 작업을 수행하는 데 도움이 되는 플러그인을 포함하도록 gatsby에 지시했습니다. 특히 정적 파일의 경우 /static 폴더에서 파일을 가져오고 블로그 게시물의 경우 /content를 가져옵니다. 또한 .md 또는 .markdown으로 끝나는 모든 파일을 마크다운을 HTML로 렌더링하기 위한 모든 주석 필드가 있는 노드로 변환하는 주석 변환기 플러그인을 포함했습니다.
마지막으로 gatsby-transformer-remark
에 의해 생성된 노드에서 작동하는 플러그인을 포함했습니다.
gatsby-config.js
API 파일 구현
앞으로 프로젝트 루트의 gatsby-node.js 내부에서 createPage
라는 함수를 내보내고 graphQL 도우미를 사용하여 GatsbyJS의 콘텐츠 계층에서 노드를 가져오는 함수의 콘텐츠를 가질 수 있습니다.
이 페이지에 대한 첫 번째 업데이트에는 MarkDown 발언 노드에 슬러그 세트가 있는지 확인하는 것이 포함됩니다. onCreateNode API를 수신하고 그에 따라 슬러그와 날짜를 포함하도록 노드를 업데이트하기 전에 노드가 MarkdownRemark 유형인지 확인하기 위해 생성된 노드를 가져옵니다.
const path = require("path"); const _ = require("lodash"); const moment = require("moment"); const config = require("./config"); // Called each time a new node is created exports.onCreateNode = ({ node, actions, getNode }) => { // A Gatsby API action to add a new field to a node const { createNodeField } = actions; // The field that would be included let slug; // The currently created node is a MarkdownRemark type if (node.internal.type === "MarkdownRemark") { // Recall, we are using gatsby-source-filesystem? // This pulls the parent(File) node, // instead of the current MarkdownRemark node const fileNode = getNode(node.parent); const parsedFilePath = path.parse(fileNode.relativePath); if ( Object.prototype.hasOwnProperty.call(node, "frontmatter") && Object.prototype.hasOwnProperty.call(node.frontmatter, "title") ) { // The node is a valid remark type and has a title, // Use the title as the slug for the node. slug = `/${_.kebabCase(node.frontmatter.title)}`; } else if (parsedFilePath.name !== "index" && parsedFilePath.dir !== "") { // File is in a directory and the name is not index // eg content/2020_02/learner/post.md slug = `/${parsedFilePath.dir}/${parsedFilePath.name}/`; } else if (parsedFilePath.dir === "") { // File is not in a subdirectory slug = `/${parsedFilePath.name}/`; } else { // File is in a subdirectory, and name of the file is index // eg content/2020_02/learner/index.md slug = `/${parsedFilePath.dir}/`; } if (Object.prototype.hasOwnProperty.call(node, "frontmatter")) { if (Object.prototype.hasOwnProperty.call(node.frontmatter, "slug")) slug = `/${_.kebabCase(node.frontmatter.slug)}`; if (Object.prototype.hasOwnProperty.call(node.frontmatter, "date")) { const date = moment(new Date(node.frontmatter.date), "DD/MM/YYYY"); if (!date.isValid) console.warn(`WARNING: Invalid date.`, node.frontmatter); // MarkdownRemark does not include date by default createNodeField({ node, name: "date", value: date.toISOString() }); } } createNodeField({ node, name: "slug", value: slug }); } };
게시물 목록
이 시점에서 createPages
API를 구현하여 모든 마크다운을 쿼리하고 위에서 만든 슬러그 경로를 사용하여 페이지를 만들 수 있습니다. Github에서 확인하세요.
//gatsby-node.js // previous code // Create Pages Programatically! exports.createPages = async ({ graphql, actions }) => { // Pulls the createPage action from the Actions API const { createPage } = actions; // Template to use to render the post converted HTML const postPage = path.resolve("./src/templates/singlePost/index.js"); // Get all the markdown parsed through the help of gatsby-source-filesystem and gatsby-transformer-remark const allMarkdownResult = await graphql(` { allMarkdownRemark { edges { node { fields { slug } frontmatter { title tags category date author } } } } } `); // Throws if any error occur while fetching the markdown files if (allMarkdownResult.errors) { console.error(allMarkdownResult.errors); throw allMarkdownResult.errors; } // Items/Details are stored inside of edges const postsEdges = allMarkdownResult.data.allMarkdownRemark.edges; // Sort posts postsEdges.sort((postA, postB) => { const dateA = moment( postA.node.frontmatter.date, siteConfig.dateFromFormat ); const dateB = moment( postB.node.frontmatter.date, siteConfig.dateFromFormat ); if (dateA.isBefore(dateB)) return 1; if (dateB.isBefore(dateA)) return -1; return 0; }); // Pagination Support for posts const paginatedListingTemplate = path.resolve( "./src/templates/paginatedListing/index.js" ); const { postsPerPage } = config; if (postsPerPage) { // Get the number of pages that can be accommodated const pageCount = Math.ceil(postsEdges.length / postsPerPage); // Creates an empty array Array.from({ length: pageCount }).forEach((__value__, index) => { const pageNumber = index + 1; createPage({ path: index === 0 ? `/posts` : `/posts/${pageNumber}/`, component: paginatedListingTemplate, context: { limit: postsPerPage, skip: index * postsPerPage, pageCount, currentPageNumber: pageNumber, }, }); }); } else { // Load the landing page instead createPage({ path: `/`, component: landingPage, }); } };
createPages
함수에서 Gatsby에서 제공하는 graphql
도우미를 사용하여 콘텐츠 계층에서 데이터를 쿼리합니다. 이를 위해 표준 Graphql 쿼리를 사용하고 allMarkdownRemark
유형에서 콘텐츠를 가져오는 쿼리를 전달했습니다. 그런 다음 작성된 날짜별로 게시물을 정렬하기 위해 앞으로 이동했습니다.
그런 다음 가져온 구성 개체에서 postPerPage
속성을 가져왔습니다. 이 속성은 단일 페이지에 대해 총 게시물을 지정된 수의 게시물로 청크다운하는 데 사용됩니다.
페이지 매김을 지원하는 목록 페이지를 만들려면 목록을 렌더링할 구성 요소로 건너뛸 제한, pageNumber 및 페이지 수를 전달해야 합니다. createPage
구성 개체의 컨텍스트 속성을 사용하여 이를 수행합니다. 한도 내에서 게시물을 가져오기 위해 또 다른 graphql 쿼리를 만들기 위해 페이지에서 이러한 속성에 액세스할 것입니다.
또한 목록에 대해 동일한 템플릿 구성 요소를 사용하고 앞서 정의한 청크 배열의 인덱스를 사용하여 경로만 변경되었음을 알 수 있습니다. Gatsby는 /{chunkIndex}
와 일치하는 주어진 URL에 필요한 데이터를 전달하므로 처음 10개의 게시물에 대해 /
를, 다음 10개의 게시물에 대해 /2
를 가질 수 있습니다.
렌더링 게시물 목록
이 페이지를 렌더링하는 구성 요소는 프로젝트 폴더의 src/templates/singlePost/index.js
에서 찾을 수 있습니다. 또한 현재 페이지 범위 내의 게시물에 대해 gatsby를 쿼리하기 위해 createPages 프로세스에서 받은 제한 및 페이지 쿼리 매개변수를 가져오는 graphql
도우미를 내보냅니다.
import React from "react"; import { graphql, Link } from "gatsby"; import Layout from "../../layout"; import PostListing from "../../components/PostListing"; import "./index.css"; const Pagination = ({ currentPageNum, pageCount }) => { const prevPage = currentPageNum - 1 === 1 ? "/" : `/${currentPageNum - 1}/`; const nextPage = `/${currentPageNum + 1}/`; const isFirstPage = currentPageNum === 1; const isLastPage = currentPageNum === pageCount; return ( <div className="paging-container"> {!isFirstPage && <Link to={prevPage}>Previous</Link>} {[...Array(pageCount)].map((_val, index) => { const pageNum = index + 1; return ( <Link key={`listing-page-${pageNum}`} to={pageNum === 1 ? "/" : `/${pageNum}/`} > {pageNum} </Link> ); })} {!isLastPage && <Link to={nextPage}>Next</Link>} </div> ); }; export default (props) => { const { data, pageContext } = props; const postEdges = data.allMarkdownRemark.edges; const { currentPageNum, pageCount } = pageContext; return ( <Layout> <div className="listing-container"> <div className="posts-container"> <PostListing postEdges={postEdges} /> </div> <Pagination pageCount={pageCount} currentPageNum={currentPageNum} /> </div> </Layout> ); }; /* eslint no-undef: "off" */ export const pageQuery = graphql` query ListingQuery($skip: Int!, $limit: Int!) { allMarkdownRemark( sort: { fields: [fields___date], order: DESC } limit: $limit skip: $skip ) { edges { node { fields { slug date } excerpt timeToRead frontmatter { title tags author category date } } } } } `;
게시물 페이지
페이지의 내용을 보려면 gatsby-node.js
API 파일 내부에 프로그래밍 방식으로 페이지를 생성해야 합니다. 먼저 콘텐츠를 렌더링할 새 구성 요소를 정의해야 합니다. 이를 위해 src/templates/singlePost/index.jsx
가 있습니다.
import React from "react"; import { graphql, Link } from "gatsby"; import _ from "lodash"; import Layout from "../../layout"; import "./b16-tomorrow-dark.css"; import "./index.css"; import PostTags from "../../components/PostTags"; export default class PostTemplate extends React.Component { render() { const { data, pageContext } = this.props; const { slug } = pageContext; const postNode = data.markdownRemark; const post = postNode.frontmatter; if (!post.id) { post.id = slug; } return ( <Layout> <div> <div> <h1>{post.title}</h1> <div className="category"> Posted to{" "} <em> <Link key={post.category} style={{ textDecoration: "none" }} to={`/category/${_.kebabCase(post.category)}`} > <a>{post.category}</a> </Link> </em> </div> <PostTags tags={post.tags} /> <div dangerouslySetInnerHTML={{ __html: postNode.html }} /> </div> </div> </Layout> ); } } /* eslint no-undef: "off" */ export const pageQuery = graphql` query BlogPostBySlug($slug: String!) { markdownRemark(fields: { slug: { eq: $slug } }) { html timeToRead excerpt frontmatter { title date category tags } fields { slug date } } } `;
다시, 우리는 createPages API를 통해 페이지로 전송될 슬러그 쿼리에 의해 페이지를 가져오기 위해 graphQL 도우미를 사용하고 있습니다.
다음으로 createPages
API 함수 끝에서 gatsby-node.js에 아래 코드를 추가해야 합니다.
// Template to use to render the post converted HTML const postPage = path.resolve("./src/templates/singlePost/index.jsx"); // Loops through all the post nodes postsEdges.forEach((edge, index) => { // Create post pages createPage({ path: edge.node.fields.slug, component: postPage, context: { slug: edge.node.fields.slug, }, }); });
그리고 '/{pageSlug}'를 방문하여 해당 페이지에 대한 마크다운 파일의 콘텐츠를 HTML로 렌더링하도록 할 수 있습니다. 예를 들어 https://localhost:8000/the-butterfly-of-the-edge 는 모든 유효한 슬러그와 유사하게 content/2020_05/01.md
에서 마크다운에 대해 변환된 HTML을 로드해야 합니다. 엄청난!
카테고리 및 태그 렌더링
단일 게시물 템플릿 구성요소에는 유사한 카테고리의 게시물을 나열하기 위해 /categories/{categoryName}
형식의 페이지에 대한 링크가 있습니다.
gatsby-node.js
파일에 단일 게시물 페이지를 구축할 때 모든 카테고리와 태그를 먼저 포착한 다음 카테고리/태그 이름을 전달하는 포착된 각 카테고리/태그에 대한 페이지를 생성할 수 있습니다.
gatsby-node.js에서 단일 게시물 페이지 생성 섹션을 수정하면 다음과 같습니다.
const categorySet = new Set(); const tagSet = new Set(); const categoriesListing = path.resolve( "./src/templates/categoriesListing/index.jsx" ); // Template to use to render posts based on categories const tagsListingPage = path.resolve("./src/templates/tagsListing/index.jsx"); // Loops through all the post nodes postsEdges.forEach((edge, index) => { // Generate a list of categories if (edge.node.frontmatter.category) { categorySet.add(edge.node.frontmatter.category); } // Generate a list of tags if (edge.node.frontmatter.tags) { edge.node.frontmatter.tags.forEach((tag) => { tagSet.add(tag); }); } // Create post pages createPage({ path: edge.node.fields.slug, component: postPage, context: { slug: edge.node.fields.slug, }, }); });
그리고 태그별로 게시물을 나열하기 위한 구성요소 내에서 pageQuery가 태그 목록에 해당 태그를 포함하여 게시물에 대한 쿼리 pageQuery
을 내보내도록 할 수 있습니다. 이를 달성하기 위해 graphql의 filter
기능과 $in 연산자를 사용할 것입니다.
// src/templates/tagsListing/ import React from "react"; import { graphql } from "gatsby"; import Layout from "../../layout"; import PostListing from "../../components/PostListing"; export default ({ pageContext, data }) => { const { tag } = pageContext; const postEdges = data.allMarkdownRemark.edges; return ( <Layout> <div className="tag-container"> <div>Posts posted with {tag}</div> <PostListing postEdges={postEdges} /> </div> </Layout> ); }; /* eslint no-undef: "off" */ export const pageQuery = graphql` query TagPage($tag: String) { allMarkdownRemark( limit: 1000 sort: { fields: [fields___date], order: DESC } filter: { frontmatter: { tags: { in: [$tag] } } } ) { totalCount edges { node { fields { slug date } excerpt timeToRead frontmatter { title tags author date } } } } } `;
그리고 우리는 카테고리 목록 구성 요소에서 동일한 프로세스를 가지고 있으며 차이점은 카테고리가 우리가 전달하는 것과 정확히 일치하는 위치만 찾기만 하면 된다는 것입니다.
// src/templates/categoriesListing/index.jsx import React from "react"; import { graphql } from "gatsby"; import Layout from "../../layout"; import PostListing from "../../components/PostListing"; export default ({ pageContext, data }) => { const { category } = pageContext; const postEdges = data.allMarkdownRemark.edges; return ( <Layout> <div className="category-container"> <div>Posts posted to {category}</div> <PostListing postEdges={postEdges} /> </div> </Layout> ); }; /* eslint no-undef: "off" */ export const pageQuery = graphql` query CategoryPage($category: String) { allMarkdownRemark( limit: 1000 sort: { fields: [fields___date], order: DESC } filter: { frontmatter: { category: { eq: $category } } } ) { totalCount edges { node { fields { slug date } excerpt timeToRead frontmatter { title tags author date } } } } } `;
주목할만한 점은 태그 및 카테고리 구성 요소 내에서 게시물 콘텐츠를 더 자세히 읽을 수 있도록 단일 게시물 페이지에 대한 링크를 렌더링한다는 것입니다.
작성자 지원 추가
여러 작성자를 지원하려면 게시물 콘텐츠를 약간 수정하고 새로운 개념을 도입해야 합니다.
JSON 파일 로드
먼저 작성자의 콘텐츠를 다음과 같이 JSON 파일에 저장할 수 있어야 합니다.
{ "mdField": "aleem", "name": "Aleem Isiaka", "email": "[email protected]", "location": "Lagos, Nigeria", "avatar": "https://api.adorable.io/avatars/55/[email protected]", "description": "Yeah, I like animals better than people sometimes... Especially dogs. Dogs are the best. Every time you come home, they act like they haven't seen you in a year. And the good thing about dogs... is they got different dogs for different people.", "userLinks": [ { "label": "GitHub", "url": "https://github.com/limistah/modern-gatsby-starter", "iconClassName": "fa fa-github" }, { "label": "Twitter", "url": "https://twitter.com/limistah", "iconClassName": "fa fa-twitter" }, { "label": "Email", "url": "mailto:[email protected]", "iconClassName": "fa fa-envelope" } ] }
우리는 그것들을 /authors
로 프로젝트 루트에 있는 작성자의 디렉토리에 저장할 것입니다. 작성자 JSON에는 마크다운 블로그 콘텐츠에 도입할 작성자 필드의 고유 식별자인 mdField
가 있습니다. 이렇게 하면 작성자가 여러 프로필을 가질 수 있습니다.
다음으로, gatsby-source-filesystem
이 authors/
디렉토리에서 파일 노드로 콘텐츠를 로드하도록 지시하는 gatsby gatsby-config.js
플러그인을 업데이트해야 합니다.
// gatsby-config.js { resolve: `gatsby-source-filesystem`, options: { name: "authors", path: `${__dirname}/authors/`, }, }
마지막으로 gatsby-transform-json
을 설치하여 다루기 쉽고 적절한 처리를 위해 생성된 JSON 파일을 변환합니다.
npm install gatsby-transformer-json --save
그리고 gatsby-config.js
플러그인 안에 포함시키세요.
module.exports = { plugins: [ // ...other plugins `gatsby-transformer-json` ], };
작성자 페이지 조회 및 생성
시작하려면 데이터 레이어에 로드된 gatsby-config.js
내부의 authors/
디렉토리에 있는 모든 작성자를 쿼리해야 합니다. createPages
API 함수에 아래 코드를 추가해야 합니다.
const authorsListingPage = path.resolve( "./src/templates/authorsListing/index.jsx" ); const allAuthorsJson = await graphql(` { allAuthorsJson { edges { node { id avatar mdField location name email description userLinks { iconClassName label url } } } } } `); const authorsEdges = allAuthorsJson.data.allAuthorsJson.edges; authorsEdges.forEach((author) => { createPage({ path: `/authors/${_.kebabCase(author.node.mdField)}/`, component: authorsListingPage, context: { authorMdField: author.node.mdField, authorDetails: author.node, }, }); });
이 스니펫에서는 allAuthorsJson 유형에서 모든 작성자를 가져온 다음 노드에서 forEach를 호출하여 작성자를 구별하기 위해 mdField
를 전달하고 작성자에 대한 전체 정보를 보려면 authorDetails
를 전달하는 페이지를 만듭니다.
작성자 게시물 렌더링
src/templates/authorsListing/index.jsx
에서 찾을 수 있는 페이지를 렌더링하는 구성 요소에서 파일에 대한 아래 내용이 있습니다.
import React from "react"; import { graphql } from "gatsby"; import Layout from "../../layout"; import PostListing from "../../components/PostListing"; import AuthorInfo from "../../components/AuthorInfo"; export default ({ pageContext, data }) => { const { authorDetails } = pageContext; const postEdges = data.allMarkdownRemark.edges; return ( <Layout> <div> <h1 style={{ textAlign: "center" }}>Author Roll</h1> <div className="category-container"> <AuthorInfo author={authorDetails} /> <PostListing postEdges={postEdges} /> </div> </div> </Layout> ); }; /* eslint no-undef: "off" */ export const pageQuery = graphql` query AuthorPage($authorMdField: String) { allMarkdownRemark( limit: 1000 sort: { fields: [fields___date], order: DESC } filter: { frontmatter: { author: { eq: $authorMdField } } } ) { totalCount edges { node { fields { slug date } excerpt timeToRead frontmatter { title tags author date } } } } } `;
위의 코드에서 우리는 작성자와 일치하는 게시물을 가져오는 GraphQL 쿼리를 만들기 위해 pageQuery
를 내보냈습니다. 이를 달성하기 위해 $eq
연산자를 사용하여 추가 읽기를 위한 단일 게시물 페이지에 대한 링크를 생성하고 있습니다.
결론
Gatsby에서 GraphQL 쿼리를 사용하여 데이터 액세스 계층 내부에 존재하는 모든 데이터를 쿼리하고 Gatsby 아키텍처에서 정의한 일부 구성을 사용하여 변수를 전달할 수 있습니다. 우리는 다양한 장소에서 graphql
도우미를 사용하고 GraphQL의 도움으로 Gatsby의 웹사이트에서 데이터를 쿼리하기 위해 널리 사용되는 패턴을 이해하는 방법을 보았습니다.
GraphQL은 매우 강력하며 서버에서 데이터 변형과 같은 다른 작업을 수행할 수 있습니다. Gatsby는 런타임에 데이터를 업데이트할 필요가 없으므로 GraphQL의 변형 기능을 지원하지 않습니다.
GraphQL은 훌륭한 기술이며 Gatsby는 프레임워크에서 사용하는 것을 매우 흥미롭게 만듭니다.
참고문헌
- GraphQL에 대한 Gatsby 지원
- Gatsby가 GraphQL을 사용하는 이유
- Gatsby의 GraphQL 개념
- 어떻게 GraphQL: 기본 개념
- GraphQL의 스키마 정의 언어
- GraphQL 소개
- 개츠비 고급 스타터