자동 빌드

“Rerun to get cross-references right.” LaTeX은 컴파일러를 한 번만 돌려서는 올바른 결과가 나오지 않는, 흔치 않은 조판 시스템입니다. 상호 참조도 목차도 인용도 첫 실행에서는 파일에 기록될 뿐이어서, latexmk 같은 자동 빌드 도구가 출력이 안정될 때까지의 왕복을 대신 돌려 줍니다. 이 페이지는 애초에 왜 여러 번 컴파일해야 하는지에서 출발해 latexmk -pdf, 저장할 때마다 다시 빌드하는 -pvc, 정리용 -c-C, 설정 파일 latexmkrc, 그리고 ararallmkmake라는 선택지까지 하나의 줄기로 따라갑니다.

LaTeX은 왜 여러 번 컴파일해야 하는가

답은 간단합니다. LaTeX은 문서를 앞에서 뒤로 딱 한 번만 읽습니다. 1쪽에 목차를 조판하는 시점에는 7절이 몇 쪽에 놓일지 아직 정해지지 않았습니다. 그래서 LaTeX은 진행하면서 알게 된 것——각 레이블의 절 번호와 쪽 번호, 목차 줄, 인용 키——을 .aux, .toc, .lof, .lot 같은 보조 파일에 적어 두고 다음 실행이 시작될 때 다시 읽어 들입니다. 즉 출력물은 언제나 직전 실행이 알아낸 값을 재료로 조판됩니다. 첫 번째 PDF의 목차가 비어 있고 참조 자리에 ??가 찍히는 이유가 바로 이것입니다.

여기에 재치 있는 장치가 하나 숨어 있습니다. LaTeX은 “몇 번 더 돌리면 되는지”를 세지 않습니다. \end{document} 시점에 이번에 계산한 각 레이블의 값을 직전 실행의 .aux에서 읽어 온 값과 하나씩 대조하고, 하나라도 다르면 LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. 경고를 냅니다. 뒤집어 말하면 이 경고가 사라진 순간이 .aux가 더는 변하지 않는 상태, 곧 부동점에 도달한 순간입니다. 문서가 완성되었는지는 페이지의 겉모습이 아니라 이 보조 파일들의 일치 여부로 결정됩니다.

text
% doc.aux -- what one run leaves behind for the next one to read
\@writefile{toc}{\contentsline {section}{\numberline {1}One}{1}{}}
\newlabel{sec:one}{{1}{1}{}{}{}}

% doc.log -- the first run, before the .aux settles
LaTeX Warning: Reference `sec:two' on page 1 undefined on input line 5.
LaTeX Warning: There were undefined references.
LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

참고문헌이 붙으면 이 왕복은 더 길어집니다. \cite가 요구한 키는 첫 실행에서 .aux에 기록되고, 그 파일을 읽은 bibtex 또는 biber.bbl을 만들며, 두 번째 실행이 .bbl을 읽어 들이고, 번호가 밀린 참조를 바로잡으려면 세 번째 실행이 필요합니다. 유명한 주문 latex → bibtex → latex → latex의 정체가 바로 이것입니다. 색인이 있으면 같은 사슬 중간에 makeindex가 끼어듭니다. 손으로 돌리는 한, 매번 어디까지 되돌아가야 할지 판단해야 합니다.

latexmk 기본 사용법: 명령 하나로 반복을 돌리기

입력하는 것은 latexmk -pdf document.tex 한 줄뿐입니다. 그다음부터는 latexmk가 .aux의 변화를 지켜보며 pdflatex을 필요한 횟수만큼 실행하고, 중간에 bibtexbibermakeindex를 알맞은 순서로 호출하며, 경고가 사라지면 멈춥니다. 이 도구의 내력은 조금 독특합니다. 처음에는 David J. Musliner가 쓴 go라는 작은 스크립트였고, 이를 Evan McLean이 개조해 latexmk라고 이름 붙였으며, 이후로는 펜실베이니아 주립대학의 물리학자 John Collins가 Perl로 보수하고 있습니다——TeX Live 2024에서 latexmk -v는 “Latexmk, John Collins, 31 Jan. 2024. Version 4.83.”이라고 답합니다. TeX Live와 MiKTeX에 모두 들어 있어 따로 설치할 일은 대개 없습니다.

terminal
$ latexmk -pdf doc.tex
Latexmk: applying rule 'pdflatex'...
Run number 1 of rule 'pdflatex'
Running 'pdflatex  -recorder  "doc.tex"'
Latexmk: References changed.
Latexmk: applying rule 'pdflatex'...
Run number 2 of rule 'pdflatex'
Running 'pdflatex  -recorder  "doc.tex"'
Latexmk: All targets (doc.pdf) are up-to-date

출력에 보이는 -recorder는 latexmk가 스스로 붙인 것입니다. 이 옵션을 주면 TeX 엔진이 해당 실행에서 읽고 쓴 파일 목록을 .fls로 내보내고, latexmk는 그것을 로그와 대조해 의존 관계를 알아낸 뒤 각 파일의 상태를 .fdb_latexmk라는 데이터베이스에 남깁니다. 중요한 것은 판정 기준입니다. latexmk는 수정 시각이 아니라 파일 내용의 체크섬을 비교합니다. 설명서는 그 이유를 분명히 적어 두었습니다. LaTeX 실행 중에 쓰인 파일은 그전에 읽어 들인 파일보다 언제나 나중이므로, 시각만 보면 영원히 “뒤처진” 것으로 보입니다. 이런 순환 의존은 LaTeX 고유의 문제이며 latexmk는 바로 그것을 넘어서려고 만들어졌다는 것입니다. 폭주에 대비한 안전장치도 있어서, $max_repeat(기본값 5)회를 넘겨도 수렴하지 않으면 무한 루프로 보고 중단합니다.

-pdf, -lualatex, -xelatex의 차이: 엔진 고르기

-pdfpdflatex, -lualatexlualatex, -xelatexxelatex를 쓰라는 지정입니다. 아무것도 붙이지 않으면 latexmk는 초기 버전과 마찬가지로 .dvi를 만들므로, PDF가 필요하면 이 중 하나는 반드시 붙여야 합니다. 여기에 알아 두면 좋은 장치가 하나 있습니다. -xelatex를 지정해도 latexmk는 xelatex에게 PDF를 바로 쓰게 하지 않습니다. 먼저 중간 형식인 .xdv를 만들어 그 위에서 반복 실행을 모두 끝낸 뒤, 마지막에 한 번만 xdvipdfmx를 호출합니다. 큰 .png를 붙인 문서에서는 PDF 생성이 느리기 때문에, 반복할 때마다 이미지를 다시 넣지 않으려는 궁리입니다. -lualatex-pdflua -dvi- -ps-의, -xelatex-pdfxe -dvi- -ps-의 줄임말입니다. 일본어의 upLaTeX + dvipdfmx처럼 DVI를 거치는 경로라면 -pdfdvi를 고릅니다.

옵션하는 일쓰는 상황
-pdfpdflatex으로 PDF를 만든다라틴 문자 중심의 일반적인 문서
-lualatexlualatex으로 PDF를 만든다(-pdflua -dvi- -ps-와 동일)OpenType 글꼴이나 Lua로 만든 확장
-xelatexxelatex으로 .xdv를 만들고 마지막에 xdvipdfmx를 호출시스템 글꼴을 그대로 쓰고 싶을 때
-pdfdvi.dvi를 만든 뒤 PDF로 변환한다upLaTeX + dvipdfmx 같은 DVI 경유 경로
-pvc소스를 감시하다가 바뀔 때마다 다시 빌드한다집필 중, 저장할 때마다 결과를 보고 싶을 때
-pvctimeout변화 없는 시간이 이어지면 -pvc를 종료한다(기본 30분)무인으로 계속 켜 두고 싶지 않을 때
-c다시 만들 수 있는 중간 파일을 지우고 PDF는 남긴다작업 디렉터리를 정리하고 싶을 때
-C-c에 더해 .dvi.ps.pdf까지 지운다클린 빌드 확인, 배포물 준비
-gg-C에 해당하는 정리를 한 뒤 일반 빌드를 수행한다처음부터 다시 빌드를 한 번에
-f오류가 나도 처리를 계속한다로그를 한꺼번에 확인하고 싶을 때
-silent엔진 출력을 줄인다(-quiet와 동일)CI 로그를 읽기 쉽게 하고 싶을 때
-r지정한 설정 파일을 추가로 읽어 들인다일시적으로 다른 경로로 빌드하고 싶을 때

저장할 때마다 다시 빌드하기——latexmk -pvc

-pvc는 preview continuously의 약자로, latexmk가 뷰어를 띄운 채 상주하면서 소스 파일 중 어느 하나라도 바뀌면 전체 반복을 다시 돌립니다. 감시 대상은 본문 .tex만이 아닙니다. .fls에서 얻은 의존 목록이 그대로 감시 목록이 되므로, \input\include로 불러온 장 파일도, 넣어 둔 그림도, .bib도 포함됩니다. 문서용 개발 서버 같은 사용감입니다. 몇 가지 버릇도 있습니다. -pvc파일 하나에만 쓸 수 있고, -p-pv와는 함께 쓸 수 없습니다. 또 이 모드에서는 강제 실행 -f가 자동으로 꺼지므로, 굳이 둘 다 원한다면 -pvc -f 순서로 적어야 합니다. 기본값으로는 내버려 두어도 스스로 종료하지 않습니다. -pvctimeout을 붙여야 변화 없는 시간이 이어질 때 끝나며, 그 대기 시간은 기본 30분이고 -pvctimeoutmins=로 바꾸거나 -pvctimeout-으로 다시 끌 수 있습니다. 뷰어 선택에도 주의가 필요해서, 설명서는 Windows의 acroread가 PDF 파일을 잠가 새 버전을 쓰지 못하게 하므로 연속 미리보기에는 부적합하다고 못 박고 있습니다.

terminal
latexmk -pdf -pvc doc.tex                 # watch the sources, rebuild on every save
latexmk -pdf -pvc -pvctimeout doc.tex     # same, but give up after 30 idle minutes
latexmk -lualatex -pvc doc.tex            # the same loop, driven by lualatex

에디터의 “저장할 때 빌드” 버튼도 속을 들여다보면 대개 latexmk입니다. VS Code의 LaTeX Workshop, TeXstudio, TeXShop, Emacs의 AUCTeX, Overleaf——이름은 달라도 실행되는 것은 같은 명령이거나 같은 발상의 내장 구현입니다. 그러므로 터미널에서 -pvc를 익혀 두면 물러설 곳이 생깁니다. 에디터가 이상하게 굴 때 맨 명령으로 내려가면 문서 탓인지 설정 탓인지 가려낼 수 있기 때문입니다. 에디터의 빌드만 실패하고 latexmk는 통과한다면 의심할 것은 문서가 아니라 에디터 설정입니다.

latexmk -c-C의 차이: 생성 파일 정리하기

차이는 딱 하나, PDF를 남기느냐 지우느냐입니다. -c는 다시 만들 수 있는 파일——.aux, .log, .toc, .fls, .fdb_latexmk 등——을 지우지만 .dvi.ps.pdf는 남깁니다. -C는 출력물까지 함께 지웁니다. 정리한 뒤 다시 빌드하는 데까지 한 번에 하고 싶다면 -gg입니다. 이것이 실무에서 중요한 이유는 오래된 .aux가 사고를 가려 주기 때문입니다. 절의 순서를 바꾸거나 \label을 지워도 이전 값이 남아 있어 내 컴퓨터에서는 PDF가 그럴듯하게 나오지만, 저장소를 막 clone한 공저자나 CI에서는 빌드가 깨집니다. 제출 전에 latexmk -C를 돌린 뒤 latexmk -pdf가 통과하는 것, 이것이 “소스만으로 조판할 수 있다”는 증명입니다.

terminal
latexmk -c                  # remove aux, log, toc, fls, fdb_latexmk ... keep the PDF
latexmk -C                  # remove all of that plus the dvi / ps / pdf output
latexmk -gg -pdf doc.tex    # clean first, then build again from scratch

빌드를 latexmkrc에 적어 프로젝트에 고정하기

문서 옆에 latexmkrc 또는 .latexmkrc라는 이름의 파일을 두면, 그 디렉터리에서 latexmk라고만 쳐도 모두가 같은 경로를 지나갑니다. latexmk는 시작할 때 시스템 전체 설정 → 사용자의 $HOME/.latexmkrc(또는 $XDG_CONFIG_HOME/latexmk/latexmkrc) → 현재 디렉터리의 latexmkrc 또는 .latexmkrc-r로 지정한 파일 순으로 읽습니다. 나중에 읽은 것이 이기므로 프로젝트 설정이 각자의 취향을 덮어씁니다. 내용은 Perl 코드이고 #부터 줄 끝까지가 주석이며, 대개는 변수 대입 몇 줄이면 충분합니다. 공동 작업에서는 이 파일을 저장소에 넣고 “이 문서는 이렇게 빌드한다”는 약속으로 삼는 편이 가장 말썽이 적습니다.

perl
# latexmkrc -- lives next to the document and is committed with it

$pdf_mode = 4;           # 4 = build the PDF with lualatex
$max_repeat = 7;         # allow a couple of extra passes on a long document

# Alternative route: upLaTeX -> DVI -> dvipdfmx
# $latex    = 'uplatex -interaction=nonstopmode -halt-on-error %O %S';
# $dvipdf   = 'dvipdfmx %O -o %D %S';
# $pdf_mode = 3;         # 3 = make the PDF from the DVI file

# Extra extensions that -c and -C should remove as well.
$clean_ext = 'synctex.gz run.xml bcf';

latexmk 말고 다른 선택지: arara, llmk, make

갈림길은 한 가지 물음, 곧 누가 절차를 정하는가입니다. latexmk는 로그와 의존 관계에서 절차를 추론합니다. 반면 arara는 아무것도 추론하지 않습니다. 문서에 적어 둔 지시문——% arara: pdflatex 같은 한 줄짜리 주석——을 읽어, 적힌 그대로, 적힌 순서대로 실행합니다. CTAN의 설명이 밝히듯 arara는 로그 분석 같은 간접적 단서가 아니라 소스 안의 메타데이터로 동작을 결정하는 설계이며, Paulo Roberto Massa Cereda를 중심으로 Island of TeX가 개발합니다(실행에는 Java가 필요합니다). llmk(TeX Live에서의 패키지 이름은 light-latex-make, 작성자는 Takuto Asakura)는 한 걸음 더 선언적입니다. 절차를 llmk.toml이나 소스 안의 TOML 필드에 적고 texlua만으로 동작하며, 어떤 환경에서도 완전히 같은 결과가 나오는 것을 최우선으로 삼은 설계입니다.

latex
% arara directives: the document itself states the workflow
% arara: pdflatex
% arara: biber
% arara: pdflatex
% arara: pdflatex
\documentclass{article}
toml
# llmk.toml -- next to the document; "source" is required in this file
source = "doc.tex"
latex = "lualatex"
bibtex = "biber"
sequence = ["latex", "bibtex", "latex", "latex"]

그렇다면 맨 make는 어떨까요. Makefile을 쓰면 LaTeX도 돌릴 수 있지만, make의 판단 기준은 수정 시각입니다. .aux는 실행할 때마다 다시 쓰이므로, 시각만 보면 읽어 들인 쪽보다 언제나 나중이고, 따라서 영원히 뒤처진 상태가 됩니다. latexmk의 설명서가 “이런 순환 의존은 LaTeX 고유의 것이며 latexmk는 그것을 넘어서려고 만들어졌다”고 적은 것이 바로 이 지점입니다. 그래도 make를 쓴다면 .aux의 사본을 두고 차이를 비교하거나, Makefile 타깃에서 그냥 latexmk를 부르는 방식이 현실적입니다. 실제로 수많은 프로젝트의 Makefile은 latexmk -pdf $< 한 줄로 정리됩니다.

도구절차를 정하는 방식설정을 두는 곳필요한 것
latexmk로그・.fls・내용 체크섬으로 추론latexmkrc / .latexmkrc(Perl)Perl. TeX Live・MiKTeX에 동봉
arara문서에 적은 지시문 그대로 실행문서 앞부분의 % arara: 주석Java
llmkTOML에 선언한 sequence대로 실행llmk.toml 또는 소스 안의 TOML 필드texlua만 있으면 됨
make수정 시각의 선후로 판단; .aux 순환에 취약Makefilemake. 대부분 환경에 이미 있음

집필 중・공동 작업・제출 전에 어떻게 나누어 쓸까

쓰임새는 세 가지 시점으로 정리됩니다. 쓰는 동안에는 -pvc로 저장할 때마다 결과를 보고, 남에게 넘기기 전에는 맨 latexmk를 한 번 통과시키고, 제출 직전에는 latexmk -C로 전부 지운 뒤 다시 빌드합니다. 특히 마지막 단계를 습관으로 삼으면, 마감 직전에야 “내 환경에서만 컴파일되는 문서”임을 알아차리는 사고를 줄일 수 있습니다. 그리고 설정을 latexmkrc에 고정해 저장소에 넣어 두면 CI 서버도 공저자도 같은 경로를 따르므로, “제 환경에서는 됩니다”라는 실랑이 자체가 잘 생기지 않습니다.

  • 작성 중latexmk -pdf -pvc doc.tex: 저장할 때마다 자동으로 다시 빌드. 무인으로 켜 두고 싶지 않다면 -pvctimeout을 덧붙입니다.
  • 엔진 고정latexmkrc$pdf_mode 등을 적고 저장소에 넣어 모두가 공유합니다.
  • 공저자에게 넘기기 전 → 맨 latexmk -pdf를 한 번 통과시키고 LaTeX Warning: Label(s) may have changed.가 남아 있지 않은지 확인합니다.
  • 제출・배포 직전latexmk -C로 전부 지운 뒤 클린 빌드. 한 번에 끝내려면 latexmk -gg -pdf doc.tex.
  • 서버나 CI에서 빌드 → CI 페이지를 보세요. -silent를 붙이면 로그가 읽기 쉬워집니다.