가이드: MDX
가이드: MDX | Next.js
섹션 제목: “가이드: MDX | Next.js”소스 URL: https://nextjs.org/docs/app/guides/mdx
Next.js에서 마크다운과 MDX를 사용하는 방법
섹션 제목: “Next.js에서 마크다운과 MDX를 사용하는 방법”마지막 업데이트 2026년 2월 20일
Markdown은 텍스트 서식을 지정할 수 있는 경량 마크업 언어입니다. 일반 텍스트 문법으로 작성한 뒤 구조적으로 유효한 HTML로 변환할 수 있으며, 웹사이트나 블로그의 콘텐츠 작성에 자주 쓰입니다.
You write…
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 Router에서 기본인 서버 컴포넌트에서도 사용할 수 있도록 지원합니다.
알아두면 좋아요 : 완전한 예제를 확인하려면 Portfolio Starter Kit 템플릿을 살펴보세요.
종속성 설치
섹션 제목: “종속성 설치”@next/mdx 패키지와 관련 패키지는 Next.js가 마크다운과 MDX를 처리하도록 구성하는 데 사용됩니다. 로컬 파일에서 데이터를 읽어와 /pages 또는 /app 디렉터리 내에 .md 또는 .mdx 확장자를 가진 페이지를 직접 만들 수 있습니다.
Next.js에서 MDX를 렌더링하려면 다음 패키지를 설치하세요:
pnpmnpmyarnbun
Terminal
pnpm add @next/mdx @mdx-js/loader @mdx-js/react @types/mdxnext.config.mjs 구성
섹션 제목: “next.config.mjs 구성”프로젝트 루트에 있는 next.config.mjs 파일을 업데이트해 MDX를 사용할 수 있도록 구성하세요:
next.config.mjs
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */ const nextConfig = { // Configure `pageExtensions` to include markdown and MDX files pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], // Optionally, add any other Next.js config below }
const withMDX = createMDX({ // Add markdown plugins here, as desired })
// Merge MDX config with Next.js config export default withMDX(nextConfig)이렇게 하면 .mdx 파일을 애플리케이션에서 페이지, 라우트 또는 import로 사용할 수 있습니다.
.md 파일 처리
섹션 제목: “.md 파일 처리”기본적으로 next/mdx는 .mdx 확장자 파일만 컴파일합니다. .md 파일을 webpack으로 처리하려면 extension 옵션을 업데이트하세요:
next.config.mjs
const withMDX = createMDX({ extension: /\.(md|mdx)$/, })mdx-components.tsx 파일 추가
섹션 제목: “mdx-components.tsx 파일 추가”프로젝트 루트(예: pages 또는 app와 같은 레벨, 필요 시 src 내부)에 mdx-components.tsx(또는 .js) 파일을 만들어 전역 MDX 컴포넌트를 정의하세요.
mdx-components.tsx
JavaScriptTypeScript
import type { MDXComponents } from 'mdx/types'
const components: MDXComponents = {}
export function useMDXComponents(): MDXComponents { return components }알아두면 좋아요 :
- App Router에서
@next/mdx를 사용하려면mdx-components.tsx가 필수이며, 없으면 작동하지 않습니다.mdx-components.tsx파일 컨벤션에 대해 자세히 알아보세요.- 사용자 정의 스타일과 컴포넌트를 사용하는 방법을 살펴보세요.
MDX 렌더링
섹션 제목: “MDX 렌더링”Next.js의 파일 기반 라우팅을 사용하거나 MDX 파일을 다른 페이지로 import하여 MDX를 렌더링할 수 있습니다.
파일 기반 라우팅 사용
섹션 제목: “파일 기반 라우팅 사용”파일 기반 라우팅을 사용할 때는 다른 페이지와 동일하게 MDX 페이지를 사용할 수 있습니다.
App Router 앱에서는 메타데이터도 함께 사용할 수 있습니다.
/app 디렉터리 안에 새 MDX 페이지를 만드세요:
my-project ├── app │ └── mdx-page │ └── page.(mdx/md) |── mdx-components.(tsx/js) └── package.json이 파일들에서 MDX를 사용할 수 있으며, MDX 페이지 안에서 React 컴포넌트를 직접 import할 수도 있습니다:
import { MyComponent } from 'my-component'
# Welcome to my MDX page!
This is some **bold** and _italics_ text.
This is a list in markdown:
- One - Two - Three
Checkout my React component:
<MyComponent />/mdx-page 라우트로 이동하면 렌더링된 MDX 페이지가 표시되어야 합니다.
import 사용
섹션 제목: “import 사용”/app 디렉터리에 새 페이지를 만들고 원하는 위치에 MDX 파일을 추가하세요:
. ├── app/ │ └── mdx-page/ │ └── page.(tsx/js) ├── markdown/ │ └── welcome.(mdx/md) ├── mdx-components.(tsx/js) └── package.json이 파일들에서 MDX를 사용할 수 있으며, MDX 페이지 안에서 React 컴포넌트를 직접 import할 수도 있습니다:
페이지 내부에서 MDX 파일을 import하여 콘텐츠를 표시하세요:
app/mdx-page/page.tsx
JavaScriptTypeScript
import Welcome from '@/markdown/welcome.mdx'
export default function Page() { return <Welcome /> }/mdx-page 라우트로 이동하면 렌더링된 MDX 페이지가 표시되어야 합니다.
동적 import 사용
섹션 제목: “동적 import 사용”파일 시스템 라우팅 대신 동적 MDX 컴포넌트를 import할 수 있습니다.
예를 들어, 별도의 디렉터리에서 MDX 컴포넌트를 로드하는 동적 라우트 세그먼트를 가질 수 있습니다:
generateStaticParams를 사용해 제공된 라우트를 사전 렌더링할 수 있습니다. dynamicParams를 false로 설정하면 generateStaticParams에 정의되지 않은 라우트에 접근할 때 404가 발생합니다.
app/blog/[slug]/page.tsx
JavaScriptTypeScript
export default async function Page({ params, }: { params: Promise<{ slug: string }> }) { const { slug } = await params const { default: Post } = await import(`@/content/${slug}.mdx`)
return <Post /> }
export function generateStaticParams() { return [{ slug: 'welcome' }, { slug: 'about' }] }
export const dynamicParams = false알아두면 좋아요 : import에서
.mdx파일 확장자를 명시하세요. 모듈 경로 별칭(예:@/content)을 사용할 필요는 없지만, import 경로를 단순화하는 데 도움이 됩니다.
사용자 정의 스타일과 컴포넌트 사용
섹션 제목: “사용자 정의 스타일과 컴포넌트 사용”마크다운이 렌더링되면 기본 HTML 요소에 매핑됩니다. 예를 들어, 다음 마크다운을 작성하면:
## This is a heading
This is a list in markdown:
- One - Two - Three다음 HTML이 생성됩니다:
<h2>This is a heading</h2>
<p>This is a list in markdown:</p>
<ul> <li>One</li> <li>Two</li> <li>Three</li> </ul>마크다운을 스타일링하려면 생성된 HTML 요소에 매핑되는 사용자 정의 컴포넌트를 제공하면 됩니다. 스타일과 컴포넌트는 전역, 로컬, 공유 레이아웃 차원에서 구현할 수 있습니다.
전역 스타일과 컴포넌트
섹션 제목: “전역 스타일과 컴포넌트”mdx-components.tsx에 스타일과 컴포넌트를 추가하면 애플리케이션의 모든 MDX 파일에 영향을 줍니다.
mdx-components.tsx
JavaScriptTypeScript
import type { MDXComponents } from 'mdx/types' import Image, { ImageProps } from 'next/image'
// This file allows you to provide custom React components // to be used in MDX files. You can import and use any // React component you want, including inline styles, // components from other libraries, and more.
const components = { // Allows customizing built-in components, e.g. to add styling. h1: ({ children }) => ( <h1 style={{ color: 'red', fontSize: '48px' }}>{children}</h1> ), img: (props) => ( <Image sizes="100vw" style={{ width: '100%', height: 'auto' }} {...(props as ImageProps)} /> ), } satisfies MDXComponents
export function useMDXComponents(): MDXComponents { return components }로컬 스타일과 컴포넌트
섹션 제목: “로컬 스타일과 컴포넌트”특정 페이지에만 스타일과 컴포넌트를 적용하려면 import한 MDX 컴포넌트에 전달하면 됩니다. 이러한 설정은 전역 스타일과 컴포넌트와 병합되며, 필요한 부분만 재정의합니다.
app/mdx-page/page.tsx
JavaScriptTypeScript
import Welcome from '@/markdown/welcome.mdx'
function CustomH1({ children }) { return <h1 style={{ color: 'blue', fontSize: '100px' }}>{children}</h1> }
const overrideComponents = { h1: CustomH1, }
export default function Page() { return <Welcome components={overrideComponents} /> }공유 레이아웃
섹션 제목: “공유 레이아웃”MDX 페이지에서 레이아웃을 공유하려면 App Router의 빌트인 레이아웃 지원을 사용하면 됩니다.
app/mdx-page/layout.tsx
JavaScriptTypeScript
export default function MdxLayout({ children }: { children: React.ReactNode }) { // Create any shared layout or styles here return <div style={{ color: 'blue' }}>{children}</div> }Tailwind typography 플러그인 사용
섹션 제목: “Tailwind typography 플러그인 사용”애플리케이션 스타일링에 Tailwind를 사용한다면 @tailwindcss/typography 플러그인을 통해 Tailwind 구성과 스타일을 마크다운 콘텐츠에도 재사용할 수 있습니다.
이 플러그인은 마크다운처럼 외부 소스에서 가져온 콘텐츠 블록에 타이포그래피 스타일을 적용할 수 있는 prose 클래스를 추가합니다.
Tailwind typography를 설치하고 공유 레이아웃과 함께 사용하여 원하는 prose 스타일을 추가하세요.
app/mdx-page/layout.tsx
JavaScriptTypeScript
export default function MdxLayout({ children }: { children: React.ReactNode }) { // Create any shared layout or styles here return ( <div className="prose prose-headings:mt-8 prose-headings:font-semibold prose-headings:text-black prose-h1:text-5xl prose-h2:text-4xl prose-h3:text-3xl prose-h4:text-2xl prose-h5:text-xl prose-h6:text-lg dark:prose-headings:text-white"> {children} </div> ) }프론트매터
섹션 제목: “프론트매터”프론트매터는 페이지에 대한 데이터를 저장하는 YAML 유사 키/값 쌍입니다. @next/mdx는 기본적으로 프론트매터를 지원하지 않지만, 다음과 같이 MDX 콘텐츠에 프론트매터를 추가하는 다양한 솔루션이 있습니다:
@next/mdx는 다른 JavaScript 컴포넌트와 동일하게 export를 사용할 수 있도록 지원합니다:
이제 MDX 파일 바깥에서도 메타데이터를 참조할 수 있습니다:
app/blog/page.tsx
JavaScriptTypeScript
import BlogPost, { metadata } from '@/content/blog-post.mdx'
export default function Page() { console.log('metadata: ', metadata) //=> { author: 'John Doe' } return <BlogPost /> }이는 MDX 모음에서 반복 처리하며 데이터를 추출하고 싶을 때 흔히 사용됩니다. 예를 들어 모든 블로그 게시글에서 블로그 인덱스 페이지를 만드는 경우입니다. Node의 fs 모듈이나 globby 같은 패키지를 사용해 게시글 디렉터리를 읽고 메타데이터를 추출할 수 있습니다.
알아두면 좋아요 :
fs,globby등은 서버 측에서만 사용할 수 있습니다.- 완전한 작동 예시는 Portfolio Starter Kit 템플릿을 참고하세요.
remark 및 rehype 플러그인
섹션 제목: “remark 및 rehype 플러그인”원한다면 remark와 rehype 플러그인을 제공해 MDX 콘텐츠를 변환할 수 있습니다.
예를 들어 remark-gfm을 사용하면 GitHub Flavored Markdown을 지원할 수 있습니다.
remark와 rehype 생태계는 ESM만 지원하므로, 구성 파일로 next.config.mjs 또는 next.config.ts를 사용해야 합니다.
next.config.mjs
import remarkGfm from 'remark-gfm' import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */ const nextConfig = { // Allow .mdx extensions for files pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], // Optionally, add any other Next.js config below }
const withMDX = createMDX({ // Add markdown plugins here, as desired options: { remarkPlugins: [remarkGfm], rehypePlugins: [], }, })
// Combine MDX and Next.js config export default withMDX(nextConfig)Turbopack과 함께 플러그인 사용하기
섹션 제목: “Turbopack과 함께 플러그인 사용하기”Turbopack에서 플러그인을 사용하려면 최신 @next/mdx로 업그레이드한 뒤 문자열로 플러그인 이름을 지정하세요:
next.config.mjs
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */ const nextConfig = { pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], }
const withMDX = createMDX({ options: { remarkPlugins: [ // Without options 'remark-gfm', // With options ['remark-toc', { heading: 'The Table' }], ], rehypePlugins: [ // Without options 'rehype-slug', // With options ['rehype-katex', { strict: true, throwOnError: true }], ], }, })
export default withMDX(nextConfig)알아두면 좋아요 :
직렬화 가능한 옵션이 없는 remark와 rehype 플러그인은 JavaScript 함수를 Rust로 전달할 수 없기 때문에 아직 Turbopack에서 사용할 수 없습니다.
심층 탐구: 마크다운을 HTML로 어떻게 변환하나요?
섹션 제목: “심층 탐구: 마크다운을 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) // Convert into markdown AST .use(remarkRehype) // Transform to HTML AST .use(rehypeSanitize) // Sanitize HTML input .use(rehypeStringify) // Convert AST into serialized HTML .process('Hello, Next.js!')
console.log(String(file)) // <p>Hello, Next.js!</p> }remark와 rehype 생태계에는 syntax highlighting, linking headings, generating a table of contents 등을 위한 플러그인이 포함되어 있습니다.
위에서 설명한 대로 @next/mdx를 사용할 때는 remark나 rehype를 직접 사용할 필요가 없습니다. 여기서는 @next/mdx 패키지가 내부적으로 무엇을 하는지 더 깊이 이해하기 위해 설명합니다.
Rust 기반 MDX 컴파일러 사용하기(실험적)
섹션 제목: “Rust 기반 MDX 컴파일러 사용하기(실험적)”Next.js는 Rust로 작성된 새로운 MDX 컴파일러를 지원합니다. 이 컴파일러는 아직 실험 단계이며 프로덕션 사용이 권장되지 않습니다. 새 컴파일러를 사용하려면 withMDX에 전달할 때 next.config.js를 구성해야 합니다:
next.config.js
module.exports = withMDX({ experimental: { mdxRs: true, }, })mdxRs는 mdx 파일을 어떻게 변환할지 구성하는 객체도 받을 수 있습니다.
next.config.js
module.exports = withMDX({ experimental: { mdxRs: { jsxRuntime?: string // Custom jsx runtime jsxImportSource?: string // Custom jsx import source, mdxType?: 'gfm' | 'commonmark' // Configure what kind of mdx syntax will be used to parse & transform }, }, })유용한 링크
섹션 제목: “유용한 링크”보내기