Markdown 파일을 HTML 파일로 변환하기

Markdown으로 글을 쓰고, 웹이나 앱에는 HTML만 넣고 싶을 때가 있습니다. 이 글에서는 Python + docutils + myst-parser + BeautifulSoup 으로 MyST Markdown을 HTML로 바꾸는 방법을 정리합니다. 스타일은 직접 관리하고, 본문 HTML만 뽑아 쓰는 흐름을 기준으로 합니다.

Markdown이란?

Markdown은 텍스트 기반 마크업 언어로, 2004년 존 그루버(John Gruber)가 제안했습니다. 특수기호와 문자를 쓴 간단한 문법으로 읽고 쓰기 쉽고, HTML로 변환하기에도 적합합니다.

다만 “Markdown”이라고 해도 방언이 많습니다. 이 글에서는 Sphinx 없이도 기술 문서용 확장을 쓸 수 있는 MyST(Markedly Structured Text) 문법을 기준으로 합니다. 문법·확장 목록은 MyST-Parser 문서를 보면 됩니다.

준비하기

다음 라이브러리를 설치합니다. BeautifulSoup은 PyPI 패키지명이 beautifulsoup4이므로 이쪽을 권장합니다. (bs4는 같은 패키지를 가리키는 메타 패키지입니다.)

Bash

pip install docutils myst-parser beautifulsoup4 pygments

라이브러리

역할

docutils

문서 트리를 HTML5 등으로 출력

myst-parser

MyST Markdown → docutils 파서

beautifulsoup4

변환된 HTML DOM 가공

pygments

코드 블록 구문 강조용 CSS·토큰

참고 링크:

스크립트를 짜기 전에 빠르게 결과만 보고 싶다면, myst-parser가 제공하는 CLI도 있습니다.

Bash

myst-docutils-html5 input.md -o output.html

Sphinx 전용 role/directive는 이 경로에서는 동작하지 않습니다. 커스텀으로 <head>를 다루거나 태그를 손보려면 아래처럼 Python 스크립트가 더 낫습니다.

Python 스크립트 작성

라이브러리 설치 후, publish_string에 MyST Parser를 넘기면 HTML을 얻을 수 있습니다. 아래 예는 ​파일에서 Markdown을 읽고, docutils 기본 스타일시트 임베드를 끄고, 필요하면 <head>를 제거한 뒤 저장합니다.

Python

from pathlib import Path from bs4 import BeautifulSoup from docutils.core import publish_string from myst_parser.docutils_ import Parser MYST_EXTENSIONS = [ "colon_fence", "dollarmath", "attrs_inline", "deflist", "fieldlist", "attrs_block", ] def md_to_html(source: str, *, strip_head: bool = True) -> str: output = publish_string( source=source, writer_name="html5", settings_overrides={ "myst_enable_extensions": MYST_EXTENSIONS, "embed_stylesheet": False, # 기본 CSS 링크/임베드 억제 "output_encoding": "unicode", # bytes 대신 str }, parser=Parser(), ) if not strip_head: return output soup = BeautifulSoup(output, "html.parser") if soup.head is not None: soup.head.extract() return str(soup) if __name__ == "__main__": md_path = Path("example.md") html_path = Path("converted.html") html = md_to_html(md_path.read_text(encoding="utf-8")) html_path.write_text(html, encoding="utf-8") print(f"wrote {html_path.resolve()}")

문자열로 바로 시험하려면 source에 Markdown 본문을 넣으면 됩니다.

Python

source = """ # 제목 문단과 `인라인 코드`, 그리고 수식 $E=mc^2$ 예제입니다. """ print(md_to_html(source)[:300])

스크립트에서 알아둘 점

  1. parser 로 myst-parser를 쓰기 때문에 myst_enable_extensions로 선택 문법을 켭니다. 항목은 optional syntax에서 확인할 수 있습니다.

  2. 기본 HTML writer는 <head>에 docutils 스타일 링크를 넣을 수 있습니다. 배포 경로에 맞지 않는 로컬 CSS 경로가 박히기도 해서, 스타일을 따로 관리할 때는 embed_stylesheet: False를 쓰는 편이 낫습니다.

  3. 그래도 <head>가 남아 거슬리면 BeautifulSoup으로 제거하면 됩니다. soup.head가 없을 수 있으니 is not None을 확인하세요.

  4. BeautifulSoup으로는 클래스 추가, 특정 태그 래핑, 스크립트/링크 삽입 같은 후처리도 할 수 있습니다.

output_encoding: "unicode"를 넣으면 .decode("utf-8")이 필요 없습니다. 이 설정을 빼면 publish_stringbytes를 반환합니다.

style 적용

<head>를 제거했거나 기본 CSS를 끈 상태라면, 브라우저에서 보면 거의 스타일이 없는 HTML입니다. MyST/docutils 계열 문서와 비슷한 느낌을 내려면 markdown-it-docutils가 제공하는 CSS를 쓰면 됩니다. (Furo 테마를 참고한 스타일이며, CSS 변수로 커스터마이즈할 수 있습니다.)

Html

<link rel="stylesheet" type="text/css" media="screen" href="https://unpkg.com/markdown-it-docutils/dist/css/style.min.css" />

이 CSS를 ​이미 변환된 HTML 에 연결하는 용도입니다. 같은 패키지 README에 나오는 아래 스크립트는 ​브라우저에서 Markdown 문자열을 다시 렌더 할 때 쓰는 예시입니다. Python으로 HTML을 만든 뒤에는 보통 필요하지 않습니다.

Html

<!-- 브라우저에서 MD를 직접 렌더할 때만 사용 --> <script src="https://cdn.jsdelivr.net/npm/markdown-it@12/dist/markdown-it.min.js"></script> <script src="https://unpkg.com/markdown-it-docutils"></script>

코드 블록 하이라이트 (Pygments)

Markdown에 코드 펜스가 있으면 변환 결과에는 대개 <pre class="code ..."><code>...</code></pre> 형태가 됩니다. 위의 markdown-it-docutils CSS만으로는 토큰 색이 충분히 안 나올 수 있습니다. MyST/docutils는 코드 강조에 Pygments를 쓰므로, 테마 CSS를 따로 뽑아 넣으면 됩니다.

Bash

pip install pygments # colorful 테마, 선택자 접두사를 .code 로 생성 pygmentize -f html -S colorful -a .code
  • -S 뒤는 Pygments builtin style 이름입니다. (monokai, github-dark 등)

  • -a .code는 생성되는 선택자 접두사입니다. 변환 HTML의 <pre class="code">에 맞춘 값입니다.

출력된 CSS를 파일로 저장해 페이지에 함께 링크하면 됩니다.

Bash

pygmentize -f html -S colorful -a .code > pygments-colorful.css

Html

<link rel="stylesheet" href="pygments-colorful.css" />

dollarmath 확장을 켰다면 HTML에는 $...$ / $$...$$가 남을 수 있습니다. 수식을 브라우저에서 보이게 하려면 MathJax나 KaTeX를 페이지에 추가해야 합니다.

실무에서 자주 걸리는 점

증상

원인·대응

스타일이 거의 없음

embed_stylesheet: False 또는 <head> 제거 후 외부 CSS 미연결

코드 색이 안 입혀짐

Pygments CSS 미적용, 또는 -a 접두사가 HTML class와 불일치

수식이 그대로 보임

MathJax/KaTeX 미로드 (dollarmath 사용 시)

Sphinx directive가 안 됨

docutils 단독 경로의 한계 → Sphinx 빌드 또는 대체 문법 사용

bytes / str 혼동

output_encoding: "unicode" 권장

정리

Markdown → HTML을 Python으로 처리할 때는 myst-parser로 파싱하고, docutils로 HTML5를 만든 뒤, BeautifulSoup으로 필요한 부분만 다듬는 구성이 실무에서 다루기 쉽습니다. 빠른 확인은 myst-docutils-html5, 스타일·DOM 제어가 필요하면 이 글의 스크립트 경로를 쓰면 됩니다.

직접 스타일을 가져가려면 markdown-it-docutils CSS와 Pygments CSS를 조합해 보시고, 수식·다이어그램처럼 브라우저 쪽 렌더러가 필요한 확장만 골라 추가해 보세요.

On this page