20 포스트 아이템 링크를 만들어 해당 게시글 페이지 연결하기 - 포스트 아이템 링크 연결 및 게시글 페이지 생성하기
source: categories/study/gatsby/gatsby_9-11.md
slug
Slug 데이터로 포스트 아이템 링크 연결하기
저번 챕터에서는 각 마크다운 데이터마다 파일 경로 및 이름을 통해 Slug 데이터를 새로 만들어주는 기능을 구현했습니다.
따라서 이번 챕터 해당 부분에서는 새로 생성한 Slug 데이터를 통해 각 포스트 아이템에 링크를 연결해주도록 하겠습니다.
먼저, 이를 위해 메인 페이지 컴포넌트에서 Slug 데이터를 추가로 Query 해야 하므로 다음과 같이 코드를 변경해주세요.
src/pages/index.tsx
// ...
export const getPostList = graphql`
query getPostList {
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date, frontmatter___title] }
) {
edges {
node {
id
fields {
slug
}
frontmatter {
title
summary
date(formatString: "YYYY.MM.DD.")
categories
thumbnail {
childImageSharp {
gatsbyImageData(width: 768, height: 400)
}
}
}
}
}
}
file(name: { eq: "profile-image" }) {
childImageSharp {
gatsbyImageData(width: 120, height: 120)
}
}
}
`
그리고 받는 데이터의 형식에 따라서 PostItem.types.ts
파일에 존재하는 PostListItemType
타입의 구조도 아래와 같이 변경해주어야 합니다.
src/types/PostItem.types.ts
// ...
export type PostListItemType = {
node: {
id: string
fields: {
slug: string
}
frontmatter: PostFrontmatterType
}
}
그럼 컴포넌트 부분은 어떻게 변경해야할까요?
기존에는 저희가 임의적으로 URL을 넘겨 해당 페이지로 이동하게 했지만, 여기에서는 Slug 데이터를 그대로 넘기면 됩니다.
따라서 다음과 같이 코드를 수정해주어야 합니다.
src/components/Main/PostList.tsx
// ...
const PostList: FunctionComponent<PostListProps> = function ({
selectedCategory,
posts,
}) {
const { containerRef, postList }: useInfiniteScrollType = useInfiniteScroll(
selectedCategory,
posts,
)
return (
<PostListWrapper ref={containerRef}>
{postList.map(
({
node: {
id,
fields: { slug },
frontmatter,
},
}: PostListItemType) => (
<PostItem {...frontmatter} link={slug} key={id} />
),
)}
</PostListWrapper>
)
}
export default PostList
여기까지 한 상태에서 로컬 서버를 실행하고, 아무 포스트 아이템이나 클릭해보세요.
아직 해당 URL에 대한 페이지가 만들어지지 않아 404 오류가 뜨겠지만, URL이 의도한대로 변경되면 올바르게 코드를 작성한 것입니다.
게시글 페이지 템플릿 생성하기
모든 게시글 페이지는 레이아웃이 동일한 형태로 이루어져 있습니다.
그러기 때문에 Next.js 프레임워크와 같은 경우에는 pages 디렉토리 내에 [slug].js
의 형태로 파일을 생성해 동일한 컴포넌트를 사용할 수 있지만, Gatsby에는 단순 정적 사이트 생성 기능만 있기 때문에 저런 방식으로 구현할 수 없습니다.
따라서 gatsby-node.js
에서 이를 위한 API를 사용하여 페이지를 만들어주는 기능을 구현해야하는데, 이를 위해서는 공통적으로 사용되는 템플릿 컴포넌트가 필요합니다.
그러므로 해당 부분에서는 게시글 페이지에 사용될 템플릿 컴포넌트를 만들어보겠습니다.
이와 같은 부류의 컴포넌트는 하나의 페이지에서만 사용되는 것이 아니기 때문에 pages 디렉토리에 저장하면 의도한대로 개발이 불가능합니다.
이에 따라 templates 디렉토리에 템플릿 컴포넌트를 저장해주겠습니다.
src/templates/post_template.tsx
import React, { FunctionComponent } from 'react'
type PostTemplateProps = {}
const PostTemplate: FunctionComponent<PostTemplateProps> = function (props) {
console.log(props)
return <div>Post Template</div>
}
export default PostTemplate
아직 저희는 어떤 형식의 Props를 받고, 사용할지 모르기 때문에 그에 대한 타입은 나중에 작성하겠습니다.
템플릿 컴포넌트로 게시글 페이지 생성하기
이제 위에서 만든 템플릿 컴포넌트를 통해 gatsby-node.js
파일 내에서 게시글 페이지를 생성해주는 부분을 구현해보겠습니다.
이를 위해서는 Gatsby에서 제공해주는 createPages API를 사용해야 합니다.
먼저 다음과 같이 코드를 추가해주세요.
gatsby-node.js
// ...
// Generate Post Page Through Markdown Data
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions;
// Get All Markdown File For Paging
const queryAllMarkdownData = await graphql(
`
{
allMarkdownRemark(
sort: {
order: DESC
fields: [frontmatter___date, frontmatter___title]
}
) {
edges {
node {
fields {
slug
}
}
}
}
}
`,
);
// Handling GraphQL Query Error
if (queryAllMarkdownData.errors) {
reporter.panicOnBuild(`Error while running query`);
return;
}
};
가장 먼저 게시글 페이지를 생성해주기 위해 모든 마크다운 데이터의 Slug 필드를 조회하는 Query 코드를 작성한 후, 해당 Query의 에러 핸들링 부분을 작성해주었습니다.
그리고, 메인 페이지에서 마크다운 데이터를 불러올 때처럼 날짜와 제목을 기준으로 내림차순 정렬한 데이터를 받도록 작성했습니다.
이제 템플릿 컴포넌트를 불러오고, 받은 데이터를 통해 페이지를 생성해주는 부분을 작성하겠습니다.
아래와 같이 코드를 수정해주세요.
gatsby-node.js
// Generate Post Page Through Markdown Data
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions;
// Get All Markdown File For Paging
const queryAllMarkdownData = await graphql(
`
{
allMarkdownRemark(
sort: {
order: DESC
fields: [frontmatter___date, frontmatter___title]
}
) {
edges {
node {
fields {
slug
}
}
}
}
}
`,
);
// Handling GraphQL Query Error
if (queryAllMarkdownData.errors) {
reporter.panicOnBuild(`Error while running query`);
return;
}
// Import Post Template Component
const PostTemplateComponent = path.resolve(
__dirname,
'src/templates/post_template.tsx',
);
// Page Generating Function
const generatePostPage = ({
node: {
fields: { slug },
},
}) => {
const pageOptions = {
path: slug,
component: PostTemplateComponent,
context: { slug },
};
createPage(pageOptions);
};
// Generate Post Page And Passing Slug Props for Query
queryAllMarkdownData.data.allMarkdownRemark.edges.forEach(generatePostPage);
};
path 라이브러리를 통해 템플릿 컴포넌트를 불러온 후, slug 데이터를 통해 페이지를 생성해주는 함수인 generatePostPage
함수를 작성해주었습니다.
해당 함수에서는 Gatsby API인 createPage 함수가 사용되었는데, pageOptions 객체의 형식으로 인자를 받습니다.
경로는 slug 데이터를 그대로 넘겨주었고, 해당 페이지에서 사용할 컴포넌트로 위에서 불러온 템플릿 컴포넌트를 전달해주었습니다.
마지막으로 context라는 이름으로 slug를 넣어주었는데, 이 데이터는 템플릿 컴포넌트에서 Props로 받을 수 있을 뿐더러 해당 컴포넌트에서 사용할 GraphQL Query의 파라미터로도 받을 수 있기 때문에 이 데이터를 통해 Slug에 맞는 마크다운 데이터를 불러올 것입니다.
그리고 Query를 통해 불러온 데이터 모두 위에서 정의한 페이지 생성 함수를 통해 게시글 페이지를 생성해주었습니다.
이제 로컬 서버를 실행해 메인 페이지에서 아무 포스트 아이템이나 눌러 404 에러가 더 이상 안 뜨는지, 콘솔창에 올바르게 Props가 출력되는지 확인해보세요.
단, 로컬 서버를 실행한 상태에서 gatsby-node.js
파일 코드를 수정했다면 로컬 서버를 재시작해야 결과가 반영됩니다.