Skip to content

마크다운이 작동하는 방식

마크다운이 어떻게 작동하는지 이해하면 이 강력한 도구를 더 잘 활용할 수 있습니다. 이 장에서는 마크다운이 일반 텍스트에서 아름답고 형식이 지정된 문서로 변환되는 방식을 자세히 살펴보겠습니다.

기본 워크플로우

마크다운 워크플로우는 다음 단계로 요약할 수 있습니다:

마크다운 소스 파일 (.md) → 마크다운 파서 → HTML 문서 → 브라우저 렌더링

1. 마크다운 소스 파일 작성

모든 텍스트 에디터를 사용하여 .md 파일을 만들고 마크다운 문법을 사용하여 콘텐츠를 작성합니다:

markdown
# 내 문서

이것은 **중요한** 단락입니다.

## 목록 예제

- 항목 1
- 항목 2
- 항목 3

2. 마크다운 파서 처리

파서는 마크다운 파일을 읽고, 문법 요소를 인식하고, 이를 해당하는 HTML로 변환합니다:

html
<h1>내 문서</h1>
<p>이것은 <strong>중요한</strong> 단락입니다.</p>
<h2>목록 예제</h2>
<ul>
  <li>항목 1</li>
  <li>항목 2</li>
  <li>항목 3</li>
</ul>

3. 브라우저 렌더링

생성된 HTML이 브라우저에서 형식이 지정된 문서로 표시됩니다.

파서가 작동하는 방식

어휘 분석

파서는 먼저 어휘 분석을 수행하여 마크다운 텍스트를 토큰으로 분해합니다:

  • # 제목 토큰으로 인식
  • **text** 굵은 텍스트 토큰으로 인식
  • - item 목록 항목 토큰으로 인식

구문 파싱

그런 다음 구문 파싱을 수행하여 추상 구문 트리(AST)를 구축합니다:

문서
├── 제목 (레벨 1): "내 문서"
├── 단락
│   ├── 텍스트: "이것은"
│   ├── 굵은 텍스트: "중요한"
│   └── 텍스트: "단락입니다."
├── 제목 (레벨 2): "목록 예제"
└── 순서 없는 목록
    ├── 목록 항목: "항목 1"
    ├── 목록 항목: "항목 2"
    └── 목록 항목: "항목 3"

HTML 생성

마지막으로 구문 트리를 순회하여 해당하는 HTML 출력을 생성합니다.

주류 파서

CommonMark

  • 표준 명세 - 통일된 마크다운 파싱 표준 제공
  • 엄격하게 정의됨 - 다양한 구현 간의 모호함 제거
  • 광범위하게 지원됨 - 여러 파서에서 채택

GitHub Flavored Markdown (GFM)

CommonMark를 기반으로 하며 다음을 추가합니다:

  • 표 지원
  • 취소선
  • 작업 목록
  • 자동 링크 감지
  • 구문 강조 코드 블록

기타 파서

파서언어기능
markedJavaScript빠름, 가벼움
markdown-itJavaScript플러그인 가능, 고도로 확장 가능
Python-MarkdownPython기능이 풍부함, 플러그인 시스템
kramdownRuby여러 출력 형식 지원
PandocHaskell범용 문서 변환기

렌더링 엔진

클라이언트 사이드 렌더링

브라우저에서 실시간으로 마크다운 파싱:

javascript
// marked.js 사용
const html = marked.parse('# Hello World');
document.body.innerHTML = html;

장점:

  • 서버 처리 불필요
  • 실시간 미리보기
  • 서버 부하 감소

단점:

  • JavaScript에 의존
  • SEO에 불리함
  • 초기 로딩이 느림

서버 사이드 렌더링

서버에서 HTML을 미리 생성:

javascript
// Node.js 예제
const fs = require('fs');
const marked = require('marked');

const markdown = fs.readFileSync('document.md', 'utf8');
const html = marked.parse(markdown);

장점:

  • SEO 친화적
  • 빠른 로딩
  • 클라이언트 사이드 JavaScript에 의존하지 않음

단점:

  • 서버 처리 오버헤드
  • 복잡한 캐시 관리

정적 사이트 생성

빌드 시 모든 페이지를 미리 생성:

bash
# VitePress 사용
npm run build

장점:

  • 가장 빠른 로딩 속도
  • 최고의 SEO
  • 높은 보안
  • 쉬운 배포

단점:

  • 동적 콘텐츠 지원 제한
  • 더 긴 빌드 시간

확장 메커니즘

플러그인 시스템

많은 파서가 플러그인 확장을 지원합니다:

javascript
// markdown-it 플러그인 예제
const md = require('markdown-it')()
  .use(require('markdown-it-footnote'))
  .use(require('markdown-it-deflist'))
  .use(require('markdown-it-abbr'));

사용자 정의 렌더러

javascript
// 사용자 정의 링크 렌더러
const renderer = new marked.Renderer();
renderer.link = function(href, title, text) {
  return `<a href="${href}" target="_blank">${text}</a>`;
};

성능 최적화

캐싱 전략

javascript
const cache = new Map();

function parseMarkdown(content) {
  const hash = generateHash(content);
  if (cache.has(hash)) {
    return cache.get(hash);
  }
  
  const result = marked.parse(content);
  cache.set(hash, result);
  return result;
}

지연 로딩

javascript
// 보이는 영역의 콘텐츠만 파싱
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      parseAndRender(entry.target);
    }
  });
});

스트리밍 처리

javascript
// 대용량 파일을 위한 스트림 파싱
const fs = require('fs');
const { Transform } = require('stream');

const markdownTransform = new Transform({
  transform(chunk, encoding, callback) {
    const html = marked.parse(chunk.toString());
    callback(null, html);
  }
});

fs.createReadStream('large-document.md')
  .pipe(markdownTransform)
  .pipe(fs.createWriteStream('output.html'));

일반적인 문제

1. 줄바꿈 문제

다른 파서가 줄바꿈을 다르게 처리할 수 있습니다:

markdown
줄 1
줄 2        ← 이것들이 같은 단락으로 파싱될 수 있음

줄 1  
줄 2        ← 줄 끝의 두 공백이 줄바꿈을 강제함

줄 1

줄 2        ← 빈 줄이 단락을 분리함

2. HTML 혼합

markdown
이것은 **마크다운**과 <em>HTML</em>의 혼합입니다.

HTML 태그의 올바른 닫기와 중첩에 주의하세요.

3. 특수 문자 이스케이핑

markdown
여기서는 \*\_ 문자를 이스케이프해야 합니다.

실제 응용 시나리오

1. 블로그 시스템

마크다운 기사 → 정적 사이트 생성기 → HTML 웹사이트

2. 문서 사이트

.md 문서 → VitePress/Docusaurus → 온라인 문서

3. README 파일

README.md → GitHub/GitLab → 프로젝트 홈페이지

4. 노트 앱

마크다운 노트 → 실시간 렌더링 → 리치 텍스트 표시

다음 단계

이제 마크다운이 어떻게 작동하는지 이해했으므로 다음을 할 수 있습니다:

Build by www.markdownlang.com