Nextjs 14에서 Markdown과 MDX를 사용하여 동적 콘텐츠 관리하기

Nextjs 14에서 Markdown과 MDX를 사용하여 동적 콘텐츠 관리하기
Cozy CodingPosted On Jul 23, 20248 min read

마크다운과 MDX

마크다운은 텍스트를 서식 지정하는 데 사용되는 가벼운 마크업 언어입니다. 일반 텍스트 구문을 사용하여 작성하고 이를 구조적으로 유효한 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를 마크다운 파일에 직접 작성할 수 있는 마크다운의 상위 집합입니다. 이는 동적 상호 작용을 추가하고 콘텐츠 내에서 리액트 컴포넌트를 포함시키는 강력한 방법입니다.

Next.js는 로컬 MDX 콘텐츠와 서버에서 동적으로 가져온 원격 MDX 파일을 모두 지원할 수 있습니다. Next.js 플러그인은 마크다운과 리액트 컴포넌트를 HTML로 변환하는 처리를 담당하며, 앱 라우터의 기본인 서버 컴포넌트에서도 사용할 수 있습니다.

@next/mdx

@next/mdx 패키지는 Next.js를 설정하여 markdown 및 MDX를 처리할 수 있도록합니다. 이는 로컬 파일에서 데이터를 가져와 .mdx 확장자가있는 페이지를 만들 수 있게 해줍니다. 이를 /pages 또는 /app 디렉토리에서 직접 사용할 수 있습니다.

우리 함께 Next.js에서 MDX를 구성하고 사용하는 방법을 살펴보겠습니다.

시작하기

MDX를 렌더링하는 데 필요한 패키지를 설치해주세요:

npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

애플리케이션 루트(src/ 또는 app/의 상위 폴더)에 mdx-components.tsx 파일을 만들어주세요:

참고: App Router를 사용하려면 mdx-components.tsx 파일이 필요하며, 이 파일이 없으면 MDX가 작동하지 않습니다.

import type { MDXComponents } from 'mdx/types'

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
  }
}

프로젝트 루트의 next.config.js 파일을 업데이트하여 MDX를 사용하도록 구성하십시오:

const withMDX = require('@next/mdx')()

/** @type {import('next').NextConfig} */
const nextConfig = {
  // `pageExtensions`를 구성하여 MDX 파일을 포함하도록 설정합니다
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // 필요에 따라 다른 Next.js 구성을 추가할 수 있습니다
}

module.exports = withMDX(nextConfig)

그런 다음, /app 디렉토리 내에 새로운 MDX 페이지를 생성하십시오:

  your-project
  ├── app
  │   └── my-mdx-page
  │       └── page.mdx
  └── package.json

이제 마크다운을 사용하여 MDX 페이지 내에서 직접 React 컴포넌트를 가져올 수 있습니다:

import { MyComponent } from 'my-components'

# 내 MDX 페이지에 오신 것을 환영합니다!

이것은 **굵은** 및 _이탤릭체_ 텍스트입니다.

이것은 마크다운 목록입니다:

- 하나
- 둘
- 셋

React 컴포넌트를 확인해보세요:

<MyComponent />

/my-mdx-page 경로로 이동하면 렌더링된 MDX가 표시됩니다.

원격 MDX

만약 마크다운 또는 MDX 파일 또는 컨텐츠가 다른 곳에 저장되어 있다면, 서버에서 동적으로 가져올 수 있습니다. 이는 별도의 로컬 폴더, CMS, 데이터베이스 또는 다른 곳에 저장된 컨텐츠에 유용합니다. 이용에 많은 커뮤니티 패키지는 next-mdx-remote가 있습니다.

알아두면 좋은 점: 조심히 진행해주세요. MDX는 JavaScript로 컴파일되고 서버에서 실행됩니다. 신뢰할 수 있는 소스에서만 MDX 컨텐츠를 가져와야 합니다. 그렇지 않으면 원격 코드 실행(RCE)로 이어질 수 있습니다.

다음 예제에서는 next-mdx-remote를 사용합니다:

import { MDXRemote } from 'next-mdx-remote/rsc'
 
export default async function RemoteMdxPage() {
  // MDX 텍스트 - 로컬 파일, 데이터베이스, CMS, fetch 등에서 가져올 수 있습니다...
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}

/my-mdx-page-remote 경로로 이동하면 렌더링된 MDX가 표시됩니다.

레이아웃

MDX 페이지 전체에 대한 레이아웃을 공유하려면 App Router의 내장 레이아웃 지원을 사용할 수 있습니다.

export default function MdxLayout({ children }: { children: React.ReactNode }) {
  // 여기서 공유 레이아웃이나 스타일을 작성할 수 있어요
  return <div style={ color: 'blue' }>{children}</div>
}

Remark과 Rehype 플러그인

MDX 콘텐츠를 변환하기 위해 remark와 rehype 플러그인을 선택적으로 제공할 수 있어요.

예를 들어, remark-gfm을 사용하여 GitHub Flavored Markdown을 지원할 수 있어요.

table 태그를 Markdown 형식으로 변경하세요.

Since the remark and rehype ecosystem is ESM only, you`ll need to use next.config.mjs as the configuration file.

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Configure `pageExtensions`` to include MDX files
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optionally, add any other Next.js config below
}

const withMDX = createMDX({
  // Add markdown plugins here, as desired
  options: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [],
  },
})

// Wrap MDX and Next.js config with each other
export default withMDX(nextConfig)

Frontmatter

Frontmatter is a YAML like key/value pairing that can be used to store data about a page. @next/mdx does not support frontmatter by default, though there are many solutions for adding frontmatter to your MDX content, such as:

  • remark-frontmatter
  • remark-mdx-frontmatter
  • gray-matter .

@next/mdx를 사용하여 페이지 메타데이터에 액세스하려면 .mdx 파일 내에서 metadata 객체를 내보낼 수 있습니다:

export const metadata = {
  author: 'John Doe',
}

# 내 MDX 페이지

사용자 정의 요소

마크다운을 사용하는 기쁜 점 중 하나는 기본 HTML 요소로 매핑되어 빠르고 직관적으로 쓸 수 있다는 것입니다:

이것은 마크다운에서의 리스트입니다:
 
- 하나
- 둘
- 셋

위의 마크다운은 다음과 같은 HTML을 생성합니다:

<p>이것은 마크다운에서의 리스트입니다:</p>
 
<ul>
  <li>하나</li>
  <li></li>
  <li></li>
</ul>

무슨 종류의 웹사이트나 애플리케이션을 만들 때 자체 요소에 스타일을 적용하고 싶을 때, 단축 코드를 전달할 수 있어요. 이것들은 HTML 요소에 매핑되는 자체적인 사용자 정의 컴포넌트입니다.

이를 위해, 애플리케이션 루트에 있는 mdx-components.tsx 파일을 열고 사용자 정의 요소를 추가하세요:

import type { MDXComponents } from 'mdx/types'
import Image, { ImageProps } from 'next/image'

// 이 파일을 사용하면 MDX 파일에서 사용할 사용자 정의 React 컴포넌트를 제공할 수 있어요.
// 필요한 경우 다른 라이브러리의 컴포넌트를 가져와 사용할 수 있고,
// 인라인 스타일이나 기타 것들을 포함해서 필요한 React 컴포넌트를 가져와 사용 가능합니다.

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    // 내장된 컴포넌트를 사용자 정의하여 스타일을 추가하는 등의 작업이 가능합니다.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...(props as ImageProps)}
      />),
    ...components,
  }
}

심층분석: 마크다운을 HTML로 어떻게 변환하나요?

리액트는 마크다운을 네이티브로 이해하지 못합니다. 먼저 마크다운 플레인 텍스트를 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('Hello, Next.js!')

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

remark와 rehype 에코시스템에는 문법 강조 표시를 위한 플러그인부터 제목 링크, 목차 생성 등 다양한 플러그인이 포함되어 있습니다.

위에서 보여준대로 @next/mdx를 사용할 때는 직접적으로 remark나 rehype을 사용할 필요가 없습니다. 이것은 대신 여러분을 위해 처리됩니다. @next/mdx 패키지가 무엇을 하는지 더 깊이 이해하기 위해 여기에 설명하고 있습니다.

러스트 기반 MDX 컴파일러 사용 (실험적)

Next.js는 러스트로 작성된 새로운 MDX 컴파일러를 지원합니다. 이 컴파일러는 아직 실험 단계이며 프로덕션 환경에서 권장되지 않습니다. 새로운 컴파일러를 사용하려면 next.config.js를 구성해야 합니다. 이를 withMDX에 전달할 때 다음과 같이 설정하세요:

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

도움이 되는 링크

  • MDX
  • @next/mdx
  • remark
  • rehype
  • Markdoc