pdflatex이 무엇이냐고 물으면 정확한 답은 "프로그램 이름"이 아닙니다. pdfTeX이라는 엔진을 LaTeX이라는 포맷을 실어 둔 채로 띄운 것입니다. pdflatex・lualatex・platex처럼 명령 이름이 나란히 놓이면 서로 다른 소프트웨어처럼 보이지만, 실제로는 엔진(실행되는 프로그램) × 포맷(미리 컴파일해 둔 매크로 묶음)이라는 두 축의 조합일 뿐입니다. 이 축이 보이면 .fmt 파일이 무엇인지, 왜 latex을 실행하면 PDF가 아니라 DVI가 나오는지, 그리고 LaTeX3은 결국 어떻게 되었는지가 한꺼번에 설명됩니다.
엔진과 포맷의 차이
엔진은 실행 파일이고, 포맷은 데이터입니다. 엔진은 .tex을 읽어 조판하는 실행 파일 그 자체로, tex・pdftex・xetex・luahbtex・euptex 따위가 있습니다. 포맷은 \documentclass나 \section 같은 매크로를 대량으로 모아 미리 전개해 하나의 덤프(.fmt 파일)로 굳혀 놓은 것입니다. 시작할 때 엔진이 그 .fmt을 순식간에 읽어 들이므로, 실행할 때마다 수만 줄의 매크로를 다시 정의하지 않아도 됩니다.
그러므로 "LaTeX"은 포맷의 이름이지 엔진의 이름이 아닙니다. 같은 LaTeX 포맷을 다른 엔진 위에서 돌릴 수 있고, 여러분이 치는 명령 이름이 바로 그 엔진+포맷의 짝을 가리킵니다. 이 대응표는 추측할 필요가 없습니다. TeX Live 안에 fmtutil.cnf라는 한 장의 설정 파일로 실재합니다. 서식은 포맷 이름/엔진 이름/하이픈 패턴 파일/인수의 네 열이고, TeX Live 2024 판에는 54줄, 곧 54가지 조합이 등록되어 있습니다.
$ grep -E '^(tex|latex|pdflatex|lualatex|xelatex|uplatex|amstex|pdfcsplain) ' \
$(kpsewhich fmtutil.cnf)
# format engine hyphenation arguments
tex tex - tex.ini
latex pdftex language.dat *latex.ini
pdflatex pdftex language.dat *pdflatex.ini
xelatex xetex language.dat -etex xelatex.ini
lualatex luahbtex language.dat,language.dat.lua lualatex.ini
uplatex euptex language.dat *uplatex.ini
amstex pdftex - *amstex.ini
pdfcsplain luatex - csplain.ini
pdfcsplain pdftex - csplain-utf8.ini
pdfcsplain xetex - csplain.ini이 표에서 두 가지를 읽어 낼 수 있습니다. 첫째, 같은 포맷 이름이 여러 엔진에 나타날 수 있다는 것——pdfcsplain은 luatex・pdftex・xetex의 세 줄을 가집니다. 포맷과 엔진이 직교한다는 데 이보다 나은 증거는 없습니다. 둘째, 인수 앞에 붙는 *. e-TeX 확장을 켠 채로 덤프하라는 지시이며, 파일 자신의 머리말 주석이 "-etex 옵션과 본질적으로 같다"고 설명합니다. latex 줄에는 *가 있고 커누스의 tex 줄에는 없습니다. 현대 LaTeX이 e-TeX을 전제한다는 사실이 이 한 글자에 응축되어 있습니다.
.fmt 파일 안에는 무엇이 들어 있는가
엔진의 내부 상태를 통째로 저장한 메모리 덤프입니다. 소스 코드도 텍스트도 아닙니다. 매크로 정의, 카테고리 코드, 하이픈 패턴, 수식 글꼴 배정——latex.ltx을 끝까지 읽어 낸 시점에 엔진의 기억에 있는 것이 그대로 이진 형태로 쓰여 나옵니다. TeX Live 2024의 latex.fmt은 약 8.2 MB입니다. 이 8.2 MB를 다시 읽는 시간과 latex.ltx을 매번 다시 해석하는 시간의 차이가, LaTeX이 순식간에 뜨는 이유입니다.
엔진과 포맷의 관계가 파일 시스템의 모양 그대로 보이는 자리가 있습니다. .fmt은 "포맷 이름"이 아니라 엔진 이름의 디렉터리에 놓입니다. TeX Live 2024의 texmf-var/web2c/ 아래에는 tex/・pdftex/・xetex/・luahbtex/・euptex/ 같은 디렉터리가 늘어서 있고, 각각 그 엔진으로 덤프한 포맷이 들어 있습니다. tex/tex.fmt은 plain TeX, pdftex/latex.fmt은 LaTeX, euptex/uplatex.fmt은 upLaTeX——같은 .fmt 확장자라도 어느 엔진으로 만들었는지에 따라 별개의 것입니다.
# Formats are filed by ENGINE, not by format name.
$ ls /usr/local/texlive/2024/texmf-var/web2c/
aleph euptex hitex luahbtex luajittex luatex metafont pdftex tex xetex
$ ls /usr/local/texlive/2024/texmf-var/web2c/euptex/
eptex.fmt euptex.fmt platex.fmt ptex.fmt uplatex.fmt ...
# Ask kpathsea which .fmt a given engine would load.
$ kpsewhich -engine pdftex latex.fmt
/usr/local/texlive/2024/texmf-var/web2c/pdftex/latex.fmt
$ ls -l $(kpsewhich -engine pdftex latex.fmt)
-rw-r--r-- 1 root wheel 8221690 May 4 2024 .../pdftex/latex.fmt포맷을 만드는 쪽의 구조도 단순합니다. 엔진을 -ini를 붙여 띄우면 .fmt을 읽지 않는 "초기화 모드"가 됩니다. 그 상태에서 latex.ltx 같은 소스를 읽히고 마지막에 프리미티브 \dump를 실행하면, 그때의 내부 상태가 .fmt으로 쓰여 나옵니다. 한때 별도의 실행 파일이었던 initex은 이 -ini 기동의 다른 이름일 뿐이며, 이제는 tex을 가리키는 심볼릭 링크로만 남아 있습니다.
포맷을 다시 만들기 — fmtutil
.fmt을 다시 만드는 명령이 fmtutil입니다. fmtutil.cnf을 읽어, 거기 적힌 엔진과 인수로 포맷을 하나씩 다시 덤프합니다. 보통은 배포판을 갱신할 때 자동으로 돌기 때문에 손으로 칠 일은 많지 않지만, 커널이나 하이픈 패턴을 바꿔 넣었는데 반영되지 않을 때는 대개 포맷이 옛것 그대로입니다. 실제로 쓰게 되는 형태는 fmtutil-sys --all(전부 다시 만들기)과 fmtutil-sys --byfmt latex(하나만) 둘입니다.
# Rebuild one format (writes into the system tree; needs write permission).
$ fmtutil-sys --byfmt latex
# Rebuild every format listed in fmtutil.cnf.
$ fmtutil-sys --all
# Rebuild only the formats that use a given engine.
$ fmtutil-sys --byengine luahbtex
# Personal tree instead of the system tree (no root needed).
$ fmtutil-user --byfmt pdflatex
# Where did the format end up, and when was it built?
$ kpsewhich -engine pdftex latex.fmt-sys와 -user의 차이는 어디에 쓰느냐입니다. 시스템 트리(TEXMFSYSVAR)냐, 사용자별 트리(TEXMFVAR)냐입니다. 관리자 권한이 있는 환경이라면 -sys가 자연스럽고, 공용 기기에서 자기 몫만 바꾸고 싶을 때 -user를 고릅니다. 다만 둘을 뒤섞으면 "고쳤는데도 안 고쳐졌다"의 온상이 되므로, 프로젝트에서는 한쪽으로 통일해 두는 편이 안전합니다. 또한 fmtutil.cnf 자체는 배포판의 관리 아래 있으며, 파일 첫머리 주석이 "이 파일을 손으로 고쳐도 갱신 때 사라진다"고 못 박고 있습니다. 영구히 바꾸려면 fmtutil-sys --enablefmt 같은 마련된 절차를 씁니다.
LaTeX2e — 지금 돌고 있는 포맷의 정체
오늘날 "LaTeX"이라고 하면 거의 예외 없이 LaTeX2e를 가리킵니다. 레슬리 램포트가 만든 LaTeX 매크로의 현행판으로, 1994년에 등장했고 지금은 LaTeX Project가 관리합니다. "2e"는 앞선 LaTeX 2.09로부터의 "작은 개정"을 뜻하며, 당시에는 오래 계획된 LaTeX3으로 가는 중간 단계로 여겨졌습니다. 손에 있는 포맷이 무엇인지는 스스로 확인할 수 있습니다. 커널 latex.ltx은 \def\fmtname{LaTeX2e}이라 밝히고, TeX Live 2024 동봉판에서는 \fmtversion이 2023-11-01, \patch@level이 1입니다.
커널은 이제 한 해에 두 번쯤의 정기 릴리스로 갱신됩니다. "그럼 최신판은 무엇인가"를 숫자로 외워 두는 것은 의미가 없습니다. 반년이면 낡기 때문입니다. 대신 자기 환경에서 latex.ltx의 \fmtversion을 보는 습관을 들이세요. 그것이 지금 실제로 덤프되어 있는 커널의 날짜입니다. 다음 릴리스를 앞당겨 시험해 보고 싶은 사람을 위해 TeX Live는 개발판 포맷도 나란히 마련해 두었습니다. fmtutil.cnf에는 latex-dev・pdflatex-dev・xelatex-dev・lualatex-dev・uplatex-dev 줄이 있어, pdflatex-dev main.tex처럼 치면 다음 커널로 조판할 수 있습니다. 실제 원고가 아니라 클래스와 패키지의 호환성 검사에 쓰는 것입니다.
LaTeX3은 결국 어떻게 되었나 — 답은 expl3
LaTeX3이라는 "포맷"은 끝내 나오지 않았습니다. 나온 것은 LaTeX3의 프로그래밍 언어 쪽입니다. expl3(L3 programming layer)이라 불리는 그 층은 독립된 새 커널이 아니라 LaTeX2e 안쪽으로 흡수되는 형태로 실현되었습니다. 그러므로 "LaTeX3으로 옮겨야 하나"라는 물음에는 답이 없습니다. 옮겨 갈 곳이 없어서가 아니라 이미 쓰고 있기 때문입니다.
그 전환점은 기록에 남아 있습니다. LaTeX News 31(2020년 2월 2일)은 expl3이 지난 10년 사이에 "대체로 실험적인 것에서 널리 안정된 것으로 옮겨 왔다"고 적었고, 그 릴리스에서 커널이 포맷을 만들 때 expl3의 상당 부분을 미리 읽어 두도록 조정했다고 보고했습니다. 동기는 속도였습니다. XeLaTeX이나 LuaLaTeX에서 fontspec을 읽어 들이는 문서는 실행할 때마다 유니코드 데이터를 읽느라 큰 비용을 치르고 있었기 때문입니다.
이제 expl3은 포맷의 일부입니다. 커널 latex.ltx을 읽어 보면 그 처리가 보입니다. expl3이 이미 있으면 Skipping: expl3 code already part of the format이라 알리고 넘어가며, 없으면 \input expl3.ltx으로 읽어 들이고, expl3.ltx 자체를 찾지 못하면 \errmessage{LaTeX requires expl3}으로 멈춥니다. 곧 expl3은 더 이상 선택적 패키지가 아니라 LaTeX2e의 필수 부품이라는 뜻입니다. 문서나 패키지에서의 \RequirePackage{expl3}은 옛 포맷을 쓰는 사람을 위해 여전히 권장되지만, 최신 포맷에서는 사실상 아무 일도 하지 않습니다. expl3의 문법 자체——\ExplSyntaxOn, \cs_new:Npn, l3kernel의 형 있는 변수——는 전용 페이지가 다룹니다.
LaTeX 말고 다른 포맷들
LaTeX은 가장 널리 쓰이는 포맷이지만 유일한 것은 아닙니다. fmtutil.cnf에 늘어선 54줄 가운데 LaTeX 계열은 극히 일부이고, 나머지는 다른 매크로 체계입니다. 여기서 amstex를 짚어 둘 만합니다. 실행 파일로서의 amstex은 pdftex을 가리키는 심볼릭 링크일 뿐이고, fmtutil.cnf의 amstex pdftex - *amstex.ini 줄이 pdfTeX 엔진에 AMS-TeX 매크로를 실은 포맷을 지정합니다. AMS-TeX은 미국수학회가 plain TeX 위에 만든 수학 조판용 매크로 모음이며, LaTeX을 쓰는 사람에게는 사실상 amsmath 패키지로 대체되었습니다. 새 문서를 AMS-TeX으로 시작할 이유는 거의 없습니다.
| 포맷 | 덤프하는 엔진 | 무엇인가 |
|---|---|---|
tex | tex | 커누스의 plain TeX. e-TeX 확장을 갖지 않은 유일한 포맷 |
latex | pdftex | LaTeX2e. DVI 모드의 pdfTeX으로 돌며 .dvi를 냅니다 |
pdflatex | pdftex | 같은 LaTeX2e를 PDF 직접 출력으로. 서구어 문서의 사실상 기본 |
xelatex | xetex | LaTeX2e를 XeTeX 위에서. 시스템 글꼴과 유니코드 |
lualatex | luahbtex | LaTeX2e를 LuaHBTeX 위에서. Lua 스크립트와 HarfBuzz를 쓸 수 있습니다 |
platex / uplatex | euptex | 일본어 조판용 LaTeX2e. DVI를 내고 dvipdfmx로 PDF로 바꿉니다 |
amstex | pdftex | AMS-TeX. plain TeX 위의 수학용 매크로. LaTeX에서는 amsmath로 대체되었습니다 |
cont-en | pdftex / xetex | ConTeXt(MkII). LaTeX과 다른 계통의 종합 포맷. 두 엔진에 등록되어 있습니다 |
csplain | pdftex | 체코어・슬로바키아어를 위한 plain TeX 파생. -etex을 명시해 만듭니다 |
pLaTeX / upLaTeX — 일본어를 위한 LaTeX 포맷
pLaTeX과 upLaTeX은 일본어 조판용 엔진에 LaTeX 포맷을 얹은 것입니다. 세로쓰기, 루비(후리가나), 일본어 행 조판 같은 고유 처리는 아스키가 확장한 pTeX 계열 엔진이 맡습니다. TeX Live 2024의 fmtutil.cnf를 보면 platex도 uplatex도 euptex 엔진 줄에 등록되어 있고, 포맷 쪽 초기화 파일(*platex.ini과 *uplatex.ini)로 동작을 가른다는 것을 알 수 있습니다. 따라서 둘의 차이는 실행 파일이 아니라 포맷의 문자 처리 방식에 있습니다. platex은 전통적인 pTeX 호환 동작을 지키고, uplatex은 내부 유니코드를 전제합니다. 새 일본어 문서라면 uplatex이 표준적인 선택입니다.
이 계통은 반드시 DVI를 출력하고, 일본어가 담긴 DVI를 dvipdfmx로 PDF로 바꾸는 2단 구성입니다(uplatex file.tex 다음에 dvipdfmx file.dvi). 색인에는 upmendex를 함께 씁니다. 클래스 파일로는 오쿠무라 하루히코의 jsarticle・jsbook(pLaTeX용)과 ujarticle 같은 유니코드판, 그리고 JIS X 4051의 일본어 조판 요건을 따르는 비교적 새로운 jlreq가 널리 쓰입니다. 엔진 내부의 세부——JFM 메트릭, 금칙 처리, 세로쓰기 구현——는 "pTeX 패밀리" 페이지가 다룹니다.
어느 명령이 어떤 조합인가
같은 LaTeX 포맷이라도 명령 이름이 뒤에 있는 엔진, 출력 형식, 글꼴 처리 방식을 정합니다. 아래 표는 명령 이름을 기점으로 왼쪽에서 오른쪽으로 읽어 주세요. 실무에서 가장 크게 작용하는 것은 맨 오른쪽 열입니다. fontspec을 쓸 수 있는지가 곧 엔진 선택으로 이어집니다.
| 명령 | 엔진 | 출력 | 유니코드 / 시스템 글꼴 |
|---|---|---|---|
latex | pdfTeX(DVI 모드) | DVI — 별도 도구로 변환이 필요합니다 | 불가. inputenc 등에 기댑니다 |
pdflatex | pdfTeX | PDF를 직접 | 불가. inputenc 등에 기댑니다 |
platex | e-upTeX(pLaTeX 호환 설정) | DVI → dvipdfmx | 일본어는 전통적인 글꼴 설정으로 |
uplatex | e-upTeX | DVI → dvipdfmx | 내부 유니코드. 일본어 글꼴은 전통 방식 |
lualatex | LuaHBTeX | PDF를 직접 | 가능 — fontspec / luatexja |
xelatex | XeTeX | PDF(내부적으로 xdvipdfmx를 거쳐) | 가능 — fontspec |
# Engines that write PDF finish in one step.
$ pdflatex main.tex # -> main.pdf
$ lualatex main.tex # -> main.pdf
$ xelatex main.tex # -> main.pdf
# DVI-based routes add a conversion step.
$ latex main.tex # -> main.dvi (pdfTeX in DVI mode)
$ uplatex main.tex # -> main.dvi (Japanese)
$ dvipdfmx main.dvi # -> main.pdf로그 첫 줄을 읽고, 조합을 고정하기
로그 첫 줄에는 엔진과 포맷이 함께 적혀 있습니다. This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=latex)를 나눠 보면, 앞부분이 엔진과 그 버전이고 (preloaded format=...)이 읽어 들인 포맷입니다. This is LuaHBTeX ..., This is XeTeX ..., This is e-upTeX ...도 같은 방식으로 읽습니다. 협업에서 "같은 명령인데 출력이 다르다"는 말이 나올 때, 이 한 줄만 맞춰 보면 편집기 설정 문제인지 TeX Live 연도나 포맷의 차이인지가 곧바로 갈립니다.
그리고 조합이 정해지면 프로젝트에 적어 두고 고정합니다. 학위 논문이라면 표지・목차・도표・참고문헌까지 다 통과한 시점에 명령을 README나 .latexmkrc에 적어 둡니다. 도중에 lualatex에서 uplatex으로 옮기면 글꼴 선택, 일본어 처리, PDF 생성 경로, 나아가 참고문헌 처리까지 한꺼번에 바뀝니다. 굳이 바꿔야 한다면 본 원고로 돌아가기 전에 작은 시험 파일로 모든 기능을 확인하세요. 애초에 무엇을 고를지에 대한 판단 재료——속도, 패키지 호환성, 일본어 사정——는 "엔진 고르기" 페이지에 정리되어 있습니다.