TeX Live 2024 설치 디렉터리에서 ls -l을 쳐 보면 첫 번째 놀라움이 곧바로 나타납니다. 누구나 가장 먼저 배우는 LaTeX 컴파일 명령 latex는 프로그램이 아닙니다. pdftex를 가리키는 심볼릭 링크이며, pdflatex, etex, xmltex, amstex 등 19개의 이름이 같은 파일 하나를 가리키고 있습니다. 어떤 명령을 치느냐는 여전히 대단히 중요하지만, 그 이유는 대부분이 생각하는 것과 다릅니다. 이름이 고르는 것은 다른 프로그램이 아니라 다른 포맷 파일 이고, 두 명령의 차이가 정수 하나로 환원되는 경우도 있습니다. 이 페이지는 실제로 손으로 치는 명령들, 곧 pdflatex, xelatex, lualatex, latex + dvipdfmx, 그리고 CJK 경로인 platex와 uplatex를 다루고, 외워 둘 만한 몇 가지 옵션과 실행이 실패했을 때 콘솔을 읽는 법을 함께 정리합니다.
.tex 파일을 컴파일하는 방법과 실행이 남기는 파일
한 줄이면 됩니다. pdflatex document.tex를 치면 document.pdf가 만들어집니다. 확장자 .tex는 생략할 수 있고, 옵션은 언제나 파일 이름 앞에 놓습니다. 다만 돌아오는 것이 PDF만은 아닙니다. 같은 디렉터리에 두 파일이 더 생깁니다. 상호 참조와 목차 정보를 모으는 장부인 document.aux, 그리고 화면에 흘러간 내용을 전부 담고 그 이상까지 기록한 document.log입니다. 둘 다 지워도 되는 중간 산물이지만, 다음에 무슨 일이 일어날지를 정하는 것은 앞의 파일입니다.
pdflatex document.tex # -> document.pdf, document.aux, document.log
lualatex document # the .tex extension is optional
xelatex -synctex=1 document.tex # options come before the file name바로 그 .aux 파일 때문에 컴파일 명령은 보통 두 번 실행해야 합니다. LaTeX은 문서를 앞에서 뒤로 한 번만 읽으므로, 1쪽에서 목차를 조판하는 시점에는 7절이 몇 쪽에 놓일지 아직 모릅니다. 첫 번째 실행이 알아낸 번호를 .aux에 적고, 두 번째 실행이 그것을 읽어 본문에 채워 넣습니다. 참고 문헌과 색인이 끼어들면 왕복 횟수는 더 늘어납니다. LaTeX이 이 반복이 끝났다고 판단하는 방식과 latexmk가 그것을 자동화하는 방식은 자동 빌드 페이지가 맡습니다. 이 페이지가 다루는 것은 그 반복 안에서 실제로 호출되는 명령 자체입니다.
latex와 pdflatex의 차이는 정수 하나
latex는 .dvi를, pdflatex는 .pdf를 출력하지만, 그 아래에서 도는 바이너리는 같은 파일 입니다. TeX Live 2024에서 두 링크는 모두 pdftex로 이어지고, latex --version은 거리낌 없이 pdfTeX 3.141592653-2.6-1.40.26 (TeX Live 2024)라고 자신을 소개합니다. 차이는 프로그램이 아니라 이름마다 읽어 들이는 포맷 파일 쪽에 있습니다. pdflatex.ini는 pdftexconfig.tex를 불러오고, 거기서 \pdfoutput = 1이 설정됩니다. latex.ini는 바로 그 같은 파일을 불러온 뒤 곧바로 \pdfoutput=0으로 덮어씁니다. 끝까지 파고들면 두 명령을 갈라놓는 것은 이 정수 하나뿐입니다.
# TeX Live 2024: four commands, three binaries
readlink $(which latex) $(which pdflatex) $(which xelatex) $(which lualatex)
# pdftex
# pdftex
# xetex
# luahbtex
latex --version
# pdfTeX 3.141592653-2.6-1.40.26 (TeX Live 2024)그렇다면 하나의 바이너리는 어떤 포맷을 읽어야 하는지 어떻게 알까요. 자신이 어떤 이름으로 호출되었는지 를 봅니다. pdfTeX의 도움말은 이를 그대로 적어 두었습니다. 프로그램 호출 이름을 NAME이라 할 때 NAME.fmt를 찾는다는 것입니다. 링크 이름을 바꾸면 다른 LaTeX이 실행됩니다. 이 추정을 덮어쓰려면 -fmt=NAME이나 -progname=NAME을 넘기거나, 소스 파일 맨 첫 줄에 %&format 줄을 둡니다. 스무 개의 이름이 pdftex라는 실행 파일 하나에 매달릴 수 있는 것도 바로 이 장치 덕분입니다.
\pdfoutput은 포맷을 만들 때만 쓰이는 설정이 아니라, 실행 중에도 살아 있는 pdfTeX 프리미티브입니다. 파일의 맨 첫 줄, \documentclass보다 앞에 \pdfoutput=0을 적으면 pdflatex으로 돌려도 나오는 것은 .dvi입니다. 다만 실무에서는 명령줄이 더 깔끔합니다. -output-format=dvi 또는 -output-format=pdf를 쓰면 되고, pdfTeX와 LuaTeX가 모두 받아들입니다(XeTeX만 이 옵션이 없어 대신 -no-pdf를 씁니다). LaTeX 자신도 이 값을 읽어 그림 처리 방식을 바꿉니다. DVI 모드에서는 l3backend-dvips.def를, PDF 모드에서는 l3backend-pdftex.def를 읽어 들입니다. 한쪽 경로가 .eps 그림을 받고 다른 쪽이 .pdf와 .png를 받는 이유가 바로 이것입니다.
pdflatex와 xelatex, lualatex의 차이 — 글꼴과 Unicode로 고릅니다
판단 기준은 하나면 충분합니다. 문서가 라틴 문자 위주이고 속도와 패키지 호환성을 우선한다면 pdflatex을 씁니다. 운영체제에 이미 설치된 글꼴을 이름으로 지정하고 싶거나 라틴 문자 밖의 글자를 조판해야 하는 순간, xelatex이나 lualatex으로 옮겨 갑니다. 셋 다 PDF를 내고 같은 .tex 파일을 받지만, 글꼴이 들어오는 문만은 전혀 다릅니다.
명령이 왜 이렇게 늘어났을까요. 역사를 보면 한 줄로 설명됩니다. Knuth는 1978년부터 TeX을 쓰기 시작했고, 그 출력 형식은 DVI(device-independent)였습니다. Adobe가 PDF를 내놓은 것은 1993년이니, 당시에는 존재하지도 않았기 때문입니다. pdfTeX 는 그 간극을 메운 확장입니다. 저자 Hàn Thế Thành이 박사 연구에서 다룬 것은 마이크로타이포그래피, 즉 행 끝에서 글자를 판면 바깥으로 아주 조금 내밀고 글자 폭을 눈에 띄지 않을 만큼 늘이고 줄여 지면의 회색도를 고르게 만드는 기술이었습니다. DVI를 거치지 않고 PDF를 바로 쓰는 기능도 같은 연구에서 나왔습니다. 오늘날 pdflatex이 가장 빠르고 가장 많은 패키지가 이를 전제하는 이유는 단순합니다. 가장 오래 쓰여 왔기 때문입니다.
xelatex 은 Jonathan Kew가 2004년 무렵에 개발한 XeTeX을 실행합니다. fontspec 패키지를 쓰면 운영체제에 있는 OpenType 글꼴을 이름으로 지정해 그대로 사용할 수 있습니다. 다만 XeTeX은 PDF를 직접 쓰지 않습니다. DVI를 확장한 .xdv를 만들고, 이를 xdvipdfmx에 넘겨 PDF로 만듭니다. -no-pdf를 붙이면 .xdv 단계에서 멈추고, -output-driver=CMD로 변환 프로그램 자체를 바꿔 끼울 수도 있습니다. 셋 중 XeTeX만 -output-format 옵션이 없는 것은 이 구조에서 곧바로 따라 나오는 결과입니다.
lualatex 에는 반전이 하나 있습니다. TeX Live 2024에서 이 링크가 향하는 곳은 luatex이 아니라 luahbtex 입니다. LuaHBTeX는 글자 정형 라이브러리 HarfBuzz 를 내장한 LuaTeX 판으로, 아랍 문자나 인도계 문자처럼 정형 규칙이 복잡한 문자 체계를 제대로 조판할 수 있게 해 주는 것이 바로 이 부분입니다. 순수한 luatex도 그대로 남아 있으며, 뒤에 나올 dvilualatex가 그쪽을 가리킵니다. LuaTeX의 간판은 이름 그대로 내장된 Lua 인터프리터입니다. 문서 쪽에서 줄바꿈이나 글꼴 적재 같은 조판 내부 처리에 손을 댈 수 있습니다. 여기서 버전 번호를 나란히 놓아 보면 재미있는 점이 보입니다. tex --version은 TeX 3.141592653을 출력합니다. Knuth는 갱신할 때마다 원주율의 자릿수를 하나씩 늘리는 방식으로 TeX의 버전을 매겨 왔고, pdfTeX와 XeTeX도 그 접두사를 그대로 물려받았습니다. LuaTeX만이 이 전통에서 내려와 Version 1.18.0이라고 밝힙니다.
| 명령 | 실제 바이너리(TeX Live 2024) | 출력 | 글꼴과 문자 |
|---|---|---|---|
pdflatex | pdftex | TeX 내장 글꼴; 가장 빠르고 호환 범위가 가장 넓음 | |
xelatex | xetex | PDF(내부적으로 .xdv 경유) | fontspec으로 OS의 OpenType 글꼴 사용 |
lualatex | luahbtex | OS 글꼴 + HarfBuzz 정형 + Lua 스크립트 | |
latex | pdftex | DVI | TeX 내장 글꼴; EPS 그림과 PSTricks용 경로 |
dvilualatex | luatex | DVI | DVI 출력이 필요하면서 LuaTeX 기능을 쓸 때 |
platex | euptex | DVI | 일본어; 내부 인코딩 EUC, JIS X 0208 범위 |
uplatex | euptex | DVI | 일본어; 내부 인코딩이 Unicode라 희귀 문자도 처리 |
latex 다음에 dvipdfmx — DVI 경로가 아직 살아 있는 이유
이유는 지금도 두 가지입니다. 첫째, DVI만 알아듣는 구조가 남아 있습니다. PostScript를 전제로 그리는 패키지들, 그중에서도 PSTricks는 latex에서 dvips로 가는 경로에서 제 실력을 냅니다. 둘째, 다음 절에서 다룰 일본어 조판이 전통적으로 이 길을 지나왔습니다. latex document.tex가 document.dvi를 만들고, dvipdfmx document.dvi가 그것을 PDF로 바꿉니다. PostScript가 필요하면 dvips를 씁니다. LuaTeX 기능을 쓰면서도 DVI를 원한다는 드문 요구에는 dvilualatex가 있으며, 이쪽은 HarfBuzz가 없는 순수 luatex을 가리킵니다.
latex document.tex # -> document.dvi
dvipdfmx document.dvi # -> document.pdf
dvips document.dvi # -> document.ps (for PSTricks and friends)CJK 명령 — platex와 uplatex, 그리고 대안
일본어에 전용 명령이 있는 것은 일본어 조판에 전용 규칙이 있기 때문입니다. 세로쓰기, 행의 처음과 끝에 올 수 있는 문자를 제약하는 금칙 처리, 그리고 일본어와 라틴 문자 사이에 들어가야 할 고정 간격이 그것입니다. pTeX 는 이 규칙들을 매크로가 아니라 엔진 자체에 심었고, 그 위에서 도는 LaTeX이 pLaTeX, 곧 platex 명령입니다. 포맷 파일 platex.ini의 첫 줄에는 지금도 “for pLaTeX (ASCII Nihongo LaTeX)”라고 적혀 있어 ASCII에서 비롯되었음을 알려 줍니다. upTeX 은 다나카 다쿠지가 pTeX을 확장해 내부 문자 코드를 완전한 Unicode로 바꾼 것이고, 그 위의 LaTeX이 upLaTeX, 곧 uplatex 명령입니다. 둘 다 출력은 언제나 DVI이며 PDF를 직접 쓰지 않습니다.
같은 놀라움이 여기서도 되풀이됩니다. TeX Live 2024에서 platex와 uplatex는 둘 다 euptex 라는 하나의 바이너리로 이어지는 링크이고, ptex, eptex, uptex도 같은 곳으로 모입니다. 이들을 가르는 것은 내부 한자 인코딩입니다. platex --version은 e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.euc)을 출력하고, uplatex --version은 끝이 (utf8.uptex)입니다. 곧 하나의 실행 파일이 호출된 이름에 따라 -kanji-internal을 바꿀 뿐입니다. pLaTeX은 내부 EUC의 고전 pTeX처럼 동작하므로 다룰 수 있는 한자가 대체로 JIS X 0208 안에 머무르고, upLaTeX은 내부가 Unicode여서 희귀 인명 한자와 CJK 통합 한자 전체를 그대로 통과시킵니다. 새 일본어 문서에서 uplatex이 기본 선택이 된 것은 바로 이 한 가지 차이 때문입니다. 이 통합은 비교적 최근의 변화이고 날짜도 분명합니다. TeX Live에 함께 배포되는 pTeX 공식 안내서는 platex이 TeX Live 2012부터 2022까지 e-pTeX 위에서 돌았고, 2023-06-01에 e-upTeX의 legacy-encoding-compatibility mode로 옮겨 갔다고 기록합니다. 그 결과 upTeX의 추가 프리미티브를 pLaTeX에서도 쓸 수 있게 되었지만, 일본어 문자의 내부 코드는 하위 호환을 위해 의도적으로 비Unicode로 남겨 두었습니다.
내부 인코딩과 입력 파일의 문자 인코딩은 서로 다른 것입니다. 입력 쪽은 -kanji=STRING으로 지정하며 euc, jis, sjis, utf8, uptex를 받습니다. 최근 TeX Live는 UTF-8을 기본으로 하므로 생략할 수 있는 경우가 많지만, 명시해 두면 환경이 바뀌어도 빌드가 흔들리지 않습니다. BOM 없는 UTF-8을 확실히 통과시키려면 -no-guess-input-enc를 함께 써서 인코딩 추측 자체를 끕니다. 출력은 DVI이므로 마무리는 dvipdfmx가 맡습니다.
# Japanese, the traditional route: typeset -> DVI -> PDF
uplatex -kanji=utf8 -no-guess-input-enc document.tex # -> document.dvi
dvipdfmx document.dvi # -> document.pdf중국어와 한국어는 이 경로를 아예 쓰지 않습니다. 둘 다 Unicode 네이티브 엔진에 맡기는 것이 정석이어서, 중국어는 xelatex이나 lualatex에 ctex 번들(내부에서 xeCJK 등을 부릅니다)을, 한국어는 같은 엔진에 kotex을 조합합니다. 일본어에도 같은 선택지가 있어서, lualatex에 luatexja를 읽히면 PDF가 곧바로 나옵니다. 이 길을 택하면 -kanji는 필요 없어집니다. 처음부터 끝까지 Unicode로 도는 엔진에는 바꿀 내부 인코딩 자체가 없기 때문입니다.
쓸 만한 옵션 — -interaction=nonstopmode, -halt-on-error, -output-directory
옵션은 언제나 파일 이름보다 앞에 놓으며, 아래 항목은 pdfTeX, XeTeX, LuaTeX 어느 명령에서도 통합니다. 매일 값을 하는 것은 사실상 네 가지입니다. 에디터 연동을 위한 -synctex=1, 실행이 멈추지 않게 하는 -interaction=nonstopmode, 반대로 첫 문제에서 손을 떼는 -halt-on-error, 그리고 메시지를 기계가 읽을 수 있는 형태로 만드는 -file-line-error입니다.
| 옵션 | 역할 |
|---|---|
-synctex=1 | document.synctex.gz를 써서 에디터와 PDF 사이의 상호 이동을 가능하게 함 |
-interaction=nonstopmode | 오류에서 입력을 기다리지 않고 끝까지 실행; batchmode는 터미널 출력도 억제 |
-halt-on-error | 첫 번째 오류에서 중단; PDF는 생성되지 않음 |
-file-line-error | 메시지 머리를 ./document.tex:3: 형태로 바꿔 IDE와 CI가 해석할 수 있게 함 |
-output-directory=DIR | 출력과 보조 파일을 DIR에 씀; DIR은 미리 존재해야 함 |
-jobname=NAME | 모든 출력 파일에서 확장자 앞부분의 이름을 NAME으로 지정 |
-draftmode | PDF를 쓰지 않고 실행(pdfTeX / LuaTeX); 참조만 확정하는 중간 실행에 유용 |
-output-format=FORMAT | dvi나 pdf를 선택(pdfTeX와 LuaTeX 전용; XeTeX은 -no-pdf) |
-shell-escape | \write18을 통한 외부 명령 실행 제한을 모두 해제(다음 절의 경고를 읽을 것) |
pdflatex -synctex=1 -interaction=nonstopmode -halt-on-error -file-line-error document.tex
mkdir -p build # -output-directory will NOT create it for you
pdflatex -output-directory=build document.tex-output-directory에는 CI 파이프라인이 자주 걸리는 함정이 있습니다. 지정한 디렉터리는 이미 존재해야 합니다. pdfTeX의 도움말도 그렇게 적어 두었습니다. 기존 DIR을 사용한다는 것입니다. 없으면 실행은 먼저 “Please type another transcript file name”이라고 말하고, 이어서 ! Emergency stop과 “Fatal error occurred, no output PDF file produced!”로 끝납니다. 원인이 조판과 무관하기 때문에 LaTeX 로그를 익숙하게 읽는 사람도 잠시 멈칫합니다. 앞 줄에 mkdir -p 한 줄이면 끝나는 일입니다.
-shell-escape — minted에 필요한 이유와 위험한 이유
이 옵션은 문서에 여러분의 컴퓨터에서 임의의 셸 명령을 실행할 권리 를 줍니다. 다만 기본 상태에서도 외부 명령이 완전히 막혀 있는 것은 아닙니다. TeX Live는 제한 모드 로 동작하며, 실행할 때마다 restricted \write18 enabled.라는 줄을 출력합니다. 이 상태에서 \write18이 부를 수 있는 것은 texmf.cnf의 허용 목록에 있는 프로그램뿐이고, TeX Live 2024의 목록은 짧습니다. bibtex, bibtex8, extractbb, gregorio, kpsewhich, makeindex, memoize-extract.pl, memoize-extract.py, repstopdf, r-mpost, texosquery-jre8입니다. 참고 문헌과 색인이 아무 옵션 없이도 동작하는 것은 이 둘이 처음부터 허용되어 있기 때문입니다.
그 목록에 pygmentize는 없습니다. texmf.cnf는 이를 의도적으로 빼 두었고, 필터 기능이 안전한지 의문이라는 취지의 주석을 붙여 두었습니다. 코드에 색을 입히는 minted가 부르는 것이 바로 그 프로그램이므로, 제한 모드에서는 동작하지 않고 -shell-escape를 통째로 여는 수밖에 없습니다. 그리고 “통째로”는 말 그대로입니다. 남에게서 받은 .tex는 그 한 번의 컴파일로 파일을 지우고, 로컬 데이터를 밖으로 보내고, 무언가를 심어 놓을 수 있습니다. 판단 기준은 단순해도 됩니다. 자신이 쓴 문서를, 자신이 관리하는 디렉터리에서 조판할 때만 켭니다. 내려받은 템플릿이나 심사로 넘어온 투고 원고에는 절대 붙이지 않습니다.
# minted calls pygmentize, which the restricted allow-list does not include
pdflatex -shell-escape document.tex
# turn it off explicitly when compiling a file you did not write
pdflatex -no-shell-escape untrusted.tex컴파일이 실패했을 때 콘솔 출력을 읽는 법
끝이 아니라 !로 시작하는 첫 줄부터 읽습니다. LaTeX 오류는 연쇄되기 때문에 화면 마지막에 남아 있는 것은 대개 첫 오류가 일으킨 2차 피해이고, 원인은 그보다 위에 있습니다. 오류 보고는 언제나 같은 모양입니다. ! 줄이 증상을 말하고, 그 아래 l.로 시작하는 줄이 위치를 알려 줍니다.
! Missing $ inserted.
<inserted text>
$
l.3 Some text with a bare x^
2 here.
?중요한 것은 l.3 줄입니다. 소스 3행이라는 뜻이지만, TeX이 읽어 낸 지점에서 정확히 둘로 접혀 표시됩니다. 접힌 자리 앞은 이미 소비된 부분이고, 뒤는 아직 읽지 않은 부분입니다. 이 예에서는 접힌 자리가 x^ 바로 뒤에 있으므로 위 첨자 기호가 범인임이 한눈에 보입니다. ! Undefined control sequence.도 같은 모양이어서, 접힌 자리 바로 앞에 놓인 철자가 수상한 명령이 그대로 답이 됩니다. 마지막의 ?는 입력 대기 프롬프트로, 기본값인 errorstopmode에서 멈춰 있다는 표시입니다. Enter를 누르면 계속되고, x를 치면 중단됩니다.
이 대화가 방해가 되는 곳이 스크립트와 CI이고, 그래서 상호작용 모드를 바꿉니다. -interaction=nonstopmode는 멈추지 않고 전부 출력하고, batchmode는 터미널 출력까지 억제하며, scrollmode는 파일을 찾지 못할 때만 묻습니다. 반대로 첫 문제만 보고 싶다면 -halt-on-error를 쓰며, 이 경우 실행은 ! Emergency stop.과 “Fatal error occurred, no output PDF file produced!”로 끝납니다. 여기에 -file-line-error를 더하면 머리글이 ./document.tex:3: Missing $ inserted.로 바뀌어, 에디터와 CI가 클릭 가능한 링크로 해석할 수 있습니다. 무엇을 고르든 완전한 기록은 언제나 .log에 남습니다. 터미널에서 놓쳤다면 그 파일을 열면 같은 내용이 있습니다.
결국 어떤 컴파일 명령을 쳐야 할까
- 라틴 문자 위주의 문서 —
pdflatex. 가장 빠르고, 대부분의 패키지가 전제하는 기본값입니다. - 운영체제의 글꼴을 이름으로 쓰고 싶을 때 —
xelatex또는lualatex. 둘 다fontspec으로 지정합니다. - 정형 규칙이 복잡한 문자 체계나 조판 자체를 스크립트하고 싶을 때 —
lualatex(실체는 HarfBuzz가 든luahbtex). - 새 일본어 문서 —
uplatex다음dvipdfmx, 또는lualatex에luatexja. - 중국어와 한국어 —
xelatex이나lualatex에ctex또는kotex. DVI는 거치지 않습니다. - PSTricks 등 DVI만 아는 구조 —
latex다음dvips또는dvipdfmx.
마지막으로 문제를 가르는 습관을 하나 덧붙입니다. 에디터의 빌드 버튼이 실패하면, 먼저 같은 명령을 터미널에서 직접 쳐 보십시오. 거기서 통과한다면 문제는 문서가 아니라 에디터 설정에 있습니다. 남에게 넘기거나 CI에 올리기 전에는 -halt-on-error -file-line-error -interaction=nonstopmode를 붙여 한 번 돌려, 첫 번째 실제 오류 위치가 읽히도록 해 둡니다. 그리고 일상에서는 이 명령들을 손으로 두 번 치는 사람이 거의 없습니다. 실행 횟수를 세고 dvipdfmx까지 불러 주는 latexmk에 맡기는 것이 보통이며, 그것이 언제 멈출지를 아는 방식은 자동 빌드 페이지가 다룹니다.