2024년 12월 05일
MDX 파일을 활용하여 블로그 게시글을 작성하고 이를 렌더링해보자.
임시로 띄워 볼 mdx 파일을 작성한다. ---는 mdx 파일에서 메타데이터와 본문을 분리해서 사용할 수 있도록 도와주는 역할을 한다.
---
title: '임시게시글'
date: 2024-12-05
desc: 임시게시글입니다아아아아아ㅏ
---
### 제목
이 글은 임시게시글 입니다.
MDX 파일의 메타데이터와 본문을 추출하기 위해 gray-matter 라이브러리를 사용한다.
yarn add --save gray-matter
gray-matter는 마크다운 또는 MDX 파일에서 프론트매터(front matter)를 분석하여 메타데이터와 본문을 분리하는 라이브러리이다. 프론트매터는 ---로 감싸진 YAML 형식의 데이터로, 일반적으로 title, date, tags 같은 정보를 포함한다.
이를 활용해 아래처럼 MDX 파일을 읽고 파싱하는 getPostData 함수를 작성하였다. gray-matter의 matter를 사용하면 mdx 파일에 씌여진 프론트매터를 통해 (meta)data와 content를 구분하여 받아올 수 있다.
import fs from 'fs';
import matter from 'gray-matter';
import path from 'path';
const postsDirectory = path.join(process.cwd(), 'src/posts/blog');
export function getPostData(filename: string) {
const filePath = path.join(postsDirectory, `${filename}.mdx`);
const fileContents = fs.readFileSync(filePath, 'utf8');
// gray-matter로 메타데이터와 본문(content)을 파싱
const { data, content } = matter(fileContents);
return {
meta: data, // 메타데이터 (title, date 등)
content, // MDX 본문
};
}
위에서 작성한 getPostData 함수를 이용해 데이터를 가져오고, 메타데이터와 본문을 렌더링하는 컴포넌트를 작성한다. 우선 파싱이 잘 되었나만 확인해 보기 위해 간단하게 title, date, content만 띄우도록 하였다.
import { getPostData } from '../lib/posts';
const MainPage = () => {
const { meta, content } = getPostData('setup');
return (
<div>
<div className="prose mx-auto p-4">
<h1>{meta.title}</h1>
<p>{meta.date}</p>
<div>{content}</div>
</div>
</div>
);
};
export default MainPage;
setup이라는 파일 하나만 파싱한다고 가정하고, 이를 메인페이지에서 띄워 확인해보려 했더니 다음과 같은 에러가 발생했다.
Error: Objects are not valid as a React child (found: [object Date]). If you meant to render a collection of children, use an array instead.
React에서 렌더링하려는 값이 Date 객체인 경우 발생하는 것으로 Date 객체를 직접 렌더링 할 수 없으므로, 문자열로 변환해야 한다. 위의 {meta.date}를 {new Date(meta.date).toLocaleDateString()} 로 수정하였다.
이제 보이기는 하는데.. 앞에 #을 붙여 표현한 마크다운 문법이 적용되지 않고 #이 그대로 표시되는 문제가 발생했다.
마크다운 형식으로 지정된 content를 HTML 요소로 변환해 주어야 한다. next-mdx-remote를 사용하여 MDX 콘텐츠를 HTML로 변환한다.
yarn add next-mdx-remote
아래와 같이 src/page.tsx의 코드를 수정했다.
import { MDXRemote } from 'next-mdx-remote';
import { serialize } from 'next-mdx-remote/serialize';
import { getPostData } from '@/lib/posts';
const MainPage = async () => {
const { meta, content } = getPostData('setup');
const mdxSource = await serialize(content);
return (
<div>
<h1>{meta.title}</h1>
<p>{new Date(meta.date).toLocaleDateString()}</p>
<MDXRemote {...mdxSource} />
</div>
);
};
export default MainPage;
또 오류가 발생했는데 next-mdx-remote는 클라이언트 전용 라이브러리로, 서버 컴포넌트에서는 사용할 수 없다는 내용의 오류였다. 클라이언트 컴포넌트로 옮기고, 서버 컴포넌트에서 데이터를 전달하는 방식으로 수정했다.
'use client';
import { MDXRemote } from 'next-mdx-remote';
import { MDXRemoteSerializeResult } from 'next-mdx-remote';
const MDXRenderer = ({ content }: { content: MDXRemoteSerializeResult }) => {
return <MDXRemote {...content} />;
};
export default MDXRenderer;
import { serialize } from 'next-mdx-remote/serialize';
import { getPostData } from '@/lib/posts';
import MDXRenderer from '@/components/MDXRenderer';
const MainPage = async () => {
const { meta, content } = getPostData('setup');
const mdxContent = await serialize(content);
return (
<div>
<h1>{meta.title}</h1>
<p>{new Date(meta.date).toLocaleDateString()}</p>
<MDXRenderer content={mdxContent} />
</div>
);
};
export default MainPage;
또 다른 문제가 있었는데 아래 사진에서 *로 강조한 강조강조 문구는 굵게 보이는데, ###로 강조한 제목은 크게 안보이고 보통 글씨와 같게 보인다.
yarn add @tailwindcss/typography
import typography from '@tailwindcss/typography';
export default {
~
plugins: [typography],
}
성공 ! 이제 MDX 파일을 블로그에서 정상적으로 렌더링할 수 있다.