마크다운 변환기 개발 기록 (markdown to html)

마크다운 변환기 markdown to html

 

🚀 “복붙” 한 번에 예쁘게! 제가 직접 만든 마크다운 변환기 개발 기록

안녕하세요, 여러분! 오늘은 제가 최근에 푹 빠져서 만든 작은 토이 프로젝트, ‘마크다운 to HTML 변환기’에 대한 이야기를 들려드리려고 합니다. “아니, 그런 거 이미 널리고 널렸잖아?” 라고 생각하실 수도 있지만, 제 변환기는 조금 특별한 ‘복붙’ 필살기를 가지고 있답니다.

마크다운 변환기 markdown to html

Github, Notion, Dev.to 같은 여러 디폴트 테마까지 입혀둬서 쉽게 선택할 수 있음

(이 글도 markdown 으로 작성하고 html 변환기 돌린겁니다 !)

링크 : https://oneweekoneproduct.com/markdown-to-html

 

🤔 “아, 제발…!” 모든 것은 사소한 빡침에서 시작됐다

개발자라면 마크다운(Markdown)은 거의 공기 같은 존재죠. README 파일, 기술 블로그, 노션 정리까지… 하루에도 몇 번씩 마크다운으로 문서를 작성하곤 합니다.

문제는 항상 여기서부터 시작됩니다.

🧑‍💻: (신나게 마크다운으로 기술 문서 작성 완료!) “좋았어, 이제 이 내용을 그대로 복사해서 팀원들에게 이메일로 공유해야지!”

(Ctrl+C, Ctrl+V)

📧 이메일 화면: (처참)

  • 코드 블록은 그냥 텍스트 덩어리가 되어 있고…
  • 예쁘게 넣었던 인용구 스타일은 온데간데없고…
  • 알록달록했던 신택스 하이라이팅은 흑백이 되어버렸다…

이런 경험, 다들 한 번쯤 있으시죠? 😭 분명 내 화면에서는 GitHub 스타일로 멋지게 보였는데, 다른 곳에 붙여넣는 순간 모든 스타일이 ‘증발’해버리는 마법! 이 사소하지만 반복되는 ‘빡침’이 바로 이 프로젝트의 시작이었습니다.

“내가 쓴 마크다운, 어디에 붙여넣든 그대로 예쁘게 보이면 안 될까?”

frustrated-meme

(매번 서식이 깨질 때의 내 심정)

✨ 1단계: 기본 뼈대 만들기 (feat. Svelte & Marked.js)

일단 빠르게 프로토타입을 만드는 게 중요했어요. 요즘 제가 푹 빠져있는 Svelte와 가장 유명한 마크다운 파서 라이브러리인 **marked**를 조합하기로 했습니다.

Svelte 5의 새로운 문법인 ‘룬(Runes)’을 사용하니 코드가 정말 직관적이고 간결해지더군요.

// 입력된 마크다운이 바뀔 때마다...
$effect(() => {
  // 알아서 HTML로 변환해줘!
  htmlOutput = marked.parse(markdownInput);
});

$state로 상태를 선언하고 $effect로 반응성을 처리하니, 왼쪽 창에 마크다운을 입력하면 오른쪽 창에 실시간으로 HTML 결과가 뾰로롱 나타났습니다. 여기까지는 순조로웠죠!

🤯 2단계: “예쁘게” 만들기, 그리고 마주한 거대한 벽

하지만 변환된 HTML은 스타일이 전혀 없는 ‘민낯’ 그 자체였습니다. 이제 여기에 예쁜 옷을 입혀줘야죠! GitHub, Notion, Velog 등 개발자들에게 친숙한 테마들을 추가하기로 했습니다.

여기서 첫 번째 난관에 부딪혔습니다.

“테마를 바꿀 때마다 페이지 전체가 아니라, 저 미리보기 창 안의 스타일만 바꿔야 하는데… 어떻게 하지?”

CSS 파일을 통째로 갈아 끼우는 건 비효율적이었어요. 고민 끝에, 사용자가 테마를 선택하면 해당 테마의 CSS 내용을 JavaScript로 읽어온 뒤, <style> 태그를 동적으로 만들어 <head>에 쏙 넣어주는 방법을 사용했습니다.

// 선택된 테마가 바뀌면...
$effect(() => {
    // 동적으로 스타일 태그를 만들고
    const styleElement = document.createElement('style');
    // 해당 테마의 CSS를 넣어서 head에 추가!
    styleElement.innerHTML = getThemeStyles(selectedTheme);
    document.head.appendChild(styleElement);
});

이 방법으로 미리보기 창에서는 아주 멋지게 테마들이 적용되었습니다. “오, 이제 거의 다 됐는데?” 라고 생각한 순간, 진짜 ‘최종 보스’가 나타났습니다.

바로 ‘복사하기’ 기능이었습니다.

미리보기 창의 내용을 드래그해서 복사해봤자, 결국 복사되는 건 스타일이 빠진 HTML 알맹이뿐이었죠. 결국 원점으로 돌아온 겁니다.

💡 3단계: 구원자 ‘Juice’의 등장과 필살기의 완성

“CSS를… HTML 태그 안에… 일일이 다 넣어줄 수는 없나?”

이 무식해 보이는 아이디어가 바로 정답이었습니다. 이메일 클라이언트처럼 외부 CSS 파일을 허용하지 않는 환경에서는, <h1 style="color: blue; font-size: 24px;">처럼 모든 스타일을 HTML 태그 안에 직접(inline) 넣어줘야 합니다.

이 지옥 같은 작업을 대신해 줄 라이브러리를 찾아 헤맸고, 마침내 운명처럼 **juice**라는 라이브러리를 발견했습니다. 이름부터 뭔가 상큼하지 않나요? 🍊

juice는 HTML과 CSS를 주면, 마법처럼 CSS 스타일을 분석해서 각 HTML 태그에 style="..." 속성으로 알아서 꽂아주는, 그야말로 ‘마법의 지팡이’였습니다.

// marked로 변환된 HTML과 테마 CSS를...
// juice에게 넘겨주면...
try {
  // 스타일이 인라인으로 적용된 최종 HTML이 짠!
  inlinedHtml = juice(htmlDoc, { extraCss: themeStyles });
} catch (err) {
  console.error('Juice error:', err);
}

juice로 처리된 HTML이야말로 제가 원했던 ‘어디에 붙여넣어도 스타일이 깨지지 않는 완전체’ 였던 것이죠!

🎉 그래서 “공유(복사) 기능”에 집착한 이유

이제 제 변환기에는 두 개의 핵심 복사 버튼이 생겼습니다.

  1. 📋 콘텐츠 복사: 이건 document.execCommand('copy')를 이용한 고전적인 방식입니다. 미리보기 화면에 보이는 ‘서식이 있는 텍스트’를 클립보드에 담아줘요. Gmail, Google Docs, Notion 같은 리치 텍스트 편집기에 바로 붙여넣을 때 아주 유용합니다. 사용자는 이게 HTML인지 뭔지 몰라도 그냥 예쁘게 복사되니 가장 직관적인 방법이죠.

  2. 📋 HTML 복사: 이 버튼이 바로 이 프로젝트의 심장입니다. juice가 만들어낸 ‘인라인 스타일이 적용된 완전한 HTML 코드’ 를 복사해줍니다. 이 코드를 복사해서 이메일 템플릿의 HTML 소스에 그대로 붙여넣으면, 어떤 이메일 클라이언트에서 열어보든 개발자가 의도한 디자인 그대로 보이게 됩니다. CSS 지원이 까다로운 환경을 위한 완벽한 해결책이죠.

결국 제가 만들고 싶었던 건 단순한 ‘변환기’가 아니라, “잘 작성된 마크다운 콘텐츠의 가치를 온전히 전달해주는 ‘공유’ 도구” 였던 셈입니다.

✨ 마치며

사소한 불편함에서 시작된 작은 프로젝트였지만, 문제를 정의하고, 부딪히고, 해결책을 찾아가는 과정은 언제나 즐거운 것 같습니다. 특히 Svelte 5와 juice 같은 멋진 도구들 덕분에 생각했던 기능을 큰 어려움 없이 구현할 수 있었네요.

혹시 저처럼 마크다운 복붙 서식 때문에 고통받으셨던 분이 있다면, 이 글이 작은 공감과 재미가 되었으면 좋겠습니다. 여러분도 생활 속 불편함을 그냥 지나치지 말고, 직접 해결하는 ‘나만의 토이 프로젝트’를 시작해보시는 건 어떨까요?

긴 글 읽어주셔서 감사합니다! 🚀

 

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다