์ž‘์„ฑ: 2026-03-04 05:39:06์ˆ˜์ •: 2026-03-04 05:39:06

MDX ์ฝ”๋“œ ๋ธ”๋ก ์Šคํƒ€์ผ๋ง: rehype-pretty-code์™€ remark-gfm ๊ฐ€์ด๋“œ

Next.js์—์„œ MDX๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ๊ธฐ๋ณธ ์ฝ”๋“œ ๋ธ”๋ก์€ ๋ฐ‹๋ฐ‹ํ•œ ํ…์ŠคํŠธ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ VS Code์ฒ˜๋Ÿผ ์˜ˆ์œ ํ•˜์ด๋ผ์ดํŒ…๊ณผ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ์ฝ”๋“œ ๋ธ”๋ก์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.


1. ํ•„์ˆ˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜

์ฝ”๋“œ ํ•˜์ด๋ผ์ดํŒ…์„ ์œ„ํ•œ rehype-pretty-code์™€ GitHub ์Šคํƒ€์ผ ๋ฌธ๋ฒ•(ํ‘œ, ์ฒดํฌ๋ฆฌ์ŠคํŠธ ๋“ฑ)์„ ์ง€์›ํ•˜๋Š” remark-gfm์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

npm install next-mdx-remote rehype-pretty-code remark-gfm shiki

2. Next.js ์„ค์ • (next.config.mjs)

ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋“ฑ๋กํ•˜๊ณ  ํ•˜์ด๋ผ์ดํŒ… ํ…Œ๋งˆ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

import remarkGfm from 'remark-gfm';
import rehypePrettyCode from 'rehype-pretty-code';
 
/** @type {import('rehype-pretty-code').Options} */
const options = {
  theme: 'one-dark-pro', // ๋‹ค์–‘ํ•œ ํ…Œ๋งˆ ์„ ํƒ ๊ฐ€๋Šฅ (github-dark, dracula ๋“ฑ)
  keepBackground: true,
};
 
const nextConfig = {
  pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
  // MDX ์„ค์ •
  experimental: {
    mdxRs: true,
  },
};
 
// MDX ๊ตฌ์„ฑ ํ•จ์ˆ˜ (๋ฒ„์ „์— ๋”ฐ๋ผ ์„ค์ • ๋ฐฉ์‹์ด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ)
export default withMDX({
  extension: /\.mdx?$/,
  options: {
    remarkPlugins: [remarkGfm], // ํ‘œ, ๋ฆฌ์ŠคํŠธ ๋“ฑ ํ™•์žฅ ๋ฌธ๋ฒ• ์ง€์›
    rehypePlugins: [[rehypePrettyCode, options]], // ์ฝ”๋“œ ํ•˜์ด๋ผ์ดํŒ…
  },
})(nextConfig);

3. ์ฃผ์š” ๊ธฐ๋Šฅ ํ™œ์šฉ๋ฒ•

โ‘  ๋ผ์ธ ํ•˜์ด๋ผ์ดํŒ…

์ฝ”๋“œ ๋ธ”๋ก ์ƒ๋‹จ์— {} ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์ค„์„ ๊ฐ•์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// 1๋ฒˆ ์ค„๊ณผ 3, 4๋ฒˆ ์ค„์ด ๊ฐ•์กฐ๋ฉ๋‹ˆ๋‹ค.
const a = 1;
const b = 2;
const c = 3;

โ‘ก ํŒŒ์ผ ์ด๋ฆ„ ํ‘œ์‹œ

title="ํŒŒ์ผ๋ช…" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฝ”๋“œ ๋ธ”๋ก ์ƒ๋‹จ์— ํŒŒ์ผ๋ช…์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

lib/utils.ts
export const add = (a: number, b: number) => a + b;

4. CSS ์Šคํƒ€์ผ๋ง (Tailwind CSS ๊ธฐ์ค€)

rehype-pretty-code๊ฐ€ ์ƒ์„ฑํ•˜๋Š” ๋ฐ์ดํ„ฐ ์†์„ฑ์„ ์ด์šฉํ•ด ์Šคํƒ€์ผ์„ ์ž…ํž™๋‹ˆ๋‹ค.

/* ๊ธ€๋กœ๋ฒŒ CSS ๋˜๋Š” Tailwind ๋ ˆ์ด์–ด์— ์ถ”๊ฐ€ */
code[data-theme*=' '] {
  @apply grid min-w-full break-words rounded-none border-l-4 border-l-transparent bg-transparent py-1;
}
 
/* ํ•˜์ด๋ผ์ดํŠธ๋œ ๋ผ์ธ ์Šคํƒ€์ผ */
div[data-rehype-pretty-code-fragment] span[data-highlighted-line] {
  @apply border-l-blue-500 bg-blue-500/10;
}
 
/* ์ฝ”๋“œ ๋ธ”๋ก ์ œ๋ชฉ ์Šคํƒ€์ผ */
div[data-rehype-pretty-code-title] {
  @apply mt-4 rounded-t-lg bg-slate-800 px-4 py-2 text-sm font-medium text-slate-200;
}

5. ๊ฒฐ๋ก 

remark-gfm์œผ๋กœ ๋ฌธ์„œ์˜ ๊ตฌ์กฐ๋ฅผ ์žก๊ณ , rehype-pretty-code๋กœ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๋ฉด ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ์˜ ํ’ˆ์งˆ์ด ํ•œ์ธต ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค. ํŠนํžˆ Shiki ๊ธฐ๋ฐ˜์˜ ์ •์  ํ•˜์ด๋ผ์ดํŒ…์€ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์—†์–ด ์„ฑ๋Šฅ ๋ฉด์—์„œ๋„ ์šฐ์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.