바로 보는 구조
Markdown source
-> parse
-> mdast
-> remark plugins
-> remark-rehype
-> hast
-> rehype plugins
-> HTML / React components처리 단계
Markdown은 바로 HTML이 아니라 syntax tree로 해석된다
현대 문서 사이트는 Markdown 원문을 바로 문자열 치환하지 않는다. 먼저 parser가 Markdown을 syntax tree로 만든 뒤, plugin이 그 tree를 검사하거나 변환하고, 마지막에 HTML 또는 React element로 출력한다. unified 생태계에서는 Markdown 쪽 tree를 mdast, HTML 쪽 tree를 hast로 구분한다.
const file = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.process(markdown)remark plugin은 Markdown tree를 다룬다
remark 단계에서는 heading, paragraph, list, code, link 같은 Markdown 구조를 다룬다. heading slug 생성, table of contents 생성, linting, GFM 확장 처리처럼 Markdown 의미를 기준으로 움직이는 작업이 여기에 맞다. 이 단계에서 HTML DOM을 직접 다룬다고 생각하면 plugin 적용 위치가 어긋난다.
rehype plugin은 HTML tree를 다룬다
remark-rehype를 지나면 Markdown tree가 HTML tree로 바뀐다. 이후 rehype plugin은 h1, a, pre, code, img 같은 HTML 요소를 대상으로 동작한다. 코드 하이라이트, heading anchor 삽입, sanitize, HTML 속성 보정은 주로 이 단계에 둔다.
raw HTML과 MDX는 경계가 더 복잡하다
Markdown 안의 raw HTML은 mdast에서는 HTML node로 남아 있다가, rehype 단계에서 어떻게 처리할지 결정된다. 안전하지 않은 HTML을 그대로 출력하면 XSS 위험이 생기므로 allowDangerousHtml, rehype-raw, sanitize 정책을 명확히 구분해야 한다. MDX는 여기에 JSX와 expression까지 들어가므로 일반 Markdown보다 빌드 실패와 보안 경계가 더 많다.
어디에 둘까
| 작업 | 적합한 단계 |
|---|---|
| GFM table/task list 해석 | remark plugin |
| 문서 목차 생성 | remark 또는 rehype, 기준 통일 |
| 코드 하이라이트 | rehype 또는 렌더러 컴포넌트 |
| HTML sanitize | rehype 단계 |
| MDX component 매핑 | MDX 컴파일/렌더 단계 |
공식 참고: Using unified, remark-rehype
주의할 점
plugin이 어느 tree를 입력으로 받는지 확인하지 않고 붙이면 동작하지 않거나 문서가 깨질 수 있습니다. remark plugin은 mdast, rehype plugin은 hast를 대상으로 한다는 경계를 먼저 확인하세요.
실패 예시
- rehype 단계 뒤에 remark-gfm을 붙임
- 결과: GFM table/task list가 기대대로 해석되지 않음
- 대응: GFM 확장은 Markdown parse 직후 remark 단계에 둔다참고 링크
2 sources