프로젝트 중 mdx 파일을 불러와야 하는 상황이 생겨 Next.js (14 버전)에서 어떻게 불러오는지 알아보았다.
MDX in NextJS
MDX 확장자는 마크다운(MD)과 JSX가 결합되어 마크다운 컨텐츠를 리액트 내에서 컴포넌트 형태로 export 하거나, JSX컴포넌트를 MDX파일 내에 import 할 수 있도록 한다. 정적 컨텐츠를 컴포넌트화 시키는 특성 때문에 SSG를 지원하는 프레임워크(Gatsby, Next)에서는 MDX를 위한 플러그인이 잘 지원되고 있다.
먼저 Next.js에서 mdx파일을 불러오기 위해 하위 packages 을 설치해야 한다.
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
설치 후 next.config.js 도 변경한다.
import remarkGfm from "remark-gfm";
import createMDX from "@next/mdx";
/** @type {import('next').NextConfig} */
const nextConfig = {
// Configure `pageExtensions`` to include MDX files
pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
// Optionally, add any other Next.js config below
};
const withMDX = createMDX({
// Add markdown plugins here, as desired
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
});
// Merge MDX config with Next.js config
export default withMDX(nextConfig);
Next.js 의 /app 디렉토리에서 사용하는 방법
- mdx 파일을 원하는 곳에 생성한다. (필자는 프로젝트 루트에 만들었다.)
test.mdx 예시
---
title: 제목1
author: 관리자
category: 공지사항
date: "2023.07.09"
---
## What is Lorem Ipsum?
Lorem Ipsum is dummy text.
- 마크다운 파일을 읽기 위해 gray-matter를 설치한다.
npm i gray-matter
리스트 페이지
- app router에서는 getStaticProps를 사용하지 못하기 때문에 Markdown 파일들이 있는 디렉토리 경로를 찾아 파일들을 불러와 리스트 페이지를 만든다.
import Image from "next/image";
import Link from "next/link";
import path from "path"; // 파일 경로 조작을 위한 Node.js 내장 모듈
import matter from "gray-matter"; // Markdown 파일의 front matter(metadata)를 추출하는 라이브러리
const fs = require("fs"); // 파일 시스템 모듈
export default function Test() {
// 포스트 데이터를 정렬하여 가져오는 함수를 정의합니다.
function getSortedPostsData() {
const postsDirectory = path.join(process.cwd(), "_newsposts"); // Markdown 파일들이 있는 디렉토리 경로
const fileNames = fs.readdirSync(postsDirectory); // 해당 디렉토리 내 파일 목록을 동기적으로 읽어옵니다.
const allPostsData = fileNames.map((fileName) => {
const id = fileName.replace(/\.mdx$/, ""); // 파일 이름에서 확장자를 제거하여 id로 사용합니다.
// Markdown 파일을 읽어옵니다.
const fullPath = path.join(postsDirectory, fileName); // 전체 파일 경로
const fileContents = fs.readFileSync(fullPath, "utf8"); // 파일 내용을 UTF-8 형식으로 읽어옵니다.
const matterResult = matter(fileContents); // gray-matter 라이브러리를 사용하여 front matter를 추출합니다.
return {
id,
...matterResult.data, // front matter 데이터를 반환합니다.
};
});
// 날짜를 기준으로 포스트 데이터를 정렬합니다.
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1; // a의 날짜가 더 작으면 b 앞으로 정렬합니다.
} else {
return -1; // b의 날짜가 더 작으면 a 앞으로 정렬합니다.
}
});
}
// 정렬된 포스트 데이터를 가져옵니다.
const postDatas = getSortedPostsData();
return (
<section className="commu_board w-[600px] border border-black mx-auto mt-10 p-4">
<div>
<ul>
{postDatas.map((data, index) => (
<li key={data.id} className="flex_line border-b border-[#dbdbdb]">
<div className="w-[110px]">{index + 1}</div>
<div className="w-[110px]">{data.date}</div>
<div className="w-[110px]">{data.category}</div>
<Link
href={`/test/${data.id}`}
className="flex-1 text-left py-8 leading-10 font-semibold text-[32px] overflow-hidden text-ellipsis whitespace-nowrap"
>
{data.title}
</Link>
</li>
))}
</ul>
</div>
</section>
);
}
리스트 클릭 시 상세페이지
- 리스트페이지에서 이동하는 href={/test/${data.id}}에 따라 data.id 값을 props로 가져오고 해당파일만 가져온다.
import Link from "next/link";
import path from "path";
import matter from "gray-matter";
const fs = require("fs");
export default function Mdx(props) {
function getSortedPostsData() {
const postsDirectory = path.join(process.cwd(), "_newsposts");
const fileNames = fs
.readdirSync(postsDirectory)
.filter((fileName) => fileName === `${props.params.id}.mdx`);
const allPostsData = fileNames.map((fileName) => {
const id = fileName.replace(/\.mdx$/, "");
const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, "utf8");
const matterResult = matter(fileContents);
return {
id,
content: matterResult.content,
...matterResult.data,
};
});
return allPostsData;
}
const postDatas = getSortedPostsData();
return (
<section className="commu_board w-[600px] mx-auto mt-10 p-4">
<div>
<ul>
{postDatas.map((data, index) => (
<li key={data.id}>
<div className="flex_line border-b border-[#dbdbdb]">
<div className="w-[110px]">{index + 1}</div>
<Link
href={`/test/${data.id}`}
className="flex-1 text-left py-8 leading-10 font-semibold text-[32px] overflow-hidden text-ellipsis whitespace-nowrap"
>
{data.title}
</Link>
<div className="w-[110px]">{data.date}</div>
<div className="w-[110px]">{data.category}</div>
</div>
<div className="m-4 text-center">{data.content}</div>
</li>
))}
</ul>
</div>
</section>
);
}
***** 동적 파일인 [id] 값 가져오기**
Next.js의 app router로 변경되면서 현재 주소를 알려면 “use client” 를 사용해 import { usePathname } from 'next/navigation’ 해야되서 당황했는데 동적 라우팅 파일인 [id]. jsx에서 props를 가져오면 현재 주소 값 및 쿼리 문자열 등의 정보를 얻을 수 있다.
export default function Mdx(props) {
console.log(props)
// 결과 : { params: { id: 'test' }, searchParams: {} }
return <></>
}
'Next.js' 카테고리의 다른 글
next-auth callbacks (session 값 추가) (0) | 2024.09.10 |
---|---|
CSR, SSR, CORS 에러 (2) | 2024.09.02 |
Next.js14 정리(2) (0) | 2024.08.28 |
Next.js 14 정리(1) (0) | 2024.08.28 |
Next Auth(2) (0) | 2024.08.26 |