TeX Live 2024의 실행 파일을 들여다보면 이상한 점이 눈에 띕니다. latex도 pdflatex도 etex도 프로그램이 아니라 pdftex를 가리키는 심볼릭 링크입니다. 커누스가 쓴 TeX 본체, 곧 명령 tex만이 여전히 독립된 하나의 프로그램으로 남아 있습니다. 이 사실이 이해되면 LaTeX을 둘러싼 혼란의 대부분이 풀립니다. 생태계 전체가 딛고 선 구분, 곧 엔진(프로그램)과 포맷(미리 읽어 둔 매크로 묶음)은 서로 다른 것이라는 사실이 드러나기 때문입니다. 이 페이지에서는 가장 아래에 놓인 얼어붙은 프로그램과, 1990년대에 그 위에 덧붙은 e-TeX 확장——이제는 누구나 알아채지 못한 채 쓰고 있는 토대——을 살펴봅니다.
tex 명령이 실제로 실행하는 것
tex가 실행하는 것은 문자와 상자를 줄과 문단에 채워 넣고, 줄바꿈 위치를 최적화하고, 결과를 파일에 써 내는 프로그램입니다. 그 이상은 하지 않습니다. 혼란의 출발점은 TeX이라는 낱말이 이 프로그램과, 그 프로그램이 해석하는 명령 체계를 함께 가리킨다는 데 있습니다. 절 나누기도, 상호 참조도, 참고문헌도 프로그램 자신은 전혀 알지 못합니다.
커누스가 첫 TeX을 쓰기 시작한 것은 1978년으로, 자신의 저서 『The Art of Computer Programming』 제2권 교정쇄의 수식 조판이 견디기 어려웠던 것이 계기였습니다. 초판은 더듬어 가며 만든 시제품이었고 1982년에 전면 재작성됩니다. 이 재작성판이 TeX82이며, 오늘날 "TeX"이라고 하면 사실상 이 계보를 가리킵니다. 커누스가 프로그램 자체에 직접 넣은 최소한의 명령 집합을 프리미티브(기본 명령)라고 하며, \def(명령 정의), \hbox(가로 상자), \vbox(세로 상자) 따위가 여기에 속합니다.
프리미티브만으로 문서를 쓰는 것은 현실적이지 않습니다. 그래서 실제로는 그 위에 매크로를 쌓아 올린 포맷을 씁니다. plain.tex을 읽어 들인 것이 plain TeX, 훨씬 큰 매크로 묶음을 읽어 들인 것이 LaTeX입니다. 포맷은 실행할 때마다 다시 읽지 않습니다. 미리 전개해 .fmt 파일로 저장해 두었다가 시작할 때 순식간에 되살립니다. 그 구조의 세부——.fmt 안에 무엇이 들어 있는지, fmtutil로 어떻게 다시 만드는지, LaTeX2e와 LaTeX3은 어떤 관계인지——는 "LaTeX 포맷" 페이지가 맡습니다.
tex, initex, etex 가운데 진짜 프로그램은 어느 것인가
tex뿐입니다. initex은 tex을 가리키는 심볼릭 링크이고, etex은 아예 pdftex을 가리키는 링크입니다. 곧 독립된 프로그램으로서의 e-TeX은 현재 배포판에 더 이상 존재하지 않습니다. 이는 의견이 아니라 readlink 한 줄로 몇 초 만에 확인되는 사실입니다. TeX Live 2024에서 tex --version은 TeX 3.141592653 (TeX Live 2024)이라고 답합니다.
$ tex --version | head -1
TeX 3.141592653 (TeX Live 2024)
# Which of these are real binaries, and which are links?
$ cd $(dirname $(which tex))
$ readlink tex initex etex latex pdflatex amstex csplain
# tex prints nothing: it is a real executable
tex # initex -> tex
pdftex # etex -> pdftex
pdftex # latex -> pdftex
pdftex # pdflatex -> pdftex
pdftex # amstex -> pdftex
pdftex # csplain -> pdftex왜 이렇게 되었을까요. e-TeX의 확장이 다른 엔진들에 통째로 흡수되었기 때문입니다. TeX Live에 동봉된 doc/etex/base/README(Karl Berry, 2024)는 이 상황을 가감 없이 적어 두었습니다. 대부분의 배포판이 제공하는 etex 실행 파일은 "DVI 모드로 도는 pdfTeX의 화신"이며, 그렇게 해야 e-TeX을 넘어서는 확장까지 함께 쓸 수 있다는 것입니다. 같은 이치로 latex은 "옛 TeX"이 아니라 DVI 모드의 pdfTeX입니다. 실행하면 배너가 This is pdfTeX, ...이라고 자신을 밝힙니다. 엔진과 포맷이 서로 다른 축이기에 이런 맞바꿈이 성립합니다.
plain TeX — \documentclass도 \begin{document}도 없는 세계
plain TeX은 커누스가 『The TeXbook』에서 제시한 표준 포맷입니다. 글꼴 설정, 기본 수학 기호, \bye 같은 편의 매크로 등 최소한의 도구를 plain.tex에 모아 둔 것으로, TeX 본체와 함께 배포됩니다. LaTeX이 나오기 전에는 이것이 TeX을 쓰는 유일한 현실적 방법이었습니다. TeX Live 2024에서는 tex 엔진이 tex.ini로부터 이 포맷을 덤프해 tex.fmt으로 저장합니다.
표기법은 LaTeX과 꽤 다릅니다. 인라인 수식이 $...$인 것은 같지만, 가로 상자는 \hbox{...}, 세로 상자는 \vbox{...}, 표 정렬은 \halign, 명령 정의는 \def, 본문 너비는 \hsize, 그리고 문서의 끝은 \bye입니다. \documentclass도 \begin{document}도 없습니다. 모두 LaTeX이 매크로로 만들어 낸 것이기 때문입니다. 뒤집어 말하면 중요한 사실이 드러납니다. LaTeX의 편리함은 포맷의 기능이지 엔진의 기능이 아니라는 것입니다.
% plain TeX — process with: tex hello.tex (produces hello.dvi)
\hsize=10cm
\font\big=cmr10 at 17pt
{\big Hello, plain \TeX!}
\medskip
This paragraph is set in the default font.
Inline math works too: $E = mc^2$.
\bye이것을 tex hello.tex으로 처리하면 DVI 파일이 만들어집니다. PDF가 아닙니다. 곧 이어서 다룹니다. \TeX 같은 로고 매크로나 \medskip(중간 크기 세로 여백) 같은 간격 매크로도 plain TeX이 정의한 것입니다. LaTeX의 \documentclass 문서와 나란히 놓고 보면, plain TeX이 맨 엔진 위에 얼마나 얇은 껍질만 씌우고 있는지 잘 드러납니다.
문학적 프로그래밍과 WEB — TeX 자신의 소스는 어떻게 쓰였는가
TeX 자체는 커누스가 제창한 문학적 프로그래밍(literate programming)의 실천으로서 WEB이라는 독자 시스템으로 쓰였습니다. WEB 소스는 사람을 위한 설명과 Pascal 코드가 번갈아 짜여 든 한 편의 문서입니다. 여기서 두 도구가 서로 다른 산물을 뽑아냅니다. tangle은 컴파일 가능한 Pascal을, weave는 조판된 해설서——바로 『TeX: The Program』이라는 책——를 만들어 냅니다.
처음의 대상 언어는 Pascal이었지만, 현대의 배포판에서는 web2c라는 도구가 WEB(을 거친 Pascal)을 C 언어로 변환해 빌드합니다. 곧 지금 여러분의 컴퓨터에서 도는 pdfTeX도 XeTeX도 LuaTeX도, 거슬러 올라가면 결국 그 한 편의 문학적 소스에 닿습니다. TeX Live의 디렉터리 이름에 web2c가 거듭 나타나는 것은 그 때문입니다.
π로 수렴하는 버전 번호, 그리고 얼어붙은 프로그램
TeX의 버전 번호는 독특합니다. 버전 3 이후로 갱신할 때마다 끝에 숫자를 하나씩 붙여 나가, 번호가 원주율 π에 점근하도록 되어 있습니다. TeX Live 2024에 동봉된 판에서 tex --version을 실행하면 TeX 3.141592653이라고 답합니다. 짝을 이루는 METAFONT의 버전이 자연로그의 밑 e에 점근한다는 것까지 함께 알아 두면, 이 작명의 장난기가 보입니다.
그리고 커누스는 자신이 죽은 뒤에 이루어질 "절대적으로 마지막인 변경"에서 버전을 정확히 π로 만들겠다고 선언했습니다. 그때 남아 있는 버그는 모두 기능이 된다는 것이 그의 표현입니다(Donald E. Knuth, "The future of TeX and METAFONT", NTG 학회지 MAPS, 1990; 『Digital Typography』 제30장에 재수록). 이 동결은 체념이 아니라 설계 판단이었습니다. 토대가 움직이지 않기 때문에 그 위에 얹히는 포맷과 드라이버가 마음 놓고 진화할 수 있습니다. 수십 년 전의 .tex 파일이 오늘도 같은 결과를 내는 것은 이 판단의 직접적인 귀결입니다.
METAFONT와 DVI — TeX이 PDF를 직접 내놓지 않는 이유
TeX에는 쌍둥이 동반자 METAFONT(명령 이름 mf)가 있습니다. 글꼴을 "모양"이 아니라 "그리는 방법을 적은 프로그램"으로 기술하는 시스템으로, 커누스는 이것으로 TeX용 Computer Modern 서체 일습을 설계했습니다. 역할 분담은 명쾌합니다. TeX이 글자를 어디에 둘지 정하고, METAFONT가 글자의 모양 자체를 만들어 냅니다.
그리고 커누스의 TeX이 직접 내놓는 것은 PDF가 아니라 DVI(DeVice Independent) 파일입니다. DVI에는 "이 위치에 이 글자를 놓아라"라는, 특정 출력 장치에 의존하지 않는 지시만 들어 있습니다. 거기서 dvips로 PostScript로, dvipdfmx로 PDF로 변환합니다. 1982년에는 PDF가 존재하지 않았으니 당연한 설계였고, 뒤집어 보면 출력 형식이 바뀌어도 TeX 본체를 고쳐 쓸 필요가 없었다는 뜻이기도 합니다. 오늘날 널리 쓰이는 pdfTeX・XeTeX・LuaTeX의 큰 차이는 이 단계를 안으로 끌어들여 PDF를 직접 써 낼 수 있다는 점입니다.
e-TeX이란 무엇인가 — 얼어붙은 프로그램을 깨지 않고 확장하기
e-TeX은 얼어붙은 TeX에 새 프리미티브를 더한 엄밀한 상위 호환 확장입니다. 커누스가 TeX을 동결한 이상 새 기능은 다른 손에서 나올 수밖에 없었습니다. 그래서 독일어권 TeX 사용자 모임 DANTE e.V.의 후원 아래 1992년에 발족한 것이 NTS(New Typesetting System) 프로젝트이고, e-TeX은 그 첫 구체적 성과입니다. 구현한 사람은 뮌헨 막스플랑크 물리학연구소의 Peter Breitenlohner(2015년 작고)이고, 프로젝트 조정 역할은 Philip Taylor가 맡았습니다. TeX Live에 동봉된 etex.1 매뉴얼은 그 목적을 "TeX의 정신과 철학을 이어받아 발전시키되, TeX은 동결된 채로 두라는 커누스의 뜻을 존중하면서"라고 적고 있습니다.
"엄밀한 상위 호환"이란 기존 입력을 한 글자도 고치지 않고 돌려도 출력이 똑같다는 뜻입니다. 이를 보장하기 위해 e-TeX에는 두 가지 모드가 있습니다. 호환 모드(compatibility mode)에서는 맨 TeX과 완전히 같게 동작하고, 확장 모드(extended mode)에 들어가야 비로소 추가된 프리미티브를 쓸 수 있습니다. 어느 쪽으로 도는지는 실행할 때가 아니라 포맷을 만들 때 정해집니다. 포맷의 원본 파일 이름 앞에 *를 붙여 덤프하면 확장 모드가 됩니다. 다음 절에서 보듯, 이 한 글자짜리 약속이 현대 LaTeX 전체를 떠받치고 있습니다.
왜 우리는 모르는 사이에 e-TeX을 쓰고 있는가
LaTeX 포맷이 확장 모드로 덤프되어 있기 때문입니다. 이는 추측이 아니라 설정 파일에 적혀 있습니다. TeX Live 2024의 fmtutil.cnf를 열면 latex pdftex language.dat -translate-file=cp227.tcx *latex.ini라는 줄이 있고, *latex.ini의 *가 확장 모드 지정입니다. 같은 파일 머리말의 주석은 이 *가 본질적으로 -etex 옵션과 같다고 못 박고 있습니다. 이에 견주어 plain TeX 줄은 tex tex - tex.ini로, 엔진은 tex이고 *도 없습니다. 맨 TeX만이 e-TeX 확장을 갖지 않은 채 남아 있는 것입니다.
LaTeX 쪽에서도 같은 사실을 확인할 수 있습니다. 커널 파일 latex.ltx의 앞부분에는 \ifx\eTeXversion\undefined \errmessage{LaTeX requires e-TeX}이라는 파수꾼이 놓여 있어, 확장이 없으면 포맷 생성 자체가 멈춥니다. 이 방침이 공식화된 시점도 분명합니다. LaTeX News 16(2003년 12월)이 앞으로 2년쯤 안에 e-TeX을 전제로 옮겨 갈 것이라 예고했고, LaTeX News 26이 2017년 1월 릴리스부터 포맷 생성에 e-TeX이 필수라고 선언했습니다. latex을 실행하면 로그 셋째 줄에 나오는 entering extended mode는 그 결정이 남긴 눈에 보이는 흔적입니다.
# Ask each engine whether the e-TeX primitives exist.
$ cat > probe.tex <<'EOF'
\message{[eTeX=\ifx\eTeXversion\undefined none\else\number\eTeXversion\eTeXrevision\fi]}
\count300=7 % register 300 does not exist in Knuth's TeX
\end
EOF
$ tex probe.tex # Knuth's TeX: no extensions
[eTeX=none]
! Bad register code (300).
$ pdftex probe.tex # pdfTeX: e-TeX 2.6 built in, 32768 registers
[eTeX=2.6]
$ latex probe.tex | head -3
This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=latex)
restricted \write18 enabled.
entering extended mode마지막 배너는 이 페이지의 주제를 그대로 한 줄로 압축한 것입니다. 3.141592653-2.6-1.40.26이라는 버전 문자열은 세 겹입니다. 커누스의 TeX이 3.141592653, e-TeX이 2.6, pdfTeX이 1.40.26이고, (preloaded format=latex)이 포맷의 이름입니다. 곧 한 줄 안에 엔진의 계보와 포맷의 선택이 함께 적혀 있습니다. 로그 첫 줄을 읽는 습관을 들이면 "같은 명령인데 결과가 다르다"는 협업의 고전적 골칫거리가 몇 초 만에 갈립니다.
e-TeX이 더한 프리미티브
e-TeX이 더한 것들은 매크로를 쓰는 사람에게 특히 효과가 큽니다. 가장 큰 것은 정수・치수・글루의 산술입니다. 맨 TeX에서는 계산할 때마다 작업용 레지스터를 돌려써야 했지만, e-TeX은 \numexpr・\dimexpr・\glueexpr을 마련했습니다. (a+b)*c/d 같은 식을 그 자리에서, 그것도 전개 가능한 형태로 계산합니다. 전개 가능하다는 점이 중요해서, \edef나 \write 안에서도 곧바로 값이 됩니다.
% On any e-TeX engine in extended mode (pdftex, xetex, luatex, euptex).
\count0=\numexpr (3+4)*2/7 \relax % yields 2
% Branch safely on whether a name is defined.
\ifdefined\foo \message{foo exists}\else \message{no foo}\fi
% Test a control sequence without creating it.
\ifcsname chapter\endcsname \message{chapter is defined}\fi
% Negate a conditional directly, instead of swapping the branches.
\unless\ifnum\count0>10 \message{count0 is not greater than 10}\fi두 번째 기둥은 조건 판정과 토큰 조작입니다. \ifdefined는 제어 시퀀스가 정의되어 있는지를 검사하고, \ifcsname...\endcsname은 이름으로 조립한 제어 시퀀스의 존재를 검사합니다. 둘 다 부작용이 없다는 점이 핵심입니다. 맨 TeX에서 쓰이던 \ifx 방식의 옛 수법은 정의되지 않은 제어 시퀀스를 소리 없이 \relax로 바꿔 버리기도 했습니다. \unless는 임의의 조건을 뒤집어 두 분기를 맞바꾸는 잡일을 없애 주고, \detokenize는 토큰 목록을 문자열 형태——카테고리 코드 12의 문자——로 바꿉니다.
| 프리미티브 | 하는 일 |
|---|---|
\numexpr | 정수식을 그 자리에서 계산합니다(전개 가능). \numexpr (3+4)*2/7 \relax |
\dimexpr / \glueexpr | 같은 일을 치수와 글루에 대해 합니다. \dimexpr \textwidth/3 \relax |
\ifdefined | 제어 시퀀스가 정의되어 있는지를 부작용 없이 검사합니다 |
\ifcsname | 이름으로 조립한 제어 시퀀스의 존재를, 그것을 만들지 않고 검사합니다 |
\unless | 바로 뒤 조건의 참거짓을 뒤집습니다. \unless\ifnum ... |
\protected | \edef나 \write 안에서 제멋대로 전개되지 않는 매크로를 정의합니다 |
\detokenize / \unexpanded | 토큰 목록을 문자열로 바꿉니다 / 토큰 목록을 전개하지 않고 그대로 둡니다 |
\scantokens / \readline | 문자열을 입력처럼 다시 읽습니다 / 입력 줄을 그대로(축자적으로) 읽습니다 |
\middle | \left … \right 중간에 늘어나는 구분 기호를 놓습니다 |
\currentgrouplevel | 지금 몇 겹의 그룹 안에 있는지를 돌려줍니다(\interactionmode는 대화 모드의 읽기·쓰기) |
전개 제어도 강화되었습니다. \unexpanded는 내용을 전개하지 않고 그대로 두고, \protected는 전개 문맥에서 제멋대로 전개되지 않는 매크로를 정의합니다. 뒤엣것은 LaTeX의 \protect를 낮은 수준에서 올바르게 구현하는 열쇠로, 깨지기 쉬운 명령을 \edef나 \write에 써 넣어도 망가지지 않게 해 줍니다. 수식에서는 \middle이 제 몫을 합니다. \left( … \middle| … \right)라고 쓰면 가운데 세로줄도 양옆 괄호와 같은 높이로 늘어납니다. 맨 TeX에는 "식 중간의 구분 기호"라는 개념 자체가 없었습니다.
수수하지만 결정적인 것이 레지스터의 대폭 증설입니다. 맨 TeX에는 \count・\dimen・\skip・\toks 따위가 각각 256개뿐이었습니다. 위 실험에서 tex이 ! Bad register code (300).이라며 거부한 것이 바로 그것입니다. e-TeX은 이를 32768개로 늘렸고, 게다가 성긴 배열로 확보하므로 쓰지 않는 몫은 메모리를 먹지 않습니다. 커다란 클래스와 패키지를 수십 개씩 읽어 들이는 오늘날의 LaTeX 문서가 레지스터 부족으로 멈추지 않는 것은 이 확장 덕분입니다.
이 밖에도 e-TeX은 양방향 조판(오른쪽에서 왼쪽으로 쓰는 문자 체계)을 위한 장치도 들여왔습니다. Breitenlohner의 TeX--XeT에서 비롯한 이 부분은 훗날 XeTeX과 LuaTeX, 그리고 일본어 처리 작업으로 이어집니다. 그리고 현대적인 패키지 상당수는 이들 프리미티브 없이는 성립하지 않습니다. LaTeX의 프로그래밍 층인 expl3이 대표적인 예로, \ifcsname이나 \protected를 쓸 수 없었다면 그 거대한 매크로 체계는 애초에 쓰일 수 없었을 것입니다.
이 층을 알면 무엇을 읽을 수 있게 되는가
오류 메시지의 바닥이 보이게 됩니다. LaTeX이 내놓는 경고의 상당수는 사실 이 아래 층의 언어로 쓰여 있습니다. Overfull \hbox (12.3pt too wide)는 "줄이라는 가로 상자에서 내용이 12.3pt 삐져나왔다"는 뜻이고, \hbox가 상자임을 알면 원인을 긴 URL이나 줄바꿈할 수 없는 낱말로 좁힐 수 있습니다. ! Missing number, treated as zero.는 "숫자나 치수로 읽을 수 있는 것을 기대했는데 없었다"는 뜻으로, 대개 길이 표기를 잘못 쓴 경우입니다.
다만 이 층을 읽는 것과 이 층에서 쓰는 것은 다른 이야기입니다. 제목・목록・상호 참조・정리 환경은 LaTeX의 구조화 명령에 맡기고, 저수준 명령은 "왜 깨졌는지 읽을" 때와 "클래스를 좁게 손볼" 때를 위해 남겨 둡니다. 새 명령을 정의할 때조차 보통의 문서에서는 날것의 \def보다 \newcommand나 \NewDocumentCommand가 안전합니다. 인수 개수를 검사하고 기존 명령과의 충돌을 알려 주기 때문입니다.
- 원고를 쓸 때 — LaTeX의 구조화 명령으로 쓰고, 저수준 프리미티브는 되도록 쓰지 않습니다.
- 클래스나 패키지를 고칠 때 —
\ifdefined나\numexpr같은 e-TeX 도구는 옛 TeX의 잔재주보다 안전하고 읽기 쉽습니다. - 로그를 읽을 때 — 경고를 상자・글루・레지스터라는 TeX의 어휘로 옮겨 보면 원인이 드러납니다.
- 재현성이 필요할 때 — 엔진은 얼어 있으므로 옛 문서도 같은 결과를 냅니다. 실제로 움직이는 것은 포맷과 패키지의 버전 쪽이며, 못 박아 둘 대상은 그쪽입니다.