← 모두의 툴

HTML→Markdown 변환, 구조적으로 사라지는 것들

가이드 · 2026-08-19 최종 확인

HTML을 Markdown으로 바꾸는 도구는 크게 두 방식으로 나뉩니다. Turndown.js 같은 라이브러리를 쓰는 방식과, 브라우저의 DOMParser로 직접 태그를 순회하며 규칙을 매핑하는 방식입니다. 어느 쪽이든 결과는 같은 근본 문제에 부딪힙니다 — Markdown(CommonMark) 문법 자체가 HTML보다 훨씬 좁은 표현력을 가지고 있어서, 대응하는 문법이 없는 요소는 변환 과정에서 사라지거나 깨진다는 점입니다. 이 가이드는 HTML→Markdown 변환기가 실제로 어떤 코드로 동작하는지 뜯어보고, 구조적으로 무엇이 살아남고 무엇이 무너지는지 구체적인 입출력 예시로 정리했습니다.

1. 표 병합 셀: colspan/rowspan이 통째로 무시된다

이 도구의 표 변환 함수는 각 행의 <th>/<td> 요소를 querySelectorAll로 긁어서 텍스트만 추출합니다. colspan/rowspan 속성은 코드 어디에서도 읽지 않습니다. 문제는 단순히 "병합이 무시된다"가 아니라, 헤더 행과 데이터 행의 셀 개수 자체가 어긋난다는 데 있습니다. 다음 입력을 보면 바로 드러납니다.

<table>
  <tr><th colspan="2">이름</th><th>나이</th></tr>
  <tr><td>김</td><td>철수</td><td>28</td></tr>
</table>

헤더 행은 <th> 2개(이름, 나이)로 처리되어 열이 2개인 표로 인식되는데, 데이터 행은 <td> 3개(김, 철수, 28)를 그대로 나열합니다. 헤더 열 수와 본문 열 수를 맞추는 로직이 없어서 다음처럼 열이 어긋난 마크다운이 출력됩니다.

실제 출력:
| 이름 | 나이 |
| --- | --- |
| 김 | 철수 | 28 |
데이터 행의 파이프(|) 개수가 헤더보다 많아 GFM 렌더러에 따라 표가 깨지거나 마지막 열이 잘려 보입니다.

즉 병합 셀이 있는 표는 변환 전에 미리 병합을 풀어서(또는 병합된 값을 각 셀에 복제해서) 입력하는 것이 안전합니다.

2. 시맨틱 태그: 의미는 버리고 텍스트만 남는다

Markdown(CommonMark)에는 강조(**굵게**, *기울임*)와 취소선(GFM의 ~~취소선~~) 외에는 인라인 서식 문법이 없습니다. 소스 코드를 보면 <mark>(형광펜), <abbr>(약어), <cite>(출처), <small>, <sub>/<sup>(아래·위 첨자)는 전부 childrenToMd(node)로 그냥 자식 텍스트만 반환하도록 매핑되어 있습니다. 즉 <mark>중요</mark>는 강조 표시 없이 그냥 "중요"라는 평문이 되고, H2O처럼 <sub>로 쓴 아래첨자도 "H2O"로 첨자 없는 평문이 됩니다. CommonMark 표준 자체에 첨자 문법이 없기 때문에 이는 이 도구만의 한계가 아니라 Markdown 생태계 공통의 한계입니다.

3. 스타일·속성: class/id/style/data-*는 애초에 읽지 않는다

변환 함수는 태그 이름(tagName)과 href/src/alt, 그리고 코드 블록의 언어 감지를 위한 class 정도만 참조합니다. 인라인 style 속성, class/id, data-*, aria-*, 이벤트 핸들러(onclick 등)는 순회 로직 자체에서 참조되지 않으므로 결과물에 반영될 여지가 없습니다. 디자인이 중요한 HTML(카드 레이아웃, 색상 강조 등)을 변환하면 시각적 정보는 전부 사라지고 콘텐츠 뼈대만 남는다고 생각하면 됩니다.

4. 반전: 중첩 목록은 의외로 잘 보존된다

정규식 기반 변환기들이 흔히 실패하는 지점이 중첩 목록인데, 이 도구는 listToMd() 함수가 li의 자식 중 ul/ol을 재귀 호출로 별도 처리하고, 재귀 깊이(depth)만큼 공백 2칸씩 들여쓰기를 누적합니다. 3단계 중첩 목록까지 실제로 정상 변환되는 것을 코드로 확인할 수 있습니다. 표와 시맨틱 태그에서는 정보가 사라지지만, 목록 구조 자체는 DOM 순회 방식 덕분에 비교적 안전하게 살아남는 예외적인 부분입니다.

5. 부수 효과: javascript: 링크는 자동으로 무력화된다

링크 변환 코드에는 href.startsWith('javascript')인 경우 링크 문법 대신 텍스트만 반환하는 분기가 들어 있습니다. 의도적인 보안 설계라기보다는 "이런 링크는 마크다운으로 옮길 의미가 없다"는 실용적 처리에 가깝지만, 결과적으로 javascript: 스킴을 이용한 악성 링크가 변환 과정에서 자동으로 제거되는 부수 효과가 있습니다.

자주 묻는 질문

Q. 병합된 표를 깨지지 않게 변환하려면 어떻게 해야 하나요?

A. 변환 전에 HTML에서 colspan/rowspan을 풀어 각 셀에 값을 개별 복제해 두거나, 변환 후 출력된 Markdown 표를 열 개수 기준으로 손으로 맞춰야 합니다. 이 도구는 병합 정보를 아예 읽지 않으므로 자동 보정을 기대할 수 없습니다.

Q. 굵게·기울임 말고 다른 서식(밑줄, 형광펜, 첨자)도 유지되나요?

A. 아니요. CommonMark/GFM에는 밑줄·형광펜·첨자 문법이 없어서 해당 태그는 서식 없이 텍스트만 남습니다. 이런 서식이 꼭 필요하다면 원본 HTML을 그대로 유지하거나, 변환 후 Markdown 안에 원시 HTML 태그를 수동으로 다시 삽입해야 합니다.

Q. CSS로 만든 디자인(카드, 배지, 색상)도 변환에 반영되나요?

A. 아니요. class/id/style 속성은 변환 로직에서 아예 참조하지 않으므로 콘텐츠(텍스트·링크·목록·표 구조)만 남고 시각적 스타일 정보는 전부 사라집니다.

Q. 중첩된 목록도 안전하게 변환되나요?

A. 네. 이 도구는 목록을 재귀적으로 순회하며 들여쓰기(레벨당 공백 2칸)를 정확히 계산하므로, 다단계로 중첩된 <ul>/<ol>도 표나 시맨틱 태그와 달리 구조가 잘 보존됩니다.