React

[React] react-markdown 심화: 수학 수식(KaTeX) · 코드 하이라이트 · 유용한 플러그인 총정리

코딩하는 Jay 2026. 6. 4. 16:25
반응형

AI로 생성한 이미지

 

안녕하세요. 코딩하는 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>에 더 이상 className prop을 직접 못 줍니다. 위처럼 바깥 <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.css import 누락 → 수식 모양이 다 깨집니다. (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 (커스터마이징·라인 번호)

라인 번호, 특정 줄 강조 등 세밀한 제어가 필요하면 componentscode를 직접 교체합니다.

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. 꼭 기억할 주의사항

  1. CSS import를 잊지 마세요 — KaTeX(katex.min.css), highlight.js(테마 CSS) 둘 다 없으면 "왜 안 꾸며지지?" 하게 됩니다.
  2. 플러그인 순서remark-math(remark)가 있어야 rehype-katex(rehype)가 렌더할 게 생깁니다. remark → rehype 흐름을 기억하세요.
  3. 보안 — 사용자 입력 마크다운을 그대로 렌더한다면, rehype-raw로 생 HTML을 허용할 때 반드시 rehype-sanitize를 함께 써서 XSS를 막으세요.
  4. 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