ReactNextCentral

MDX

Published on
마크다운 파일에서 JSX를 작성하기 위해 MDX를 설정하는 방법을 알아봅니다.
Table of Contents

Markdown은 텍스트를 형식화하는 데 사용되는 경량 마크업 언어입니다. 평문 구문을 사용하여 작성하고 구조적으로 유효한 HTML로 변환할 수 있습니다. 웹사이트와 블로그의 내용을 작성하는 데 일반적으로 사용됩니다.

마크다운으로 아래와 같이 작성하면,

I **love** using [Next.js](https://nextjs.org/)

아래와 같이 변환됩니다.

<p>I <strong>love</strong> using \
<a href="https://nextjs.org/">Next.js</a></p>

MDX는 마크다운의 상위 집합으로 마크다운 파일에서 직접 JSX를 작성할 수 있게 해줍니다. 이는 콘텐츠 내에 동적 상호 작용을 추가하고 React 컴포넌트를 포함하는 강력한 방법입니다.

Next.js는 애플리케이션 내의 로컬 MDX 콘텐츠와 서버에서 동적으로 가져온 원격 MDX 파일을 모두 지원할 수 있습니다. Next.js 플러그인은 마크다운과 React 컴포넌트를 HTML로 변환하며, app에서의 기본 사용을 포함하여 서버 컴포넌트에서의 사용을 지원합니다.


@next/mdx

@next/mdx 패키지는 프로젝트 루트의 next.config.js 파일에서 구성됩니다. 로컬 파일에서 데이터를 가져옵니다, /pages 또는 /app 디렉터리에 .mdx 확장자로 페이지를 직접 생성할 수 있습니다.


시작하기

@next/mdx 패키지를 설치하세요.

터미널
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

응용 프로그램의 루트(즉, app/ 또는 src/의 상위 폴더)에 mdx-components.tsx를 생성하세요.

mdx-components.tsx
import type { MDXComponents } from 'mdx/types'

// 이 파일은 MDX 파일에서 사용할 사용자 지정 React 컴포넌트를 제공합니다.
// 원하는 모든 React 컴포넌트를 가져와서 사용할 수 있으며,
// 다른 라이브러리의 컴포넌트도 포함됩니다.

// `app` 디렉터리에서 MDX를 사용하려면 이 파일이 필요합니다.
export function useMDXComponents(components: MDXComponents)
: MDXComponents {
  return {
    // 기본 컴포넌트를 사용자 정의하려면, 예를 들어 스타일을 추가합니다.
    // h1: ({ children }) 
    // => <h1 style={{ fontSize: "100px" }}>{children}</h1>,
    ...components,
  }
}

next.config.js를 업데이트하여 mdxRs를 사용하도록 설정하세요.

"next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    mdxRs: true,
  },
}

const withMDX = require('@next/mdx')()
module.exports = withMDX(nextConfig)

app 디렉터리에 MDX 콘텐츠가 포함된 새 파일을 추가하세요.

"app/hello.mdx"
안녕, Next.js!

MDX 파일에서 React 컴포넌트를 가져와 사용할 수 있습니다.

콘텐츠를 표시하기 위해 page 내에서 MDX 파일을 가져옵니다.

app/page.tsx
import HelloWorld from './hello.mdx'

export default function Page() {
  return <HelloWorld />
}
JavaScript
app/page.js
import HelloWorld from './hello.mdx'

export default function Page() {
  return <HelloWorld />
}

원격 MDX

만약 마크다운 또는 MDX 파일이 애플리케이션 내부에 존재하지 않는다면, 서버에서 그것들을 동적으로 가져올 수 있습니다. 이것은 CMS 또는 다른 데이터 소스에서 내용을 가져오는 데 유용합니다.

MDX 콘텐츠를 가져오기 위한 두 가지 인기 있는 커뮤니티 패키지가 있습니다. next-mdx-remotecontentlayer. 예를 들면, 아래 예제는 next-mdx-remote를 사용합니다.

주의 깊게 진행해 주세요. MDX는 자바스크립트로 컴파일되며 서버에서 실행됩니다. 신뢰할 수 있는 출처에서만 MDX 콘텐츠를 가져와야 합니다. 그렇지 않으면 원격 코드 실행(RCE)이 발생할 수 있습니다.

app/page.tsx
import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function Home() {
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}
JavaScript
app/page.js
import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function Home() {
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}

레이아웃

MDX 콘텐츠 주위에 레이아웃을 공유하려면 App Router와 함께 내장 레이아웃 지원을 사용할 수 있습니다.


Remark와 Rehype 플러그인

MDX 콘텐츠를 변환하기 위해 remarkrehype 플러그인을 선택적으로 제공할 수 있습니다. 예를 들어, GitHub Flavored Markdown을 지원하기 위해 remark-gfm를 사용할 수 있습니다.

remarkrehype 생태계가 ESM만 지원하므로, 설정 파일로 next.config.mjs를 사용해야 합니다.

next.config.mjs
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'

/** @type {import('next').NextConfig} */
const nextConfig = {}

const withMDX = createMDX({
  options: {
    extension: /\.mdx?$/,
    remarkPlugins: [remarkGfm],
    rehypePlugins: [],
    // 만약 `MDXProvider`를 사용한다면, 아래 줄의 주석을 해제하세요.
    // providerImportSource: "@mdx-js/react",
  },
})
export default withMDX(nextConfig)

프론트매터

프론트매터는 페이지에 대한 데이터를 저장하기 위해 사용할 수 있는 YAML과 유사한 키/값 쌍입니다. @next/mdx는 기본적으로 프론트매터를 지원하지 않지만, MDX 콘텐츠에 프론트매터를 추가하기 위한 많은 솔루션이 있습니다. 예를 들면 gray-matter와 같은 것들이 있습니다.

@next/mdx로 페이지 메타데이터에 접근하려면, .mdx 파일 내에서 메타 객체를 내보낼 수 있습니다.

export const meta = {
  author: 'Rich Haines',
}

# 나의 MDX 페이지

커스텀 요소들

마크다운을 사용하는 즐거운 점 중 하나는, 이것이 기본 HTML 요소로 매핑되기 때문에 빠르고 직관적으로 쓸 수 있다는 것입니다.

이것은 마크다운에서의 목록입니다.

- 하나
--

위의 것은 다음 HTML을 생성합니다.

<p>이것은 마크다운에서의 목록입니다.</p>

<ul>
  <li>하나</li>
  <li></li>
  <li></li>
</ul>

웹사이트나 애플리케이션에 맞춤 느낌을 주기 위해 자신의 요소들을 스타일링하려 할 때, 단축 코드를 전달할 수 있습니다. 이것들은 HTML 요소에 매핑되는 자신만의 커스텀 컴포넌트입니다. 이를 위해 MDXProvider를 사용하고 컴포넌트 객체를 prop으로 전달합니다. 컴포넌트 객체 내의 각 객체 키는 HTML 요소 이름에 매핑됩니다.

활성화하려면 next.config.js에서 providerImportSource: "@mdx-js/react"를 지정해야 합니다.

next.config.js
const withMDX = require('@next/mdx')({
  // ...
  options: {
    providerImportSource: '@mdx-js/react',
  },
})

그런 다음 페이지에서 프로바이더를 설정합니다.

pages/index.js
import { MDXProvider } from '@mdx-js/react'
import Image from 'next/image'
import { Heading, InlineCode, Pre, Table, Text } from 'my-components'

const ResponsiveImage = (props) => (
  <Image
    alt={props.alt}
    sizes="100vw"
    style={{ width: '100%', height: 'auto' }}
    {...props}
  />
)

const components = {
  img: ResponsiveImage,


  h1: Heading.H1,
  h2: Heading.H2,
  p: Text,
  pre: Pre,
  code: InlineCode,
}

export default function Post(props) {
  return (
    <MDXProvider components={components}>
      <main {...props} />
    </MDXProvider>
  )
}

사이트 전체에서 사용한다면, 모든 MDX 페이지가 커스텀 요소 설정을 선택하도록 _app.js에 프로바이더를 추가하려고 할 수 있습니다.


깊게 탐구하기: 마크다운을 HTML로 어떻게 변환하나요?

React는 기본적으로 마크다운을 이해하지 못합니다. 마크다운 평문을 HTML로 변환해야 합니다. 이 작업은 remarkrehype를 통해 수행할 수 있습니다.

remark는 마크다운 주변의 다양한 도구들을 모아놓은 생태계입니다. rehype도 마찬가지로, HTML을 위한 도구 모음입니다. 예를 들어, 아래 코드 조각은 마크다운을 HTML로 변환합니다.

import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'

main()

async function main() {
  const file = await unified()
    .use(remarkParse) // 마크다운 AST로 변환
    .use(remarkRehype) // HTML AST로 변환
    .use(rehypeSanitize) // HTML 입력 청소
    .use(rehypeStringify) // AST를 직렬화된 HTML로 변환
    .process('안녕, Next.js!')

  console.log(String(file)) // <p>안녕, Next.js!</p>
}

remarkrehype 생태계에는 구문 강조, 헤딩 링킹, 목차 생성 등을 위한 플러그인들이 포함되어 있습니다.

아래와 같이 @next/mdx를 사용할 때는 remarkrehype를 직접 사용할 필요가 없습니다. 이미 처리되어 있기 때문입니다.

Rust 기반 MDX 컴파일러 사용하기 (실험적)

Next.js는 Rust로 작성된 새로운 MDX 컴파일러를 지원합니다. 이 컴파일러는 아직 실험적이며, 실제 운영 환경에서의 사용은 권장되지 않습니다. 새로운 컴파일러를 사용하려면, withMDX에 넘겨줄 때 next.config.js를 설정해야 합니다.

next.config.js
module.exports = withMDX({
  experimental: {
    mdxRs: true,
  },
})

유용한 링크들