Perl School Publishing의 비하인드 스토리
Source: Dev.to
우리는 방금 새로운 Perl School 책, Design Patterns in Modern Perl (Mohammad Sajid Anwar 저)를 출간했습니다.
새 타이틀을 마지막으로 내놓은 지 꽤 되었고, 그 사이 전자책 세계는 변했습니다 – Amazon은 더 이상 .mobi를 사용하지 않으며, 도구들도 바뀌었고, “대충 눈을 깜빡이면 동작한다”는 옛 빌드 파이프라인은 삐걱거리기 시작했습니다.
게다가 우리는 촉박한 마감일이 있었습니다: 런던 Perl 워크숍에 맞춰 책을 준비하고 싶었습니다. 날짜가 다가오면서 마지막 순간 수정과 수동 조정이 점점 더 무섭게 느껴졌습니다. 우리는 매번 “양질의 PDF + EPUB”을 만들 수 있는 신뢰할 수 있고 재현 가능한 방법이 절실히 필요했습니다.
그래서 지난 몇 주 동안 Perl School 책 파이프라인을 처음부터 다시 구축했습니다. 이 글은 그 과정, 최종적으로 사용하게 된 도구들, 그리고 여러분이 자신의 책에 적용할 수 있는 방법을 이야기합니다.
The old world, and why it wasn’t good enough
원래 Perl School 파이프라인은 아주 다른 시대에 시작되었습니다:
- Amazon은
.mobi파일을 원했습니다. - EPUB 지원은 엉성했습니다.
- 저는 셸 스크립트로 억지로 이어붙이고 최선을 기대했습니다.
동작했습니다… 안 될 때까지. 각 책마다 약간씩 다른 스크립트, 약간씩 다른 가정, 그리고 약간씩 다른 마지막 순간 수동 수정이 있었습니다. 새 저자에게 건네며 “이걸 믿어라”라고 말하기엔 전혀 적합하지 않았습니다.
Design Patterns in Modern Perl에 다시 적용해 보니 그 문제가 뚜렷이 드러났습니다. 책 자체는 현대적이고 구조가 잘 잡혀 있지만, 이를 만든 파이프라인은 유물처럼 느껴져서는 안 됩니다.
Choosing tools: Pandoc and wkhtmltopdf (and no LaTeX, thanks)
새 파이프라인은 두 가지 주요 도구를 중심으로 구성됩니다:
- Pandoc – 문서 변환의 스위스 군용 나이프. Markdown/Markua와 메타데이터를 받아 HTML, EPUB, 그 외 수많은 형식으로 변환할 수 있습니다.
wkhtmltopdf– HTML을 헤드리스 브라우저 엔진을 이용해 인쇄용 PDF로 변환합니다.
왜 LaTeX가 아니냐? 알레르기가 있기 때문입니다. LaTeX는 엄청나게 강력하지만, 진지하게 사용하려고 할 때마다 제가 즐기지 않는 언어로 페이지 나눔을 디버깅하게 됩니다. HTML + CSS는 견딜 수 있고, 브라우저는 이해할 수 있습니다.
Conversion flow
PDF route
Markdown → HTML (via Pandoc) → PDF (via wkhtmltopdf)
EPUB route
Markdown → EPUB (via Pandoc) → validated with epubcheck
앞표지, 타이틀 페이지, 저작권 등 프런트 매터는 간단한 book-metadata.yml 파일을 템플릿 툴킷으로 생성한 뒤 챕터와 합쳐 일관된 책을 만듭니다.
이 정도면 꽤 멀리 왔지만, 어느 독자가 버그를 발견했습니다.
The iBooks bug report
출판 직후, Leanpub EPUB을 구매해 Apple Books(iBooks)에서 읽던 독자가 큰 분홍색 오류 상자를 보았습니다:
There’s something wrong with the XHTML in this EPUB.
Apple Books는 XHTML의 “X”에 대해 매우 엄격합니다: 잘 형성된 XML을 기대하며, 단순히 “대충 유효한 HTML”은 받아들이지 않습니다. EPUB을 다룰 때는 HTML5의 유연성을 잊어야 합니다.
Discovering epubcheck
epubcheck는 EPUB 파일을 위한 공식 검증 도구입니다. .epub 파일을 지정하면 압축을 풀고, 모든 XML/XHTML을 파싱하고, 메타데이터와 매니페스트를 검사해 정확히 무엇이 잘못됐는지 알려줍니다.
책에 실행해 보니 즉시 다음과 같은 결과가 나왔습니다:
Fatal Error while parsing file: The element type `br` must be terminated by the matching end-tag `</br>`.
HTML에서는 <br>가 허용되지만, XHTML(XML)에서는 <br/>(self‑closing) 혹은 <br></br>를 사용해야 합니다. 몇몇 챕터에 이런 문제가 여러 번 나타났습니다. Pandoc이 원시 HTML을 그대로 EPUB에 넣었지만, 그 HTML이 엄격히 유효한 XHTML이 아니었기 때문에 Apple Books가 거부한 것입니다.
A quick (but not scalable) fix
시간이 촉박한 상황에서 가장 빠른 진단 확인 방법은 다음이었습니다:
- 생성된 EPUB을 압축 해제한다.
- 문제의 XHTML 파일을 연다.
<br>를<br/>로 몇 군데 수동 수정한다.- EPUB을 다시 압축한다.
epubcheck를 다시 실행한다.- Apple Books에서 확인한다.
오류가 사라지고 epubcheck도 만족했으며, 독자는 수정된 파일이 정상적으로 열리는 것을 확인했습니다. 하지만 “EPUB을 텍스트 편집기로 열어 XHTML을 손수 고친다”는 방식은 지속 가능한 출판 전략이 아닙니다.
HTML vs XHTML, and why linters matter
근본적인 문제는 단순합니다:
- HTML은 관대합니다; 브라우저가 깨진 마크업을 스스로 고칩니다.
- XHTML은 XML이므로 관대하지 않습니다. EPUB 3 콘텐츠 파일은 XHTML이며, 조악한 HTML은 일부 리더(예: Apple Books)에서 로드를 거부하게 합니다.
저는 Pandoc이나 epubcheck에 도달하기 전에 원고 HTML 린터를 도구 체인에 추가했습니다.
대략적인 린터 동작:
- 원고를 읽고(Perl 예제 안의
<때문에 fenced code block은 무시) - 원시 HTML 조각을 추출하고
- 임시 루트 요소에 감싼 뒤
XML::LibXML을 이용해 XML이 잘 형성됐는지 검사하고- 파일명과 라인 번호와 함께 오류를 보고합니다.
전체 HTML 검증은 아니지만 “이 HTML이 EPUB에 들어가면 XML 파서가 울겠는가?”를 묻는 것입니다. 이 과정이라면 <br> 문제를 책이 내 컴퓨터를 떠나기 전에 잡을 수 있었을 겁니다.
Hardening the pipeline: epubcheck in the loop
린터는 원고 단계에서 명백한 문제를 잡아내고, epubcheck는 최종 EPUB에 대한 최종 권위자로 남습니다.
현재 파이프라인 흐름은 다음과 같습니다:
- 원고 HTML 린트 – 변환 전 깨진 원시 HTML/XHTML을 잡는다.
make_book으로 PDF + EPUB 빌드.- EPUB에
epubcheck실행 – 최종 파일이 표준을 준수하는지 확인한다. - 그 뒤에 Leanpub과 Amazon에 업로드한다.
새 CSS, 새로운 템플릿, 다른 메타데이터 등 미래의 모든 변경도 동일한 검증 과정을 거치며, 파이프라인은 독자가 문제를 겪기 전에 미리 경고합니다.
Docker and GitHub Actions: making it reproducible
멋진 Perl 스크립트와 내 노트북에 설치된 도구 목록만으로는 혼자 프로젝트를 진행하기엔 괜찮지만, 다음 경우엔 한계가 있습니다:
- 다른 저자들이 자신의 초안을 빌드하고 싶을 때, 혹은
- CI에서 자동으로 빌드하고 싶을 때.
다음 단계는 모든 것을 Docker 이미지에 담고 GitHub Actions와 연결하는 것이었습니다.
Docker image contents
- Perl +
cpanm+ 레포의cpanfile에 명시된 모든 CPAN 모듈 pandocwkhtmltopdf- Java +
epubcheck - Perl School 유틸리티 스크립트(
make_book,check_ms_html등)
Typical workflow in a book repo
# Mount the book’s Git repo into /work
docker run --rm -v "$(pwd)":/work perl-school-builder \
perl check_ms_html # lint the manuscript
docker run --rm -v "$(pwd)":/work perl-school-builder \
perl make_book # build built/*.pdf and built/*.epub
docker run --rm -v "$(pwd)":/work perl-school-builder \
java -jar /usr/local/epubcheck/epubcheck.jar built/*.epub
모든 것이 컨테이너화되고 자동화되면, 어떤 저자라도 정확히 동일한 빌드를 재현할 수 있고, CI 파이프라인은 표준을 준수하는 PDF와 EPUB만이 공개되도록 보장합니다.