LaTeX 포맷

pdflatex이 무엇이냐고 물으면 정확한 답은 "프로그램 이름"이 아닙니다. pdfTeX이라는 엔진을 LaTeX이라는 포맷을 실어 둔 채로 띄운 것입니다. pdflatexlualatexplatex처럼 명령 이름이 나란히 놓이면 서로 다른 소프트웨어처럼 보이지만, 실제로는 엔진(실행되는 프로그램) × 포맷(미리 컴파일해 둔 매크로 묶음)이라는 두 축의 조합일 뿐입니다. 이 축이 보이면 .fmt 파일이 무엇인지, 왜 latex을 실행하면 PDF가 아니라 DVI가 나오는지, 그리고 LaTeX3은 결국 어떻게 되었는지가 한꺼번에 설명됩니다.

엔진과 포맷의 차이

엔진은 실행 파일이고, 포맷은 데이터입니다. 엔진은 .tex을 읽어 조판하는 실행 파일 그 자체로, texpdftexxetexluahbtexeuptex 따위가 있습니다. 포맷은 \documentclass\section 같은 매크로를 대량으로 모아 미리 전개해 하나의 덤프(.fmt 파일)로 굳혀 놓은 것입니다. 시작할 때 엔진이 그 .fmt을 순식간에 읽어 들이므로, 실행할 때마다 수만 줄의 매크로를 다시 정의하지 않아도 됩니다.

그러므로 "LaTeX"은 포맷의 이름이지 엔진의 이름이 아닙니다. 같은 LaTeX 포맷을 다른 엔진 위에서 돌릴 수 있고, 여러분이 치는 명령 이름이 바로 그 엔진+포맷의 짝을 가리킵니다. 이 대응표는 추측할 필요가 없습니다. TeX Live 안에 fmtutil.cnf라는 한 장의 설정 파일로 실재합니다. 서식은 포맷 이름/엔진 이름/하이픈 패턴 파일/인수의 네 열이고, TeX Live 2024 판에는 54줄, 곧 54가지 조합이 등록되어 있습니다.

terminal
$ 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

이 표에서 두 가지를 읽어 낼 수 있습니다. 첫째, 같은 포맷 이름이 여러 엔진에 나타날 수 있다는 것——pdfcsplainluatexpdftexxetex의 세 줄을 가집니다. 포맷과 엔진이 직교한다는 데 이보다 나은 증거는 없습니다. 둘째, 인수 앞에 붙는 *. 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 확장자라도 어느 엔진으로 만들었는지에 따라 별개의 것입니다.

terminal
# 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(하나만) 둘입니다.

terminal
# 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 동봉판에서는 \fmtversion2023-11-01, \patch@level1입니다.

커널은 이제 한 해에 두 번쯤의 정기 릴리스로 갱신됩니다. "그럼 최신판은 무엇인가"를 숫자로 외워 두는 것은 의미가 없습니다. 반년이면 낡기 때문입니다. 대신 자기 환경에서 latex.ltx\fmtversion을 보는 습관을 들이세요. 그것이 지금 실제로 덤프되어 있는 커널의 날짜입니다. 다음 릴리스를 앞당겨 시험해 보고 싶은 사람을 위해 TeX Live는 개발판 포맷도 나란히 마련해 두었습니다. fmtutil.cnf에는 latex-devpdflatex-devxelatex-devlualatex-devuplatex-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를 짚어 둘 만합니다. 실행 파일로서의 amstexpdftex을 가리키는 심볼릭 링크일 뿐이고, fmtutil.cnfamstex pdftex - *amstex.ini 줄이 pdfTeX 엔진에 AMS-TeX 매크로를 실은 포맷을 지정합니다. AMS-TeX은 미국수학회가 plain TeX 위에 만든 수학 조판용 매크로 모음이며, LaTeX을 쓰는 사람에게는 사실상 amsmath 패키지로 대체되었습니다. 새 문서를 AMS-TeX으로 시작할 이유는 거의 없습니다.

포맷덤프하는 엔진무엇인가
textex커누스의 plain TeX. e-TeX 확장을 갖지 않은 유일한 포맷
latexpdftexLaTeX2e. DVI 모드의 pdfTeX으로 돌며 .dvi를 냅니다
pdflatexpdftex같은 LaTeX2e를 PDF 직접 출력으로. 서구어 문서의 사실상 기본
xelatexxetexLaTeX2e를 XeTeX 위에서. 시스템 글꼴과 유니코드
lualatexluahbtexLaTeX2e를 LuaHBTeX 위에서. Lua 스크립트와 HarfBuzz를 쓸 수 있습니다
platex / uplatexeuptex일본어 조판용 LaTeX2e. DVI를 내고 dvipdfmx로 PDF로 바꿉니다
amstexpdftexAMS-TeX. plain TeX 위의 수학용 매크로. LaTeX에서는 amsmath로 대체되었습니다
cont-enpdftex / xetexConTeXt(MkII). LaTeX과 다른 계통의 종합 포맷. 두 엔진에 등록되어 있습니다
csplainpdftex체코어・슬로바키아어를 위한 plain TeX 파생. -etex을 명시해 만듭니다

pLaTeX / upLaTeX — 일본어를 위한 LaTeX 포맷

pLaTeX과 upLaTeX은 일본어 조판용 엔진에 LaTeX 포맷을 얹은 것입니다. 세로쓰기, 루비(후리가나), 일본어 행 조판 같은 고유 처리는 아스키가 확장한 pTeX 계열 엔진이 맡습니다. TeX Live 2024의 fmtutil.cnf를 보면 platexuplatexeuptex 엔진 줄에 등록되어 있고, 포맷 쪽 초기화 파일(*platex.ini*uplatex.ini)로 동작을 가른다는 것을 알 수 있습니다. 따라서 둘의 차이는 실행 파일이 아니라 포맷의 문자 처리 방식에 있습니다. platex은 전통적인 pTeX 호환 동작을 지키고, uplatex은 내부 유니코드를 전제합니다. 새 일본어 문서라면 uplatex이 표준적인 선택입니다.

이 계통은 반드시 DVI를 출력하고, 일본어가 담긴 DVI를 dvipdfmx로 PDF로 바꾸는 2단 구성입니다(uplatex file.tex 다음에 dvipdfmx file.dvi). 색인에는 upmendex를 함께 씁니다. 클래스 파일로는 오쿠무라 하루히코의 jsarticlejsbook(pLaTeX용)과 ujarticle 같은 유니코드판, 그리고 JIS X 4051의 일본어 조판 요건을 따르는 비교적 새로운 jlreq가 널리 쓰입니다. 엔진 내부의 세부——JFM 메트릭, 금칙 처리, 세로쓰기 구현——는 "pTeX 패밀리" 페이지가 다룹니다.

어느 명령이 어떤 조합인가

같은 LaTeX 포맷이라도 명령 이름이 뒤에 있는 엔진, 출력 형식, 글꼴 처리 방식을 정합니다. 아래 표는 명령 이름을 기점으로 왼쪽에서 오른쪽으로 읽어 주세요. 실무에서 가장 크게 작용하는 것은 맨 오른쪽 열입니다. fontspec을 쓸 수 있는지가 곧 엔진 선택으로 이어집니다.

명령엔진출력유니코드 / 시스템 글꼴
latexpdfTeX(DVI 모드)DVI — 별도 도구로 변환이 필요합니다불가. inputenc 등에 기댑니다
pdflatexpdfTeXPDF를 직접불가. inputenc 등에 기댑니다
platexe-upTeX(pLaTeX 호환 설정)DVI → dvipdfmx일본어는 전통적인 글꼴 설정으로
uplatexe-upTeXDVI → dvipdfmx내부 유니코드. 일본어 글꼴은 전통 방식
lualatexLuaHBTeXPDF를 직접가능 — fontspec / luatexja
xelatexXeTeXPDF(내부적으로 xdvipdfmx를 거쳐)가능 — fontspec
terminal
# 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 생성 경로, 나아가 참고문헌 처리까지 한꺼번에 바뀝니다. 굳이 바꿔야 한다면 본 원고로 돌아가기 전에 작은 시험 파일로 모든 기능을 확인하세요. 애초에 무엇을 고를지에 대한 판단 재료——속도, 패키지 호환성, 일본어 사정——는 "엔진 고르기" 페이지에 정리되어 있습니다.