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
를 생성하세요.
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
를 사용하도록 설정하세요.
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
mdxRs: true,
},
}
const withMDX = require('@next/mdx')()
module.exports = withMDX(nextConfig)
app
디렉터리에 MDX 콘텐츠가 포함된 새 파일을 추가하세요.
안녕, Next.js!
MDX 파일에서 React 컴포넌트를 가져와 사용할 수 있습니다.
콘텐츠를 표시하기 위해 page
내에서 MDX 파일을 가져옵니다.
import HelloWorld from './hello.mdx'
export default function Page() {
return <HelloWorld />
}
JavaScript
import HelloWorld from './hello.mdx'
export default function Page() {
return <HelloWorld />
}
원격 MDX
만약 마크다운 또는 MDX 파일이 애플리케이션 내부에 존재하지 않는다면, 서버에서 그것들을 동적으로 가져올 수 있습니다. 이것은 CMS 또는 다른 데이터 소스에서 내용을 가져오는 데 유용합니다.
MDX 콘텐츠를 가져오기 위한 두 가지 인기 있는 커뮤니티 패키지가 있습니다. next-mdx-remote
와 contentlayer
. 예를 들면, 아래 예제는 next-mdx-remote
를 사용합니다.
주의 깊게 진행해 주세요. MDX는 자바스크립트로 컴파일되며 서버에서 실행됩니다. 신뢰할 수 있는 출처에서만 MDX 콘텐츠를 가져와야 합니다. 그렇지 않으면 원격 코드 실행(RCE)이 발생할 수 있습니다.
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
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 콘텐츠를 변환하기 위해 remark
와 rehype
플러그인을 선택적으로 제공할 수 있습니다. 예를 들어, GitHub Flavored Markdown을 지원하기 위해 remark-gfm
를 사용할 수 있습니다.
remark
와 rehype
생태계가 ESM만 지원하므로, 설정 파일로 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"
를 지정해야 합니다.
const withMDX = require('@next/mdx')({
// ...
options: {
providerImportSource: '@mdx-js/react',
},
})
그런 다음 페이지에서 프로바이더를 설정합니다.
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로 변환해야 합니다. 이 작업은 remark
와 rehype
를 통해 수행할 수 있습니다.
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>
}
remark
와 rehype
생태계에는 구문 강조, 헤딩 링킹, 목차 생성 등을 위한 플러그인들이 포함되어 있습니다.
아래와 같이 @next/mdx
를 사용할 때는 remark
나 rehype
를 직접 사용할 필요가 없습니다. 이미 처리되어 있기 때문입니다.
Rust 기반 MDX 컴파일러 사용하기 (실험적)
Next.js는 Rust로 작성된 새로운 MDX 컴파일러를 지원합니다. 이 컴파일러는 아직 실험적이며, 실제 운영 환경에서의 사용은 권장되지 않습니다. 새로운 컴파일러를 사용하려면, withMDX
에 넘겨줄 때 next.config.js
를 설정해야 합니다.
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})