처리 방식과 흐름(DVI/PDF)

한 줄짜리 LaTeX 문서——\documentclass{article}\begin{document}Hi\end{document}——를 pdflatex에 통과시키면 11,529바이트 PDF가 나옵니다. 똑같은 소스를 latex에 통과시키면 나오는 DVI 파일은 248바이트. 46배 차이입니다. 둘 다 같은 한 쪽, 같은 “Hi” 한 낱말을 기술하는데 이만큼 다른 이유는 무엇일까요. 그 답이 TeX 처리 흐름 전체를 설명해 줍니다. 이 페이지에서는 소스에서 엔진을 거쳐 출력에 이르는 길, .dvi 안에 실제로 무엇이 들어 있는지, 그리고 DVI를 거치는 옛 경로가 여태 사라지지 않은 이유를 실측값과 함께 따라갑니다.

왜 WYSIWYG가 아닌가: 일괄 처리라는 선택

TeX이 화면을 즉시 갱신하지 않는 것은 줄 나누기를 단락 단위로 최적화하기 때문입니다. TeX은 한 줄씩 차례로 채우는 것이 아니라, 단락 전체를 놓고 “행 끝을 어디에 두는 조합이 가장 덜 흉한가”를 평가해 총비용이 가장 작은 해를 고릅니다. 그 결과, 단락 끝에 글자 하나를 더한 것만으로 그 단락 첫 줄의 끊기 위치까지 바뀌기도 합니다. 키를 누를 때마다 모든 단락을 다시 계산하는 것은 수지가 맞지 않습니다.

그래서 TeX은 일괄 처리를 택했습니다. 원고를 끝까지 읽고 전체를 한 번에 조판하는 방식입니다. 잃는 것은 입력하면서 결과를 보는 편안함이고, 얻는 것은 전체 최적의 조판, 긴 문서에서도 흐트러지지 않는 체재, 그리고 일반 텍스트라서 가능한 자동화——스크립트로 소스를 생성하고, Git으로 차이를 보고, 서버에서 무인으로 빌드하는 일입니다. 작업 리듬은 “쓰고 → 컴파일하고 → PDF를 본다”의 반복이 됩니다. 이 왕복을 빠르게 만드는 것이 아래 이야기의 거의 전부입니다.

소스에서 출력까지의 두 경로: 직접 PDF와 DVI 경유

엔진은 PDF를 바로 쓰거나, DVI라는 중간 파일을 쓰거나 둘 중 하나입니다. PDF를 바로 쓰는 것은 pdflatexxelatexlualatex(차례로 pdfTeX・XeTeX・LuaTeX 엔진)이고, DVI를 쓰는 것은 latex과 일본어용 platexuplatex입니다. DVI를 쓰는 것으로 끝이 아니라, dvi 드라이버라 불리는 별도 프로그램에 넘겨 원하는 형식으로 변환해야 합니다——dvipdfmx면 PDF, dvips면 PostScript, dvisvgm이면 SVG입니다. 화면에 그리는 드라이버는 dvi 뷰어라고 부릅니다.

terminal
# Direct to PDF, one step
lualatex  document.tex        # -> document.pdf
pdflatex  document.tex        # -> document.pdf

# Via DVI, two steps (the Japanese route)
uplatex   document.tex        # -> document.dvi
dvipdfmx  document.dvi        # -> document.pdf

# Other dvi drivers, same input
dvips     document.dvi        # -> document.ps
dvisvgm   document.dvi        # -> document.svg

여기서 헷갈리기 쉬운 점은 “DVI를 거치는가”와 “유니코드・시스템 글꼴을 쓸 수 있는가”가 서로 다른 축이라는 것입니다. pdflatex은 DVI를 거치지 않고 PDF를 바로 내지만 입력을 다루는 방식은 옛것이라, 컴퓨터에 설치된 OpenType 글꼴을 그대로 지정할 수 없습니다. 반대로 xelatexlualatex은 유니코드를 그대로 읽고 fontspec으로 글꼴을 이름으로 지정할 수 있습니다. 두 축을 분리해 두면 이름의 홍수가 단번에 정리됩니다.

명령출력입력과 글꼴다룰 수 있는 이미지
latexDVI (dvi 드라이버 필요)ASCII 중심. TeX 고유의 글꼴 형식EPS・PS
pdflatexPDF (직접)유니코드는 제한적. 시스템 글꼴은 쓸 수 없습니다PNG・JPEG・PDF (EPS는 자동 변환)
uplatexDVI (dvipdfmx에 넘깁니다)유니코드를 다루는 일본어 엔진. 일본어 글꼴 삽입은 설정하기 나름EPS・PDF・PNG・JPEG
xelatexPDF (내부적으로 .xdv를 경유)유니코드. 시스템 OpenType을 fontspec으로 지정PNG・JPEG・PDF・EPS
lualatexPDF (직접)유니코드. 시스템 글꼴. 내부를 Lua로 확장 가능PNG・JPEG・PDF・EPS

.dvi의 내용물은 무엇인가: 248바이트를 열어 보기

DVI는 device independent(장치 비의존)의 줄임말이며, 그 내용은 “어느 글자를 어느 위치에 놓을지”만 나열한 명령의 열입니다. TeX Live에 딸려 오는 dvitype을 쓰면 사람이 읽을 수 있는 형태로 풀어낼 수 있습니다. 앞의 한 줄짜리 문서에서 만든 248바이트 DVI를 열어 보니 실제로 들어 있던 것은 다음과 같았습니다.

terminal
$ dvitype -output-level=4 one.dvi
numerator/denominator=25400000/473628672
magnification=1000
' TeX output 2026.08.13:1233'
maxv=41484288, maxh=26673152, maxstackdepth=3, totalpages=1
Font 27: cmr10---loaded at size 655360 DVI units

42: beginning of page 1
117: down4 41484288       v:=0+41484288=41484288
140: right3 5046272       h:=0+5046272=5046272
144: fntdef1 27: cmr10
165: fntnum27             current font is cmr10
166: setchar72            h:=5046272+491521=5537793
167: setchar105           h:=5537793+182045=5719838
[Hi]
181: setchar49            h:=15204352+327681=15532033
[ 1]
185: eop

이것이 전부입니다. setchar72는 “문자 코드 72(H)를 여기 놓아라”, setchar105는 “105(i)를 놓아라”이고, setchar49는 아래쪽에 찍히는 쪽 번호 1입니다. right3down4는 현재 위치를 옮기며, 단위인 DVI unit은 1 sp, 곧 1/65536 pt입니다. 그래서 cmr10의 “655360 DVI units”는 정확히 10 pt입니다. 그리고 결정적인 것은 fntdef1 27: cmr10 줄입니다. DVI는 글꼴을 이름으로 부를 뿐입니다. cmr10이 어떤 모양인지는 파일 어디에도 적혀 있지 않습니다.

PDF는 이 점에서 정반대입니다. pdflatex이 낸 11,529바이트 PDF를 들여다보면 글꼴이 UNYBJV+CMR10이라는 이름으로 삽입되어 있습니다. 앞의 여섯 글자는 서브셋 표시로, 실제로 쓴 글리프만 뽑아냈다는 증거입니다. 그 글꼴을 담은 객체에는 /Length1 1394 /Length2 8300 /Length3 0 /Length 9259라고 적혀 있습니다. 압축 뒤에도 9,259바이트, 전체 11,529바이트의 8할입니다. 46배 차이의 정체가 사실상 이것입니다. DVI는 “cmr10의 H”라고 적으면 그만이지만, PDF는 그 H의 윤곽 자체를 지고 다녀야 합니다. 어디로 가져가도 똑같이 보인다는 PDF의 약속에 대한 대가입니다.

다만 한 가지 주의가 필요합니다. “DVI가 작은 것은 글꼴을 담지 않기 때문”은 맞지만, “PDF가 11,529바이트인 것은 글꼴을 담기 때문”만으로는 설명이 부족합니다. 같은 248바이트 DVI를 dvipdfmx에 통과시키면 나오는 PDF는 1,950바이트입니다. 이쪽도 글꼴을 삽입하지만 압축률이 훨씬 높은 /Subtype/Type1C 형식으로 변환해, 글꼴 본체는 546바이트에 그칩니다. 곧 낱말 하나짜리 같은 쪽이 기술 방식만으로 248 → 1,950 → 11,529바이트로 달라지는 셈입니다. 파일 크기가 문제가 되는 자리에서는 경로의 차이가 실제로 드러납니다.

DVI 경로가 아직 살아 있는 이유: 일본어와 .xdv

DVI가 남아 있는 가장 큰 실무적 이유는, 일본어 조판의 표준 경로가 바로 거기에 있기 때문입니다. platexuplatex은 DVI를 내고, 마지막에 dvipdfmx로 PDF를 만듭니다. 이 프로그램은 Mark A. Wicks의 dvipdfm에서 출발해 일본어의 요구에 맞춰 확장된 것으로, TeX Live 2024에 들어 있는 것은 2024년 3월판입니다. 하는 일은 DVI의 좌표 명령을 PDF의 그리기 명령으로 옮기고, 참조된 글꼴을 찾아 삽입하는 것입니다. 실제로 upLaTeX으로 일본어 한 줄짜리 문서를 통과시키면 396바이트 DVI가 5,998바이트 PDF가 되고, 그 안에 일본어 글꼴 HaranoAjiMincho가 CID 글꼴로 삽입되어 있었습니다.

게다가 이 구조는 생각보다 훨씬 현역입니다. TeX Live 2024에서 dvipdfmx의 실체를 확인해 보면 xdvipdfmx로 가는 심볼릭 링크입니다. xdvipdfmx는 XeTeX이 내놓는 .xdv를 읽는 프로그램입니다. 곧 XeTeX의 속내는 “DVI를 조금 확장한 형식을 내고, 그것을 드라이버로 PDF로 바꾼다”는, 바로 DVI 경로 그 자체입니다. 실제로 xelatex -no-pdf를 쓰면 변환 직전에 멈춰 .xdv를 꺼낼 수 있습니다. 한 줄짜리 문서라면 .xdv는 436바이트. 그것을 xdvipdfmx에 넘기면 2,329바이트 PDF가 되는데, 같은 소스에 xelatex을 그대로 실행했을 때의 출력과 크기가 같습니다. 손으로 두 단계로 나눠 실행한 것이, xelatex이 내부에서 하던 일 그 자체였던 셈입니다.

terminal
$ readlink $(which dvipdfmx)
xdvipdfmx

$ xelatex -no-pdf one.tex     # stop before the driver stage
$ ls -l one.xdv
-rw-r--r--  1 user  staff  436 one.xdv

$ xdvipdfmx one.xdv           # run the driver by hand
$ ls -l one.pdf
-rw-r--r--  1 user  staff  2329 one.pdf

실무적 구분은 단순합니다. 새로 영문이나 라틴 문자 언어로 쓴다면 직접 PDF 경로(pdflatexlualatex)를 고르십시오. 공정이 한 단계 줄면 사고의 씨앗도 하나 줍니다. 일본어로 쓰면서 기존 자산이나 연구실의 관행이 있다면 DVI 경로(uplatex + dvipdfmx)가 여전히 견실하며, 세로쓰기와 일본어 금칙 처리에서 오랜 실적이 있습니다. 자세한 판단은 “엔진 고르는 법”에 맡기지만, 적어도 DVI를 “낡았으니 피해야 할 것”으로 여길 필요는 없습니다.

보조 파일과 처리 횟수: BibTeX・makeindex까지 포함한 전체 그림

“엔진을 한 번 돌리면 끝”인 것은 참조도 목차도 문헌도 색인도 없는 문서뿐입니다. 엔진은 한 번의 실행 동안 .aux(번호와 쪽의 기록)와 .toc(목차)를 써내고, 다음 실행이 그것을 다시 읽습니다. 이 왕복이 수렴할 때까지 되풀이하는 것이 기본형이며, \ref가 하나라도 나오는 순간 최소 두 번이 됩니다. 문헌이나 색인이 들어오면 엔진 이외의 프로그램이 끼어듭니다. .aux를 읽어 .bbl을 만드는 BibTeX(또는 .bcf를 읽는 biber), .idx를 읽어 .ind를 만드는 makeindex입니다. 그 출력들을 다시 엔진이 읽어야 하므로 실행 순서는 아래와 같아집니다.

terminal
# A document with cross-references, a bibliography and an index
pdflatex   thesis      # writes .aux, .idx; references still print as ??
bibtex     thesis      # reads .aux  -> writes .bbl
makeindex  thesis      # reads .idx  -> writes .ind
pdflatex   thesis      # pulls .bbl and .ind in; numbering shifts again
pdflatex   thesis      # everything settles

# Or simply
latexmk -pdf thesis    # figures out the order and the count on its own

횟수 세기를 그만두기: latexmk・편집기・SyncTeX

latexmk는 보조 파일의 변화를 지켜보며 문서에 필요한 만큼 정확히 다시 실행하는 도구입니다. .aux 등의 내용이 지난번과 같아지면 “수렴했다”고 판단해 멈추고, 도중에 BibTeX/biber나 makeindex가 필요하면 불러냅니다. 기본값은 최대 다섯 번 반복($max_repeat = 5)이고, 그래도 안정되지 않으면 무한 루프로 보고 중단합니다. 실제 문서가 이 상한에 닿는 일은 거의 없습니다. DVI 경로를 쓰고 싶다면 latexmk 설정 파일에 경로를 적어 두면 똑같이 한 명령으로 끝납니다.

terminal
latexmk -pdf      document.tex   # pdfLaTeX, as many passes as needed
latexmk -lualatex document.tex   # LuaLaTeX
latexmk -pv       document.tex   # build, then open the viewer
latexmk -c                       # remove .aux, .log, .toc and friends
latexmk -C                       # the same, and remove the PDF too

VS Code의 LaTeX Workshop, TeXShop, TeXstudio, Overleaf 같은 환경은 대개 뒤에서 latexmk를 부릅니다. 그래서 “컴파일” 단추를 누르는 것만으로 횟수 문제는 이미 해결되어 있습니다. 함께 켜 두면 좋은 것이 SyncTeX로, PDF의 각 위치가 소스 몇 번째 줄에서 왔는지를 .synctex.gz에 기록하는 장치입니다. 켜 두면 PDF의 한 점을 눌러 소스의 해당 줄로 뛰고, 반대로 소스에서 PDF의 해당 자리로도 뛸 수 있습니다. 문서가 길수록 더 값을 합니다.

멈췄을 때 어느 단계를 의심할 것인가

오류 메시지가 어느 단계에서 왔는지가 찾을 곳을 정합니다. .log에 엔진의 오류가 나오면 원고 쪽 문제이고, dvi 드라이버가 불평하면 그림이나 글꼴 쪽이며, BibTeX/biber의 출력이라면 .bib 쪽입니다. 특히 헷갈리는 증상이 “그림이 안 나온다”인데, 이것이 바로 경로 차이가 그대로 드러난 경우입니다. DVI 경로는 EPS를, 직접 PDF 경로는 PDF・PNG・JPEG를 무리 없이 다루므로, latex으로 컴파일하는 문서에 PNG를 넣으려다 실패하는 것은 아주 흔한 입구입니다. 로그를 읽는 법 자체는 “오류 추적” 페이지가 자세히 다룹니다.

  • 상호 참조만 이상할 때는 보조 파일을 지우기 전에, 먼저 latexmk에 필요한 횟수를 맡기고 다시 돌립니다.
  • 그림만 나오지 않을 때는 지금 돌리는 경로가 직접 PDF인지 DVI인지 확인하고, 그 경로가 다룰 수 있는 형식으로 변환합니다.
  • 원인을 알 수 없는 증상이 이어질 때에 한해 .aux.toc.out을 지우고 다시 만듭니다(latexmk -c). 낡은 보조 파일이 원인이었다면 이것으로 고쳐집니다.
  • 일본어 PDF에서 글꼴이 달라질 때dvipdfmx의 삽입 설정을 의심합니다. 엔진이 아니라 드라이버 쪽 이야기입니다.