각 페이지에 들어가면 적어뒀던 md 파일 내용이 보이게 해보자
1. post 데이터를 가져와야하는 경로목록을 가져오기
md파일 경로들을 가져와야 하는데 가져올때
getStaticPath를 이용해서 id를 가져온 다음에 아이디를 이용해서 그에 맞는 데이터들을 가져오는 것이다.
먼저, lib/post.ts에 getAllPostIds 함수를 구현하여 Markdown 파일들의 경로를 가져옵니다. 그런 다음, Next.js의 getStaticPaths를 이용해 각 파일에 대한 경로를 사전 생성합니다.
/lib/post.ts
//md파일을 데이터로 바꾸는 함수
import fs from "fs"
import path from "path"
import matter from "gray-matter"
const postsDirectory = path.join(process.cwd(),'posts') // posts 디렉토리 경로 설정
console.log("process.cwd()", process.cwd());
console.log("postsDirectory",postsDirectory);
export function getSortedPostsData(){
const fileNames = fs.readdirSync(postsDirectory)
console.log("fileName",fileNames);
const allPostsData = fileNames.map(fileName => {
const id = fileName.replace(/\.md$/,"")
const fullPath = path.join(postsDirectory,fileName)
const fileContents = fs.readFileSync(fullPath,"utf8")
const matterResult = matter(fileContents)
return{
id,
...(matterResult.data as {date:string; title:string})
}
})
return allPostsData.sort((a,b)=>{
if(a.date < b.date){
return 1
}else{
return -1
}
})
}
⭐⭐ export function getAllPostIds(){
const fileNames = fs.readdirSync(postsDirectory) //이렇게하면 파일네임들이 배열로 들어간다
return fileNames.map(fileName=>{
return {
params:{
id: fileName.replace(/\.md$/, "") // .md 확장자를 제거해 ID로 사용
}
}
})
}
파일들이 이름을 가져와야하는데 fs파일 시스템 모듈을 이용한다.
- process.cwd(): 현재 프로젝트의 루트 디렉토리를 가져옵니다.
- fs.readdirSync: 지정된 경로에 있는 파일 목록을 동기적으로 읽습니다.
- .map 함수: 파일 이름에서 .md 확장자를 제거하고, 경로 파라미터 객체로 반환합니다.
/pages/posts/[id].tsx
getStaticPaths를 통해 생성된 경로 목록을 반환합니다. 이렇게 하면 각 포스트에 대한 페이지가 생성됩니다.
import { GetStaticPaths } from "next"
import { getAllPostIds } from "../../../lib/post"
export default function Post(){
return(
<div>
</div>
)
}
export const getStaticPath:GetStaticPaths=async() =>{
const paths = getAllPostIds() // posts 디렉토리에서 모든 파일 ID 가져오기
// [{params: {id : ' post1 '}
console.log('paths', paths)
return{
paths, // { params: { id: "파일이름" } } 형태의 배열
fallback:false // 정의되지 않은 경로로 접근 시 404 페이지 표시 };
}
}
fallback 옵션
- false: 경로로 정의되지 않은 페이지는 404로 처리됩니다.
- true: 정의되지 않은 경로도 허용되며, fallback 페이지를 보여줍니다.
2. 전달받은 ID를 이용해서 해당 post의 데이터 가져오기
/pages/posts/[id].tsx 에서 getStaticProps와 /lib/post.tsx 파일에서 getPostData를 만들어준다.
/lib/post.tsx
//md파일을 데이터로 바꾸는 함수
import fs from "fs"
import path from "path"
import matter from "gray-matter"
import { remark } from "remark";
import remarkHtml from "remark-html";
const postsDirectory = path.join(process.cwd(),'posts')
console.log("process.cwd()", process.cwd());
console.log("postsDirectory",postsDirectory);
export function getSortedPostsData(){
const fileNames = fs.readdirSync(postsDirectory)
console.log("fileName",fileNames);
const allPostsData = fileNames.map(fileName => {
const id = fileName.replace(/\.md$/,"")
const fullPath = path.join(postsDirectory,fileName) // 파일 경로
const fileContents = fs.readFileSync(fullPath,"utf8") // 파일 내용 읽기
const matterResult = matter(fileContents) // frontmatter 데이터 파싱
return{
id,
...(matterResult.data as {date:string; title:string})
}
})
return allPostsData.sort((a,b)=>{
if(a.date < b.date){
return 1
}else{
return -1
}
})
}
export function getAllPostIds(){
const fileNames = fs.readdirSync(postsDirectory)
return fileNames.map(fileName=>{
return {
params:{
id: fileName.replace(/\.md$/, "")
}
}
})
}
⭐⭐ export async function getPostData(id:string){
const fullPath = path.join(postsDirectory, `${id}.md`)
const fileContents = fs.readFileSync(fullPath,'utf8')
const matterResult = matter(fileContents)
const processedContent = await remark().use(remarkHtml).process(matterResult.content)
const contentHtml = processedContent.toString()
return{
id,contentHtml,...(matterResult.data as {data:string; title:string})
}
}
pages/posts/[id].tsx
getStaticProps를 사용해 getPostData로 가져온 데이터를 페이지에 전달합니다.
import { GetStaticPaths, GetStaticProps } from "next"
import { getAllPostIds, getSortedPostsData } from "../../../lib/post"
export default function Post(){
return(
<div>
</div>
)
}
export const getStaticPath:GetStaticPaths=async() =>{
const paths = getAllPostIds()
console.log('paths', paths)
return{
paths,
fallback:false
}
}
⭐⭐
export const getStaticProps : GetStaticProps = async({params})=> {
console.log('params',params);
const postData = await getPostData(params.id as string) // id를 이용해 데이터 가져오기
return{
props:{
postData // 페이지에 전달할 데이터
}
}
}
3. Markdown 데이터를 HTML로 렌더링하기
Markdown 데이터를 HTML로 변환해 페이지에서 렌더링합니다. HTML을 렌더링하기 위해 dangerouslySetInnerHTML를 사용합니다.
pages/posts/[id].tsx
Markdown 데이터를 받아와 페이지에 표시합니다.
/[id].tsx
export default function Post({ postData}:{
postData : {
title:string
date:string
contentHtml:string
}
}){
return(
<div>
<head>
<title>{postData.title}</title>
</head>
<article>
<h1 className={homeStyles.headingXl}>{postData.title}</h1>
<div className={homeStyles.lightText}>
{postData.date}
</div>
<div dangerouslySetInnerHTML={{__html:postData.contentHtml}}/>
</article>
</div>
)
}
dangerouslySetInnerHTML: HTML 문자열을 DOM에 렌더링할 때 사용하는 React의 메서드입니다. 외부 HTML 데이터를 삽입할 때 반드시 이 메서드를 사용해야 합니다.