본문 바로가기
문서 정리 프레임워크/Nextra

[Nextra] Mermaid 플러그인에서 발생하는 렌더링 충돌 오류 해결

by minhyeok.lee 2025. 4. 9.
반응형

하나의 .mdx파일에서 Mermaid 2개 사용시 렌더링 오류

Nextra에서 하나의 .mdx파일에서 Mermaid 다이어그램을 2개 사용했을 때 마지막 하나만 출력되는 오류가 발생

 

※ Mermaid란?

2023.06.20 - [문서 정리 프레임워크/Nextra] - [Nextra] FileTree, Steps, Mermaid사용

 

[Nextra] FileTree, Steps, Mermaid사용

Nextra에서 FileTree, Steps, Mermaid사용FileTree 컴포넌트의 사용방법import { FileTree } from "nextra-theme-docs"; 출력결과Steps 컴포넌트의 사용방법import { Steps } from "nextra-theme-docs"; ### Step1Step1의 내용이다.### Step2

kfdd6630.tistory.com

 


문제가 발생하는 이유

1. 이 문제는 Nextra에서 Mermaid 다이어그램이 여러 개 연속해서 선언될 때 발생하는 렌더링 충돌 때문에 발생

2. Mermaid의 SSR 렌더링 타이밍과 Nextra의 마크다운 처리 방식이 충돌하면서 첫 번째 mermaid 블록이 무시되고 마지막 블록만 렌더링되는 문제가 발생


해결 방법?

1. Mermaid 블록 사이에 공백 콘텐츠 <br /> 혹은 "  "삽입

2. Mermaid를 <div>로 감싸 명시적으로 분리
3. Mermaid를 컴포넌트화하여 사용

 

결과

1. 일반적인 해결법인 마크다운 문단 구분, <br/>, <div>, 주석 삽입 모두 효과 없음
2. 3가지 모두 해결되지 않음

 


이유는?

1. Nextra + rehype + mermaid 플러그인의 구조적인 렌더링 버그

2. 현재 Nextra에서는 mermaid 코드 블록을 rehype-plugin이 단 한 번만 처리하기 때문에 두 번째 이후 코드블록만 DOM 상에서 실행되는 현상이 생기는 문제

3. Nextra의 Markdown → HTML 변환기(markdoc, remark, rehype 등) 혹은 Mermaid 플러그인에서 발생하는 렌더링 충돌이 발생


해결 방법

Nextra 공식팀과 커뮤니티에서 가장 권장하는 방식인 <Mermaid> 컴포넌트 사용하여 해결

 


 

1. mermaid 다운로드

# pnpm 사용
pnpm add mermaid
# npm 사용
npm install mermaid

 

2. mermaid 컴포넌트 생성(components/Mermaid.tsx)

'use client'
import { useEffect, useRef } from 'react'
import mermaid from 'mermaid'

mermaid.initialize({ startOnLoad: false })

export default function Mermaid({ chart }: { chart: string }) {
  const ref = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (ref.current) {
      mermaid.render('graph-div', chart).then(({ svg }) => {
        ref.current!.innerHTML = svg
      })
    }
  }, [chart])

  return <div ref={ref} />
}

 

이렇게 사용한다면 Mermaid가 한 페이지에 두 개가 출력되지만 2가지 문제를 해결해야 함

 

1. 두 개의 Mermaid 다이어그램이 겹쳐 나오는 현상

2. Hydration Error 발생


1. 두 개의 Mermaid 다이어그램이 겹쳐 나오는 현상 해결

1의 이유는 mermaid.render('graph-div', ...) → 이 ID가 고정되어 있어 두 개가 같은 DOM 요소를 공유하여 발생

 


해결방법

1. nanoid라는 ID 중복 없이 랜덤 생성해주는 초경량 라이브러리 다운로드

# npm 사용
npm install nanoid
# pnpm 사용
pnpm add nanoid

 

2. nanoid 적용

'use client'
import { useEffect, useRef } from 'react'
import mermaid from 'mermaid'
import { nanoid } from 'nanoid'

mermaid.initialize({ startOnLoad: false })

export default function Mermaid({ chart }: { chart: string }) {
  const renderRef = useRef<HTMLDivElement>(null)
  const idRef = useRef(`mermaid-${nanoid()}`) // 고유 ID 부여

  useEffect(() => {
    if (!renderRef.current) return

    const render = async () => {
      try {
        const { svg } = await mermaid.render(idRef.current, chart)
        renderRef.current!.innerHTML = svg
      } catch (e) {
        console.error("Mermaid render error:", e)
      }
    }

    render()
  }, [chart])

  return <div className="mermaid" ref={renderRef} />
}

 


2. Hydration Error 발생

React + Next.js(Nextra)에서 서버에서 렌더링된 HTML과 클라이언트에서 재렌더링된 UI가 다를 때 발생하는 오류

 


해결방법

Next.js에서 서버 렌더링을 완전히 끄고, 클라이언트에서만 렌더링되도록 해야 함

 

1. components/Mermaid.tsx

'use client'
import { useEffect, useRef } from 'react'
import mermaid from 'mermaid'
import { nanoid } from 'nanoid'

mermaid.initialize({ startOnLoad: false })

export default function Mermaid({ chart }: { chart: string }) {
  const ref = useRef<HTMLDivElement>(null)
  const id = useRef(`mermaid-${nanoid()}`)

  useEffect(() => {
    if (!ref.current) return
    mermaid.render(id.current, chart).then(({ svg }) => {
      ref.current!.innerHTML = svg
    })
  }, [chart])

  return <div ref={ref} />
}

 

2. .mdx에서는 반드시 동적 import 사용(SSR 사용하지않음)

// components/ClientMermaid.tsx
import dynamic from 'next/dynamic'

const Mermaid = dynamic(() => import('./Mermaid'), { ssr: false })

export default Mermaid

 

3. .mdx 사용 예

import Mermaid from '@/components/ClientMermaid'

### 🗓 타임라인

<Mermaid chart={`timeline
  title 마일스톤
  2025-04-10 : 시작
  2025-08-14 : 종료
`} />

---

### 🔁 흐름도

<Mermaid chart={`flowchart TD;
  A --> B;
`} />
반응형

댓글