재하의 개발 블로그
TIL 5 분 소요

Mermaid 렌더링 안 되는 문제 — allowDangerousHtml 설정

mermaid remark rehype markdown troubleshooting

문제 발견

Supabase에서 가져온 블로그 게시글에 Mermaid 다이어그램을 추가했는데, 브라우저에서 렌더링되지 않았다. 데이터베이스에는 Mermaid 코드 블록이 정상적으로 저장되어 있었다.

```mermaid
flowchart LR
    A[Start] --> B[Process]
    B --> C[End]
```

증상

Playwright로 페이지를 확인했을 때:

  • <pre class="mermaid"> 요소: 0개
  • .mermaid-diagram div: 0개
  • 렌더링된 SVG: 0개

하지만 서버 로그에는 Mermaid 블록을 찾았다는 메시지가 출력되고 있었다:

[remarkMermaid] Found 3 mermaid blocks, isMdx: false

원인 파악

remarkMermaid 플러그인은 Markdown의 Mermaid 코드 블록을 HTML로 변환한다. 비-MDX 모드에서는 다음과 같은 HTML 노드를 생성한다:

// src/lib/remark-mermaid.ts
const htmlNode = {
  type: "html",
  value: `<div class="mermaid-diagram my-6 flex justify-center">
    <pre class="mermaid">${escapeHtml(code)}</pre>
  </div>`,
};

문제는 src/lib/markdown-processor.ts의 unified 파이프라인 설정이었다:

.use(remarkMermaid)
.use(remarkCallout)
.use(remarkRehype, { allowDangerousHtml: false }) // ❌ 여기가 문제
.use(rehypePrettyCode, {
  // ...
})
.use(rehypeRaw)
.use(rehypeStringify)

remarkRehypeallowDangerousHtml 옵션이 false로 설정되어 있어서, remarkMermaid가 생성한 HTML 노드가 제거되고 있었다.

해결

allowDangerousHtmltrue로 변경했다:

.use(remarkMermaid)
.use(remarkCallout)
.use(remarkRehype, { allowDangerousHtml: true }) // ✅ 수정
.use(rehypePrettyCode, {
  // ...
})
.use(rehypeRaw)
.use(rehypeStringify)

작동 원리

  1. remarkMermaid: Mermaid 코드 블록을 HTML 노드로 변환
  2. remarkRehype: Markdown AST를 HTML AST로 변환
    • allowDangerousHtml: true — HTML 노드를 보존
  3. rehypeRaw: 보존된 HTML 문자열을 파싱하여 실제 HTML 노드로 변환
  4. rehypePrettyCode: 코드 블록 문법 강조 (Mermaid HTML은 건드리지 않음)
  5. rehypeStringify: 최종 HTML 문자열 생성

검증

수정 후 Playwright로 확인:

{
  mermaidPreElements: 3,      // ✅
  mermaidDiagramDivs: 3,      // ✅
  renderedSvgDiagrams: 3      // ✅
}

클라이언트 사이드 mermaid.js가 <pre class="mermaid"> 요소를 찾아서 인터랙티브 SVG 다이어그램으로 변환했다.

배운 점

remarkRehype의 allowDangerousHtml

allowDangerousHtml 옵션은 이름이 위험해 보이지만, remark 플러그인이 생성한 HTML을 보존하는 데 필요하다.

  • false (기본값): HTML 노드를 제거 — XSS 공격 방지
  • true: HTML 노드를 보존 — 커스텀 플러그인의 HTML 출력 허용

rehypeRaw의 역할

rehypeRawallowDangerousHtml: true와 함께 사용된다:

  1. remarkRehype가 HTML 문자열을 보존
  2. rehypeRaw가 HTML 문자열을 실제 HTML AST 노드로 파싱

rehypeRaw 없이 allowDangerousHtml: true만 설정하면, HTML이 문자열로만 남아서 제대로 렌더링되지 않는다.

참고 자료