hhjeee.log
GitHub Icon

mdx 파일 파싱하기

2024년 12월 05일


MDX 파일을 활용하여 블로그 게시글을 작성하고 이를 렌더링해보자.

1. MDX 파일 작성

임시로 띄워 볼 mdx 파일을 작성한다. ---는 mdx 파일에서 메타데이터와 본문을 분리해서 사용할 수 있도록 도와주는 역할을 한다.

src/posts/blog/setup.tsx
---
title: '임시게시글'
date: 2024-12-05
desc: 임시게시글입니다아아아아아ㅏ
---
 
### 제목
 
이 글은 임시게시글 입니다.

2. gray-matter를 이용한 MDX 파일 파싱

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를 구분하여 받아올 수 있다.

src/lib/posts.ts
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 본문
  };
}

3. 메타데이터와 본문 출력

위에서 작성한 getPostData 함수를 이용해 데이터를 가져오고, 메타데이터와 본문을 렌더링하는 컴포넌트를 작성한다. 우선 파싱이 잘 되었나만 확인해 보기 위해 간단하게 title, date, content만 띄우도록 하였다.

src/app/pages.tsx
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;

⚠️ 발생한 문제: Date 객체 렌더링 오류

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()} 로 수정하였다.

⚠️ 발생한 문제: 마크다운 문법이 적용되지 않음

이제 보이기는 하는데.. 앞에 #을 붙여 표현한 마크다운 문법이 적용되지 않고 #이 그대로 표시되는 문제가 발생했다.

parsing-error1

4. next-mdx-remote를 이용한 MDX 렌더링

마크다운 형식으로 지정된 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는 클라이언트 전용 라이브러리

또 오류가 발생했는데 next-mdx-remote는 클라이언트 전용 라이브러리로, 서버 컴포넌트에서는 사용할 수 없다는 내용의 오류였다. 클라이언트 컴포넌트로 옮기고, 서버 컴포넌트에서 데이터를 전달하는 방식으로 수정했다.

src/components/MDXRenderer.tsx
'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;
src/app/page.tsx
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;

parsing-error2

5. Tailwind Typography 적용

또 다른 문제가 있었는데 아래 사진에서 *로 강조한 강조강조 문구는 굵게 보이는데, ###로 강조한 제목은 크게 안보이고 보통 글씨와 같게 보인다.

  1. @tailwindcss/typography 플러그인 설치
yarn add @tailwindcss/typography
  1. tailwind.config.ts 수정
import typography from '@tailwindcss/typography';
export default {
    ~
    plugins: [typography],
}
  1. markdown 보여주는 부분의 className에 prose 클래스 추가

parsing

성공 ! 이제 MDX 파일을 블로그에서 정상적으로 렌더링할 수 있다.