LaTeX은 TeX 위에서 돈다
LaTeX은 TeX 위에서 돈다
TeX과 LaTeX을 워드랑 아래아한글처럼 경쟁하는 두 조판 프로그램으로 아는 경우가 많다. 아니면 LaTeX이 TeX의 최신 버전이라고. 둘 다 아니다. LaTeX은 TeX 위에서 돈다. TeX이 토대고 LaTeX은 그 위에 얹은 한 겹이다.
이 관계 하나만 잡으면 pdflatex이니 .fmt니 하는 주변 이름들도 줄줄이 풀린다. 반대로 이걸 모르면 그 이름들이 계속 따로 노는 단어로 남는다. 그러니 TeX이 정확히 뭔지부터 보자.
TeX은 조판 엔진이자 매크로 언어다
TeX은 1970년대 후반에 도널드 크누스(Donald Knuth)가 만들었다. 계기는 사소했다. 자기 책 『The Art of Computer Programming』 2판 교정쇄를 받았는데 1판보다 조판이 형편없었던 거다. 인쇄 공정이 활자에서 사진식자로 넘어가던 과도기였고, 새 공정이 수식 가득한 수학책을 감당하지 못했다. 분수, 첨자, 적분기호의 크기와 간격을 책 한 권 내내 일관되게 맞추는 걸 당시 어떤 시스템도 제대로 못 했다. 그가 만들려던 건 워드프로세서가 아니라, 활자 장인이 손으로 짜던 수준의 조판이었다.
이 목표가 TeX의 구조를 둘로 갈랐다. 수학 표기법은 분야마다 사람마다 다르다. 새 연산자를 만드는 수학자도 있고 자기만의 행렬 표기를 쓰는 물리학자도 있으니, 크누스가 세상의 모든 표기를 미리 프로그램에 박아둘 수가 없다. 그래서 TeX은 두 부분으로 만들어졌다.
하나는 조판 엔진이다. 글자와 기호를 받아 박스로 만들고, 그 박스들을 줄과 페이지에 어떻게 앉힐지 계산하는 알고리즘 덩어리. 아주 저수준의 일만 한다. 다른 하나는 매크로 확장 언어다. 사용자가 \def\foo{...}라고 쓰면 그때부터 \foo가 그 내용으로 치환되는, 자기만의 명령을 정의하는 기능이다. 표기법 문제를 크누스가 정면으로 푸는 대신, 표기를 정의할 권한 자체를 사용자에게 넘긴 것이다. TeX 파일을 처리한다는 건 결국 매크로를 끝까지 펼쳐서 남은 원시 명령을 엔진에 먹이는 일이다.
이름에도 이 성격이 박혀 있다. TeX은 영어 단어가 아니라 그리스어 τέχνη(technē)의 앞 세 글자 τ-ε-χ다. technē는 손기술과 예술이 갈라지기 전의 단어, 기예다. 그래서 끝 글자 X는 알파벳 엑스가 아니라 그리스 문자 카이(χ)이고, 발음도 "텍스"가 아니라 독일어 ach의 ch에 가까운 "테흐"다. 로고에서 가운데 E가 아래로 처져 있는 것도 같은 고집이다.
날것의 TeX은 쓰기 힘들어서, 포맷이 생겼다
엔진이 저수준만 맡는다는 결정은 곧장 불편을 만든다. 순수한 TeX 명령만으로 글을 쓰는 건 어셈블리로 앱을 짜는 꼴이다. "여기 12pt 굵게, 위로 14pt 띄우고, 다음 줄은 들여쓰기 없이" 같은 걸 매번 손으로 박아야 한다. 장 제목 하나 여는 데 명령이 수십 줄이다.
크누스도 이걸 알았다. 그래서 자주 쓸 매크로를 한 묶음 미리 정의해 같이 배포했는데, 이게 plain TeX이다. 여기서 개념 하나가 선다. 엔진 위에 얹는 매크로 묶음을 포맷(format)이라 부른다. 엔진은 그대로 두고 위에 어떤 매크로 세트를 까느냐로 사용 경험이 완전히 달라진다. plain TeX은 그중 크누스가 직접 깐 얇은 기본 묶음이다.
그런데 plain TeX도 여전히 조판 디테일을 만지는 수준에 머물렀다. "나는 글자가 몇 pt로 찍히는지 신경 쓰기 싫다. 이건 제목이고 이건 절이고 이건 인용이다, 그것만 말하고 싶다" 하는 사람한테는 부족했다. 글의 구조를 가리키는 어휘가 없었던 거다.
LaTeX은 그 포맷을 두껍게 쌓은 것이다
그 빈자리를 채운 사람이 레슬리 램포트(Leslie Lamport)다. 1980년대 초에 plain TeX 위에 구조 중심 매크로를 잔뜩 쌓아 묶음으로 만들고, 자기 이름을 붙였다. La(mport) + TeX, 그래서 LaTeX이다. (끝의 TeX는 여전히 "테흐"로 읽는다.)
LaTeX이 한 일의 핵심은 관심사를 떼어놓은 것이다. 글 쓰는 사람은 \documentclass{article}, \section{서론}, \begin{itemize}처럼 "이게 무엇인지"만 선언한다. 그게 실제로 몇 pt에 어떤 간격, 어떤 글꼴로 찍힐지는 문서 클래스와 패키지가 뒤에서 정한다. 글의 구조와 겉모습을 분리한 거다.
여기서 처음의 관계가 정리된다.
TeX = 엔진(조판 알고리즘) + 매크로 언어 ← 토대
plain = TeX 위의 얇은 매크로 묶음 (크누스) ← 가벼운 포맷
LaTeX = TeX 위의 구조 중심 매크로 묶음 (램포트) ← 두꺼운 포맷
그러니까 LaTeX은 TeX의 경쟁자도, 다음 버전도, 다른 언어도 아니다. TeX 엔진 위에서 도는 매크로 패키지 중 제일 인기 있는 하나일 뿐이다. 우리가 매일 쓰는 \section{서론}도 결국 램포트가 \def로 정의해둔 매크로고, 그걸 부르면 안에서 번호 매기고 글씨 키우고 간격 띄우고 목차에 등록하는 원시 명령 수십 개가 펼쳐진다. "LaTeX은 사실상 대부분 TeX 매크로다"라는 말이 정확히 이 뜻이다.
이 분리가 공짜는 아니다. 매크로가 매크로를 부르고, 패키지 A가 정의한 명령을 패키지 B가 덮어쓰고, 그 위에 문서 클래스가 또 한 겹 얹는다. 그러다 정의 안 된 명령 하나가 호출되면 Undefined control sequence가 뜨는데, 메시지가 가리키는 곳은 내가 쓴 줄이 아니라 매크로가 펼쳐지다 망가진 저 아래 지점이다. 추상을 한 겹 쌓으면 평소엔 편하지만, 뭔가 어긋났을 때는 그 두께만큼 거꾸로 헤집고 내려가야 한다. LaTeX 에러가 악명 높은 건 LaTeX이 잘못 만들어져서가 아니라 두꺼운 추상이 원래 그렇기 때문이다.
그래서 pdflatex, .fmt, 여러 엔진이 뭔지도 풀린다
TeX이 토대고 LaTeX이 그 위 한 겹이라는 걸 잡으면, 주변에서 헷갈리던 이름들이 다 이 두 층 사이의 이야기로 정리된다.
먼저 엔진이 여러 개인 이유. 크누스의 원조 TeX 엔진은 1980년대 컴퓨터를 전제로 짠 물건이라, 세상이 옮겨가면서 한계가 드러났다. 원조 TeX은 글자를 8비트(256칸)로만 다뤄서 한글·한자가 들어갈 자리가 없고, OS에 깔린 폰트도 직접 못 읽는다. 이걸 푼 게 XeTeX으로, 입력을 유니코드로 받고 시스템 폰트를 이름으로 부른다(\setmainfont{Times New Roman}). 또 원조 TeX은 PDF가 나오기 전 물건이라 DVI(DeVice Independent, "장치 독립")라는 중간 파일을 뱉는데, 세상이 PDF로 굴러가면서 이걸 PDF로 직접 뱉도록 고친 게 pdfTeX이다. 복잡한 로직을 매크로로만 짜는 고통을 덜려고 Lua 스크립트를 엔진에 박은 LuaTeX도 있다. 세 엔진은 따로 외울 제품이 아니라 원조 TeX의 약점을 하나씩 메운 후손들이다.
다음으로 .fmt 파일. LaTeX 매크로 정의는 수천 줄이라, 컴파일할 때마다 처음부터 읽으면 매번 같은 준비 작업을 반복하게 된다. 그래서 매크로를 다 읽어들인 직후의 엔진 메모리 상태를 통째로 파일에 떠둔다. 이게 .fmt 파일(pdflatex.fmt 등)이고, 다음부터는 이 스냅샷을 즉시 불러오니 수천 줄을 다시 읽지 않는다.
이 둘을 합치면 명령어 이름이 풀린다. pdflatex은 독립된 프로그램이 아니라 "pdfTeX 엔진을, LaTeX 매크로가 미리 적재된 .fmt 상태로 띄운 것"이다. 엔진과 포맷의 조합이라는 뜻이다.
pdflatex = pdfTeX 엔진 + LaTeX 포맷
xelatex = XeTeX 엔진 + LaTeX 포맷
lualatex = LuaTeX 엔진 + LaTeX 포맷
윗단(LaTeX)은 같고 아랫단(엔진)만 바뀐다. 한글이 안 나와서 pdflatex을 xelatex으로 바꾸면 되는 건, 위에 얹은 LaTeX은 그대로 두고 맨 아래 엔진만 유니코드 대응으로 갈아끼우는 일이기 때문이다. 그래서 둘은 우열이 아니라 용도 차이다. 한글이나 시스템 폰트를 쓰면 xelatex(또는 lualatex), 영문 위주에 속도가 중요하면 pdflatex.
토대가 실제로 하는 일
마지막으로, 왜 TeX이 토대고 LaTeX이 그 위 한 겹인지를 가장 분명히 보여주는 건 엔진이 실제로 하는 조판 계산이다. LaTeX은 "무엇인지"만 말할 뿐, 그걸 보기 좋게 앉히는 진짜 일은 전부 엔진이 한다.
엔진은 모든 걸 박스로 본다. 글자도 박스, 단어도 박스, 줄도 박스다. 박스 사이의 공백은 고정된 빈칸이 아니라 글루(glue), 즉 "기본 크기에 더해 얼마까지 늘고 얼마까지 줄 수 있다"는 신축 범위를 가진 간격이다. 단어 사이가 그냥 4pt가 아니라 "기본 4pt, 2pt까지 늘고 1pt까지 주는" 값인 셈이다. 덕분에 양끝 정렬이 자연스럽게 된다. 한 줄을 좌우 끝에 맞추려면 단어 간격을 넓혀야 하는데, 글루들이 각자 비율대로 늘어나며 균등하게 벌어진다.
진짜 차이는 줄나눔에서 난다. 보통 워드프로세서는 줄을 왼쪽부터 채우다 단어가 안 들어가면 다음 줄로 넘긴다. 한 줄씩 그때그때 결정하니 빠르지만, 어떤 줄은 빡빡하고 바로 아랫줄은 헐렁한 들쭉날쭉한 문단이 나온다. TeX은 문단 전체를 한꺼번에 본다. 가능한 줄바꿈 조합마다 각 줄에 못생김 점수(badness)를 매기고 — 글루를 너무 늘이거나 줄인 줄일수록 높다 — 문단 전체 점수의 합이 최소가 되는 조합을 고른다. 크누스와 마이클 플라스가 짠 알고리즘이다. 그래서 한 줄이 너무 빡빡해질 것 같으면 윗줄을 미리 살짝 풀어서 문단 전체를 고르게 만든다. 크누스가 교정쇄에서 못 견뎌 한 들쭉날쭉한 페이지의 반대편이 바로 이 전역 최적화다.
.tex 파일이 PDF가 되는 전 과정을 세우면 이렇다.
.tex 소스 사람이 쓴 \section{...}, $x^2$ 같은 고수준 명령
│
[매크로 확장] LaTeX 매크로층을 .fmt 스냅샷에서 즉시 로드해 펼침
│
[엔진 조판] pdfTeX/XeTeX/LuaTeX — 글자를 박스로, 공백을 글루로
│
[줄나눔] 문단 전체를 보고 badness 합이 최소인 줄바꿈 선택
│
[페이지나눔] 줄들을 페이지에 배치
│
[백엔드 출력] 엔진에 따라 DVI 또는 PDF 직접 출력
▼
PDF
위쪽 두 칸이 LaTeX(매크로)의 몫이고 아래쪽이 엔진의 몫이다. 결국 TeX은 글자를 아름답게 앉히는 조판 엔진이고, LaTeX은 그 위에 얹어 "이건 제목, 이건 절"이라고 말하게 해주는 매크로 한 겹이다. pdflatex이든 .fmt든 여러 엔진이든, 전부 이 두 층 사이에서 생긴 이름들이다.