mermaid는 렌더링해 주는 곳에서만 그림이 돼요. GitHub README와 이 블로그에서는 도식으로 보이지만, PDF로 내보내면 코드 블록 그대로 남고, 사내 위키에 붙이면 텍스트 뭉치가 돼요. 슬라이드에 넣으려면 결국 캡처 도구를 꺼내야 해요.

mmdc는 이 지점을 없애는 도구입니다. mermaid 문법을 입력받아 SVG, PNG, PDF 파일로 저장하는 공식 커맨드라인 도구이고, mermaid 개발팀이 직접 관리합니다. 2026년 7월 기준 버전은 11.16.0입니다.

무엇을 하는 도구인가

내부는 단순합니다. Puppeteer로 headless Chromium을 띄우고, 그 안에서 mermaid 라이브러리로 다이어그램을 그린 다음, 결과 SVG를 꺼내거나 스크린샷을 찍습니다.

flowchart TD
    SRC["diagram.mmd
또는 .md"] MMDC["mmdc"] CHROME["headless
Chromium"] MER["mermaid 11.x"] OUT["svg / png / pdf"] SRC --> MMDC MMDC --> CHROME CHROME --> MER MER --> OUT

브라우저를 통째로 띄운다는 사실이 장점이자 단점입니다. 웹에서 보이는 그림과 결과물이 정확히 같다는 게 장점이고, 실행 한 번에 Chromium이 뜨니 가볍지 않다는 게 단점입니다.

설치

Node 18.19 이상 또는 20 이상이 필요합니다. 전역 설치는 이렇게 합니다.

npm install -g @mermaid-js/mermaid-cli
mmdc --version

한두 번 쓸 목적이라면 설치 없이 실행해 봅니다.

npx -p @mermaid-js/mermaid-cli mmdc -i diagram.mmd -o diagram.svg

CI나 로컬 환경에 의존성을 남기고 싶지 않으면 Docker 이미지를 씁니다.

docker run --rm -u $(id -u):$(id -g) -v "$PWD:/data" \
  ghcr.io/mermaid-js/mermaid-cli/mermaid-cli \
  -i /data/diagram.mmd -o /data/diagram.svg

-u $(id -u):$(id -g)를 빼면 결과 파일이 root 소유로 생깁니다. 컨테이너에서 파일을 만드는 도구의 흔한 함정입니다.

Homebrew 설치는 지원이 끊겼습니다. brew install mermaid-cli로 기억하고 있다면 npm 또는 Docker로 옮겨야 합니다.

첫 렌더

.mmd 파일 하나를 만듭니다.

flowchart TD
    A[요청] --> B{캐시 있나}
    B -- 있음 --> C[캐시 응답]
    B -- 없음 --> D[DB 조회]
    D --> E[캐시 저장]
    E --> C

변환합니다.

mmdc -i flow.mmd -o flow.svg

출력 확장자가 형식을 결정합니다. flow.png로 주면 PNG, flow.pdf로 주면 PDF가 나옵니다. 확장자와 무관하게 형식을 정하려면 -e svg 같은 식으로 명시합니다.

파일을 만들지 않고 파이프로 넘기는 방식도 됩니다.

cat << 'EOF' | mmdc -i - -o out.png
sequenceDiagram
    Client->>Server: 요청
    Server-->>Client: 응답
EOF

주요 옵션

옵션기본값용도
-i, --input필수입력 파일. .md는 마크다운으로 처리, -는 stdin
-o, --output입력 + .svg출력 파일. -는 stdout
-e, --outputFormat확장자에서 추론svg, png, pdf 중 선택
-t, --themedefaultdefault, forest, dark, neutral
-b, --backgroundColorwhitetransparent, #F0F0F0
-w, --width800페이지 폭 (px)
-H, --height600페이지 높이 (px)
-s, --scale1Puppeteer 배율. PNG 해상도를 올릴 때
-c, --configFile없음mermaid 설정 JSON
-C, --cssFile없음페이지에 주입할 CSS
-p, --puppeteerConfigFile없음Puppeteer 설정 JSON
-f, --pdfFit꺼짐PDF를 도식 크기에 맞춤
-j, --jobsCPU 절반다이어그램 여러 개를 병렬 렌더링
-q, --quiet꺼짐로그 억제

발표 자료에 넣을 PNG라면 배율과 배경을 함께 조정하는 조합이 무난합니다.

mmdc -i arch.mmd -o arch.png -t dark -b transparent -s 3

-s 3은 같은 도식을 3배 해상도로 그립니다. 슬라이드에서 확대해도 글자가 깨지지 않습니다.

마크다운 파일 일괄 변환

.md 파일을 입력으로 주면 동작이 달라집니다. 파일 안의 mermaid 코드 블록을 모두 찾아 각각 이미지로 렌더링하고, 원본 블록을 이미지 참조로 바꾼 새 마크다운을 내놓습니다.

mmdc -i README.md -o README-rendered.md

이미지는 README-rendered-1.svg, README-rendered-2.svg 같은 이름으로 순번이 붙고, 마크다운 안에는 ![diagram](README-rendered-1.svg) 형태로 들어갑니다. 이미지를 별도 디렉토리에 모으려면 --artefacts를 지정합니다.

mmdc -i README.md -o dist/README.md -a dist/images

mermaid를 렌더링하지 못하는 목적지에 문서를 올려야 할 때 쓰는 기능입니다. 사내 위키, 정적 PDF 매뉴얼, 오래된 문서 시스템이 대표적입니다. 원본은 mermaid 코드로 관리하고 배포할 때만 이미지로 바꿔 내보내는 흐름이 만들어집니다.

설정 파일 세 가지

옵션 플래그로 부족할 때 JSON 또는 CSS 파일을 붙입니다.

-c로 넘기는 mermaid 설정은 live editor의 Config 탭과 같은 내용입니다. 폰트, 곡선 스타일, ELK 레이아웃 사용 여부를 여기서 정합니다.

{
  "theme": "neutral",
  "themeVariables": { "fontFamily": "Noto Sans KR" },
  "flowchart": { "curve": "basis" }
}

-C로 넘기는 CSS는 렌더링 페이지에 그대로 주입됩니다. mermaid가 테마에서 만들어 넣는 스타일을 덮어써야 하니 !important가 필요한 경우가 잦습니다.

-p로 넘기는 Puppeteer 설정은 실행 환경 문제를 푸는 열쇠입니다. 컨테이너나 CI에서 Chromium이 sandbox 때문에 죽으면 이렇게 우회합니다.

{ "args": ["--no-sandbox", "--disable-dev-shm-usage"] }

--disable-dev-shm-usage/dev/shm이 작게 잡힌 컨테이너에서 렌더링이 중간에 끊기는 문제를 막아 줍니다. Docker 기본값 64MB로는 큰 도식을 그리다 공유 메모리가 부족해집니다.

CI에서 문법 검증기로 쓰기

이미지 생성이 아니라 검증 목적으로도 씁니다. mermaid 문법이 깨졌으면 mmdc는 0이 아닌 종료 코드를 남깁니다. 문서에 도식이 많은 저장소라면 PR 단계에서 걸러냅니다.

#!/usr/bin/env bash
set -euo pipefail

fail=0
while IFS= read -r f; do
  if ! mmdc -q -i "$f" -o /tmp/check.svg 2>/tmp/err; then
    echo "FAIL: $f"
    cat /tmp/err
    fail=1
  fi
done < <(git ls-files '*.md' | xargs grep -l '```mermaid')

exit "$fail"

이 블로그에는 활용처가 하나 더 있습니다. mermaid 도식이 본문 폭 720px 안에 들어가는지는 브라우저를 띄워 눈으로 확인해 왔는데, -w 720으로 렌더링한 SVG의 viewBox 값을 읽으면 폭 초과를 숫자로 판정할 길이 열립니다. 노드 라벨 길이 규칙을 지켰는지 사람이 세지 않아도 되는 셈입니다.

걸리는 지점들

한글이 사라지는 문제가 첫 관문입니다. 공식 Docker 이미지에는 font-noto-cjk가 들어 있어서 한글이 그려지지만, 직접 만든 슬림 이미지나 폰트 없는 CI 러너에서는 한글 라벨이 빈칸이나 두부(□)로 나옵니다. 렌더링 환경에 CJK 폰트를 깔았는지부터 확인합니다.

RUN apk add --no-cache font-noto-cjk font-noto-emoji

실행 비용도 무시하기 어렵습니다. 다이어그램마다 Chromium을 띄우는 구조라 수십 개를 순차 처리하면 시간이 꽤 듭니다. 마크다운 입력에서 도식이 여러 개면 -j로 병렬 처리하니, CPU 여유가 있는 CI에서는 이 값을 올려 잡습니다.

CSP를 켜 둔 환경에서 인라인 CSS가 막히는 사례도 보고돼 있습니다. -C로 준 스타일이 적용되지 않으면 이쪽을 의심합니다.

Node와 Chromium을 동시에 요구하니 의존성이 가볍지 않다는 점은 그대로 남습니다. Python만 있는 파이프라인이라면 브라우저 없이 도는 mermaidx 같은 대안을 검토하는 편이 낫습니다. 다만 mermaid 최신 문법 지원 범위는 공식 CLI가 앞섭니다.

언제 쓰고 언제 안 쓰나

도식 한두 개를 급하게 이미지로 받고 싶으면 mermaid live editor에서 다운로드 버튼을 누르는 편이 빠릅니다. 여러 서비스가 섞인 팀 문서 파이프라인이라면 Kroki 같은 렌더 서버를 세우는 선택지도 있습니다.

mmdc가 제 몫을 하는 자리는 반복과 자동화예요. 문서 저장소에 mermaid를 원본으로 두고 배포 때 이미지로 바꿔 내보내는 흐름, PR마다 도식 문법을 검사하는 파이프라인, 슬라이드용 고해상도 PNG를 스크립트 한 줄로 뽑는 작업이 여기 해당해요. 매번 마우스로 저장 버튼을 누르던 일이 커맨드가 되는 순간부터 제값을 해요.

참고