MDX 설정하기 in Next.js 15 🍝 (Markdown for thecomponent era)

개발을 하다 보면 마크다운 문서에 인터랙티브한 React 컴포넌트를 섞고 싶을 때가 있을 수 있다.

(ex. 문서 안에 버튼을 넣거나, 경고 박스를 커스텀 컴포넌트로 만들고 싶을 때)

 

그럴 때 유용하게 쓰이는 게 바로 MDX이다 🍹

 

MDX란?

MDXMarkdown + JSX이다.
즉, 마크다운 문법 안에서 React 컴포넌트를 직접 사용할 있는 포맷을 의미한다.

 

직접 예시로 확인해보자면, 다음과 같은 느낌이다.

# 안녕하세요

이건 일반 마크다운입니다.

<Button>눌러보세요!</Button>

+ markdown  문법 #과 컴포넌트 Button이 동시에 존재하는 특이한 문법이다 

 

🚀 Next.js 15에서 MDX 사용하는 방법

그렇다면 이번에는 App Router기반 Next.js 15버전에서 직접 MDX를 써보고자 한다.

 

먼저 필요한 패키지를 설치한다.

yarn add @next/mdx @mdx-js/loader @mdx-js/react

 

그리고 이제 next.js의 webpack 설정을 건드려주자.

 

⚙️ next.config.js 설정

// next.config.ts
import withMdxCreate from '@next/mdx';
import type { NextConfig } from 'next';

const withMDX = withMdxCreate({
  // Optionally provide remark and rehype plugins
  options: {
    // If you use remark-gfm, you'll need to use next.config.mjs
    // as the package is ESM only
    // https://github.com/remarkjs/remark-gfm#install
    remarkPlugins: [],
    rehypePlugins: [],
  },
  extension: /\.mdx?$/,
});

export default withMDX(nextConfig);

(옵션으로 remark 플러그인과 rehype 플러그인을 설정해줄 수 있는데, remark는 markdown processor로 마크다운 문법을 지원하고자 사용된 플러그인이다. 추가 설정을 원한다면, 이곳에서 설정할 수 있다.)

(rehype 플러그인은 remark와 비슷한 용도로 사용되는데, 이쪽은 html을 지원하기 위한 플러그인으로, 동일하게 추가 설정을 해줄 수 있다)

 

 

설정을 끝마친 뒤, 직접 next.js 프로젝트에서 mdx를 렌더링해볼 수 있다.

일단, 실제로 content.mdx라는 파일을 만들어 다음과 같이 작성해보자.

 

markdown 문법과 JSX 문법이 혼재된 코드를 다음과 같이 작성할 수 있다.

import { Button } from './Button';

# 문서 페이지입니다

이건 MDX에서 작성된 문서입니다. 아래는 커스텀 버튼입니다:

<Button>클릭하세요</Button>

 

# Button 컴포넌트는 다음과 같이 작성한다.

import React, { FC, PropsWithChildren } from 'react';

interface ButtonProps {}

export const Button: FC<PropsWithChildren<ButtonProps>> = ({ children }) => {
  const handleClick = () => {
    alert('Button clicked!');
  };
  return (
    <div role="button" onClick={handleClick}>
      {children}
    </div>
  );
};

 

 

그리고 실제 페이지를 작성하여 잘 렌더링되는지 테스트해볼 수 있다.

'use client';
import dynamic from 'next/dynamic';
import React from 'react';

const SampleContent = dynamic(() => import('./content.mdx'));

// with webpack 5, didn't work in turbopack
const MdxPage = () => {
  return (
    <div>
      <h1>MDX Page</h1>
      <SampleContent />
    </div>
  );
};

export default MdxPage;

실행해보면, mdx로 작성한대로 잘 화면이 뜨는 것을 볼 수 있다 ✨

버튼을 클릭해보면, 기능도 잘 동작한다.

 

+ 다면, 이번 구현에서 원래 사용하던 터보팩 설정을 끄고 진행을 하였다.

webpack에서의 설정은 레퍼런스도 많고 비교적 간단했는데 터보팩에서의 설정은 레퍼런스가 많지 않고, 동작에 대한 신뢰성이나 이슈가 좀 있는 상태인 것 같았다... 💧

 

💘 직접 작성한 코드 

https://github.com/citron03/practice-next-15/commit/5bca059534b9f9da323de97bac5a1990c192d3bd

 

feat(mdx): setting mdx with next.js · citron03/practice-next-15@5bca059

"lint:css": "stylelint '**/*.{css,scss,sass}' --fix"

github.com

 

 

🌠 참고자료 

https://mdxjs.com/

 

Markdown for the component era | MDX

MDX lets you use JSX in your markdown content. You can import components, such as interactive charts or alerts, and embed them within your content. This makes writing long-form content with components a blast.

mdxjs.com

https://github.com/remarkjs/remark-gfm#install

 

GitHub - remarkjs/remark-gfm: remark plugin to support GFM (autolink literals, footnotes, strikethrough, tables, tasklists)

remark plugin to support GFM (autolink literals, footnotes, strikethrough, tables, tasklists) - remarkjs/remark-gfm

github.com

https://remark.js.org/

 

remark

Markdown processor powered by plugins

remark.js.org

https://github.com/rehypejs/rehype

 

GitHub - rehypejs/rehype: HTML processor powered by plugins part of the @unifiedjs collective

HTML processor powered by plugins part of the @unifiedjs collective - rehypejs/rehype

github.com

https://github.com/vercel/turborepo/issues/6365

 

[turborepo] mdx files don't cause rebuild · Issue #6365 · vercel/turborepo

What version of Turborepo are you using? 1.10.16 What package manager are you using / does the bug impact? pnpm What operating system are you using? Linux Describe the Bug MDX Files Not Triggering ...

github.com