
안녕하세요. 코딩하는 Jay입니다.
예전에 [React 환경에서 Markdown 출력하기(react-markdown)] 글을 올렸는데, 꾸준히 찾아주시더라고요. 그래서 오늘은 한 발 더 들어가서, 실제 서비스에서 자주 필요한 ① 수학 수식(KaTeX) ② 코드 문법 강조(syntax highlight) ③ 유용한 remark/rehype 플러그인을 정리해보려고 합니다.
본 글은 react-markdown v9 기준입니다. (v9에서 API가 일부 바뀌었으니 아래 주의사항도 함께 보세요.)
1. remark와 rehype, 뭐가 다른가요?
react-markdown은 내부적으로 unified 생태계를 씁니다. 처리 흐름을 알면 플러그인 배치가 쉬워집니다.
마크다운 텍스트
→ (remark) mdast: 마크다운 추상 구문 트리
→ (rehype) hast: HTML 추상 구문 트리
→ React 엘리먼트
- remarkPlugins = 마크다운 단계에서 동작 (예:
$수식$문법 인식, GFM 표 파싱) - rehypePlugins = HTML 단계에서 동작 (예: 수식 렌더링, 코드 하이라이트)
즉 "문법을 새로 인식"하는 건 remark, "HTML로 꾸미는" 건 rehype라고 보면 됩니다.
2. 설치
npm install react-markdown remark-gfm remark-math rehype-katex rehype-highlight katex highlight.js
| 패키지 | 역할 |
remark-gfm |
GFM 지원 (표, 체크박스, 취소선, 자동 링크) |
remark-math |
$...$ / $$...$$ 수식 문법 인식 |
rehype-katex |
인식된 수식을 KaTeX로 렌더링 |
rehype-highlight |
코드 블록 문법 강조 (highlight.js 기반) |
3. 기본 + GFM (표·체크박스)
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
export default function MarkdownView({ content }: { content: string }) {
return (
<div className="markdown-body">
<Markdown remarkPlugins={[remarkGfm]}>{content}</Markdown>
</div>
)
}
⚠️ v9 주의:
<Markdown>에 더 이상classNameprop을 직접 못 줍니다. 위처럼 바깥<div>로 감싸서 스타일을 적용하세요.
4. 수학 수식 — KaTeX
remark-math(인식) + rehype-katex(렌더)를 함께 써야 합니다. 그리고 KaTeX CSS import가 필수입니다.
import Markdown from 'react-markdown'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import 'katex/dist/katex.min.css' // ← 이거 빠뜨리면 수식이 깨져 보입니다
export default function MathMarkdown({ content }: { content: string }) {
return (
<Markdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
{content}
</Markdown>
)
}
마크다운에서는 이렇게 씁니다.
인라인 수식: $E = mc^2$
블록 수식:
$$
a^2 + b^2 = c^2
$$
💡 자주 하는 실수 2가지: (1)
katex.min.cssimport 누락 → 수식 모양이 다 깨집니다. (2) 본문에$기호(가격 등)가 많을 때 → 의도치 않게 수식으로 파싱될 수 있으니\$로 이스케이프하세요.
5. 코드 문법 강조 (syntax highlight)
방법 A — rehype-highlight (가장 간단)
import Markdown from 'react-markdown'
import rehypeHighlight from 'rehype-highlight'
import 'highlight.js/styles/github-dark.css' // ← 테마 CSS 필수
export default function CodeMarkdown({ content }: { content: string }) {
return (
<Markdown rehypePlugins={[rehypeHighlight]}>{content}</Markdown>
)
}
```js 처럼 fence에 언어를 적으면 자동으로 강조됩니다. 테마 CSS만 import하면 끝이라 가장 빠릅니다.
방법 B — react-syntax-highlighter (커스터마이징·라인 번호)
라인 번호, 특정 줄 강조 등 세밀한 제어가 필요하면 components로 code를 직접 교체합니다.
import Markdown from 'react-markdown'
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
export default function CodeMarkdown({ content }: { content: string }) {
return (
<Markdown
components={{
code({ className, children, ...rest }) {
const match = /language-(\w+)/.exec(className || '')
return match ? (
<SyntaxHighlighter
{...rest}
PreTag="div"
language={match[1]}
style={oneDark}
showLineNumbers
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code className={className} {...rest}>
{children}
</code>
)
},
}}
>
{content}
</Markdown>
)
}
- 방법 A: 빠르고 가벼움. 블로그·문서엔 보통 이걸로 충분
- 방법 B: 라인 번호·줄 강조 등 제어 필요할 때
6. 다 합치기 + 유용한 플러그인
실제 서비스에서는 보통 이렇게 묶어 씁니다.
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import rehypeHighlight from 'rehype-highlight'
import rehypeSlug from 'rehype-slug'
import 'katex/dist/katex.min.css'
import 'highlight.js/styles/github-dark.css'
export default function RichMarkdown({ content }: { content: string }) {
return (
<div className="markdown-body">
<Markdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex, rehypeHighlight, rehypeSlug]}
>
{content}
</Markdown>
</div>
)
}
알아두면 좋은 플러그인:
| 플러그인 | 역할 | 단계 |
rehype-slug |
헤딩에 id 자동 부여 (목차·앵커용) |
rehype |
rehype-autolink-headings |
헤딩에 앵커 링크 추가 | rehype |
rehype-raw |
마크다운 안의 생 HTML 렌더 허용 | rehype |
rehype-sanitize |
XSS 방지용 HTML 정제 | rehype |
7. 꼭 기억할 주의사항
- CSS import를 잊지 마세요 — KaTeX(
katex.min.css), highlight.js(테마 CSS) 둘 다 없으면 "왜 안 꾸며지지?" 하게 됩니다. - 플러그인 순서 —
remark-math(remark)가 있어야rehype-katex(rehype)가 렌더할 게 생깁니다. remark → rehype 흐름을 기억하세요. - 보안 — 사용자 입력 마크다운을 그대로 렌더한다면,
rehype-raw로 생 HTML을 허용할 때 반드시rehype-sanitize를 함께 써서 XSS를 막으세요. - v9 변경점 —
<Markdown className>이 제거됐습니다. 래퍼 div로 감싸세요.
마무리
react-markdown은 플러그인만 잘 조합하면 수식·코드·표·목차까지 갖춘 꽤 완성도 높은 렌더러가 됩니다. 저도 제 사이드 프로젝트(jay-project.kr)의 여러 도구에서 remark + rehype + KaTeX 조합을 실제로 쓰고 있는데, 한 번 세팅해두면 두고두고 재활용하기 좋더라고요.
필요한 기능부터 하나씩 붙여보세요. 도움이 되셨길 바랍니다. 참고하세요! 🙂
'React' 카테고리의 다른 글
| Zustand를 활용한 React 상태 관리 (0) | 2024.12.31 |
|---|---|
| React 환경에서 Markdown 출력하기 (react-markdown) (0) | 2024.04.22 |
| Next.js v14 (0) | 2023.10.31 |
| React v18 (1) | 2023.10.30 |
| [React] 이벤트 리스너에서 현재 상태가져오기 (0) | 2023.06.20 |