MDX vs MD 기반 블로그: 렌더링 성능 차이와 선택 기준
MDX 기반 문서 포털(Fumadocs + Next.js)을 Docker Compose로 운영하면서 겪은 렌더링 성능 문제를 분석하고, MD 기반 대안과 비교한다. 결론적으로 VitePress로의 전환을 결정한 과정을 정리한다.
[01] 문제 상황
Fumadocs(Next.js + MDX) 기반 문서 포털을 Docker Compose로 실행한 뒤, .md 파일 하나를 수정하고 브라우저에서 반영을 확인하려고 했다.
기대: 파일 저장 → 1~2초 내 브라우저 반영
현실: 파일 저장 → 30초 이상 대기
graph LR
EDIT[".md 파일 수정"] -->|기대: 1~2초| BROWSER["브라우저 반영"]
EDIT -->|현실: 30초+| WAIT["⏳ 대기..."]
WAIT --> BROWSER
style EDIT fill:#e3f2fd,stroke:#1565c0
style WAIT fill:#ffcccc,stroke:#c62828
style BROWSER fill:#e8f5e9,stroke:#2e7d32
문서 한 줄 수정하고 30초를 기다리는 것은 개발 생산성을 심각하게 저하시킨다. 왜 이렇게 느린지 원인을 분석했다.
[02] MDX 렌더링이 느린 이유
2-1. MDX 빌드 파이프라인의 복잡성
MDX는 Markdown + React의 하이브리드다. 단순히 마크다운을 HTML로 변환하는 것이 아니라, 6단계 파이프라인을 거쳐야 한다.
graph TD
A[".mdx 파일 변경"] --> B["[1] Fumadocs-mdx 스캔
.mdx 파싱, frontmatter 추출
.source/ 재생성 (84+ imports)"]
B --> C["[2] Remark/Rehype
플러그인 체인 처리"]
C --> D["[3] TypeScript 컴파일
JSX 변환, 타입 체크"]
D --> E["[4] Webpack/Turbopack
번들링, 코드 스플리팅"]
E --> F["[5] Next.js SSG
페이지별 HTML 생성"]
F --> G["[6] 브라우저 반영 (HMR)
약 15~30초 소요"]
style A fill:#e3f2fd,stroke:#1565c0
style G fill:#ffcccc,stroke:#c62828
반면 순수 Markdown(.md)은:
graph LR
A[".md 파일 변경"] --> B["Markdown → HTML 변환"]
B --> C["브라우저 반영
~100~300ms"]
style A fill:#e3f2fd,stroke:#1565c0
style C fill:#e8f5e9,stroke:#2e7d32
핵심 차이: MDX는 React 컴포넌트 변환, TypeScript 컴파일, 번들링이 필수다. MD는 텍스트 → HTML 변환만 하면 끝이다.
2-2. Next.js Dev 모드의 JIT 오버헤드
Next.js 개발 모드에서는 페이지 요청마다 on-the-fly 컴파일을 수행한다.
sequenceDiagram
participant User as 브라우저
participant Next as Next.js Dev Server
participant MDX as MDX 컴파일러
participant TS as TypeScript
participant WP as Webpack
User->>Next: 페이지 요청
Next->>MDX: .mdx 파일 로드
MDX->>MDX: frontmatter 추출
MDX->>TS: JSX → JS 변환
TS->>WP: 번들링 요청
WP->>WP: 코드 스플리팅
WP-->>Next: 번들 완료
Next-->>User: HTML 응답
Note over User,WP: 이 전체 과정이 매 요청마다 반복
프로덕션 최적화(Minification, Code Splitting, Tree Shaking)도 Dev 모드에서는 비활성화 상태라 더 느리다.
2-3. Docker 환경의 추가 오버헤드
Docker Compose로 실행하면 성능이 더 악화된다.
graph TD
subgraph "로컬 개발 (~5ms)"
L1["호스트 SSD"] -->|직접 접근| L2["Node.js"]
end
subgraph "Docker 개발 (~50~100ms+)"
D1["호스트 SSD"] -->|FUSE/VPKit| D2["Docker 데몬"]
D2 -->|가상화 계층| D3["컨테이너 Node.js"]
end
style L2 fill:#e8f5e9,stroke:#2e7d32
style D3 fill:#ffcccc,stroke:#c62828
| 병목 지점 | 로컬 | Docker |
|---|---|---|
| 파일 I/O | ~5ms (SSD 직접) | ~50~100ms+ (볼륨 마운트) |
| 파일 변경 감지 | OS 이벤트 (즉시) | 폴링 방식 (+1초) |
| 네트워크 | 직접 | 컨테이너 포트 변환 (+10~20ms) |
| node_modules 접근 | 로컬 | 호스트 마운트 시 10배 느림 |
특히 볼륨 마운트 I/O가 치명적이다. node_modules의 수천 개 파일을 매번 가상화 계층을 통해 접근하면, 체감 성능이 크게 떨어진다.
[03] MD vs MDX 성능 비교
같은 문서를 MD 기반과 MDX 기반으로 처리할 때의 성능 차이다.
graph LR
subgraph MD["MD 기반 서버 (~120ms)"]
direction LR
M1["파일 감지
100ms"] --> M2["MD→HTML
10ms"]
M2 --> M3["템플릿
5ms"]
M3 --> M4["파일 쓰기
5ms"]
end
subgraph MDX["MDX 기반 Docker (~21.7초)"]
direction LR
X1["파일 감지
500ms"] --> X2["mdx 스캔
200ms"]
X2 --> X3[".source/
2초"]
X3 --> X4["TS 컴파일
5초"]
X4 --> X5["번들링
8초"]
X5 --> X6["SSG
5초"]
X6 --> X7["Docker I/O
500ms"]
end
style MD fill:#e8f5e9,stroke:#2e7d32
style MDX fill:#ffcccc,stroke:#c62828
| 구분 | MD 기반 | MDX 기반 (Docker) |
|---|---|---|
| 파일 감지 | 100ms | 500ms |
| 변환 처리 | 15ms | 15,200ms |
| 기타 오버헤드 | 5ms | 6,000ms |
| 총 소요 | ~120ms | ~21.7초 (실제 30초+) |
~180배 차이. 문서 한 줄 수정에 30초는, 생산성이 아니라 인내심 테스트다.
[04] 대안 솔루션 비교
4-1. 카테고리별 분류
graph TD
ROOT["문서 사이트 도구"] --> RT["런타임 렌더링
(빌드 제로)"]
ROOT --> FAST["빠른 SSG
(1초 이내)"]
ROOT --> SLOW["MDX 기반 SSG
(15초 이상)"]
RT --> Docsify["Docsify
0초"]
RT --> Wiki["Wiki.js
실시간"]
FAST --> Hugo["Hugo (Go)
~50ms"]
FAST --> mdBook["mdBook (Rust)
~100ms"]
FAST --> VP["VitePress (Vite)
~200ms"]
FAST --> MK["MkDocs (Python)
~300ms"]
SLOW --> Fuma["Fumadocs
15~30초"]
SLOW --> Docu["Docusaurus
20~40초"]
SLOW --> Nextra["Nextra
10~20초"]
style RT fill:#e8f5e9,stroke:#2e7d32
style FAST fill:#e3f2fd,stroke:#1565c0
style SLOW fill:#ffcccc,stroke:#c62828
style VP fill:#ffffcc,stroke:#f9a825,stroke-width:3px
4-2. 성능 비교 테이블
| 솔루션 | 엔진 | 재빌드 시간 | 컴포넌트 | SEO | 특징 |
|---|---|---|---|---|---|
| Hugo | Go | ~50ms | 제한적 | O | 가장 빠른 SSG |
| mdBook | Rust | ~100ms | 제한적 | O | Rust 공식 문서 사용 |
| VitePress | Vue/Vite | ~200ms | Vue | O | HMR 즉시 반영 |
| MkDocs Material | Python | ~300ms | 제한적 | O | 기술문서 인기 1위 |
| Docsify | JS (브라우저) | 0초 | X | X | 빌드 불필요 |
| Nextra | Next.js | 10~20초 | React | O | MDX 중 상대적 빠름 |
| Fumadocs | React | 15~30초 | React | O | 현재 사용 중 |
| Docusaurus | React | 20~40초 | React | O | Meta 주도 |
[05] 왜 VitePress인가
5-1. 선택 기준
graph TD
Q1{"파일 수정 후
즉시 반영 필요?"}
Q1 -->|Yes| Q2{"React 컴포넌트
반드시 필요?"}
Q1 -->|No| KEEP["현재 스택 유지
(최적화 필수)"]
Q2 -->|Yes| NEXTRA["Nextra 검토
(MDX 중 가장 빠름)"]
Q2 -->|No| Q3{"모던 개발 경험
(TS, HMR) 필요?"}
Q3 -->|Yes| VITEPRESS["✅ VitePress"]
Q3 -->|No| MKDOCS["MkDocs / Hugo"]
style Q1 fill:#fff3e0,stroke:#e65100
style VITEPRESS fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px
style KEEP fill:#ffcccc,stroke:#c62828
| 질문 | 답 | 결과 |
|---|---|---|
| 파일 수정 후 즉시 반영(1초 이내)이 필요한가? | Yes | MDX 계열 탈락 |
| React 커스텀 컴포넌트가 반드시 필요한가? | No | Vue 컴포넌트로 대체 가능 |
| 모던 개발 경험(TypeScript, HMR)이 필요한가? | Yes | Hugo, MkDocs 대비 우위 |
5-2. Fumadocs vs VitePress 비교
| 항목 | Fumadocs (현재) | VitePress (전환 대상) |
|---|---|---|
| 재빌드 시간 | 15~30초 (Docker: 30초+) | ~200ms |
| HMR | 느림 (전체 빌드 경유) | 즉시 (Vite 네이티브) |
| 컴포넌트 | React (MDX 필수) | Vue 3 (선택적) |
| 프로덕션 빌드 | 수 분 | 1초 미만 |
| 설정 복잡도 | 높음 (Next.js + Fumadocs) | 낮음 |
| Docker 호환성 | 볼륨 마운트 병목 | 가벼워서 영향 적음 |
5-3. VitePress 기본 구조
1
2
npm create vitepress
npm run docs:dev
1
2
3
4
5
6
7
8
9
docs/
├── .vitepress/
│ └── config.ts # 사이트 설정
├── index.md # 메인 페이지
├── guide/
│ ├── getting-started.md
│ └── advanced.md
└── api/
└── reference.md
1
2
3
4
5
6
7
8
9
10
11
// docs/.vitepress/config.ts
export default {
title: 'Tech Docs Portal',
lang: 'ko-KR',
themeConfig: {
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/' }
]
}
}
[06] 핵심 교훈
6-1. MDX가 필요한 경우
- 문서 안에 인터랙티브 React 컴포넌트가 필수일 때
- 디자인 시스템 문서처럼 라이브 코드 프리뷰가 필요할 때
- React 기반 프로젝트의 스토리북 대체 용도로 사용할 때
6-2. MD 기반이 더 나은 경우 (대부분)
- 기술 문서, API 레퍼런스, 가이드 등 텍스트 중심 콘텐츠
- 파일 수정 후 즉시 확인이 중요한 워크플로우
- Docker 환경에서 개발/배포하는 경우
- 팀원 중 프론트엔드 전문가가 아닌 사람이 문서를 작성하는 경우
6-3. 판단 기준
graph TD
Q{"문서에 React
인터랙션이
꼭 필요한가?"}
Q -->|Yes| MDX["MDX 기반
(Fumadocs, Docusaurus)
⚠️ 30초+ 빌드 감수"]
Q -->|No| MD["MD 기반
(VitePress, MkDocs, Hugo)
✅ 200~300ms 즉시 반영"]
style Q fill:#fff3e0,stroke:#e65100
style MDX fill:#ffcccc,stroke:#c62828
style MD fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px
대부분의 기술 문서는 텍스트 + 코드 블록 + 이미지로 충분하다. “혹시 나중에 React 컴포넌트가 필요할지도” 라는 이유로 MDX를 선택하면, 매일 30초씩 대기하는 비용을 치르게 된다.