번호가 나와야 할 자리에 ??가 나오고, 로그에는 LaTeX Warning: There were undefined references. LaTeX을 쓰기 시작하면 가장 먼저 부딪히는 부조리가 이것입니다. 참조가 ??가 되는 이유는, LaTeX이 직전 실행이 남긴 .aux 파일을 읽어 번호를 정하기 때문입니다. 즉 ??와 “왜 두 번 컴파일해야 하는가”라는 고전적인 의문은 같은 동전의 양면입니다. 이 페이지에서는 그 2패스 구조, Label(s) may have changed・multiply defined・Citation ... undefined의 구별, BibTeX나 Biber가 끼어들었을 때의 올바른 실행 순서, 그리고 가장 무서운 함정인——경고를 하나도 내지 않고 틀린 번호를 인쇄하는 \label의 위치를 다룹니다.
왜 두 번 컴파일해야 하는가
LaTeX은 문서를 처음부터 끝까지 한 번 읽어 내기 전에는 뒤에 있는 것의 번호를 모르기 때문입니다. 3쪽에서 “7장 참조”라고 써도, 그 시점에 7장이 몇 쪽이 될지는 아직 정해지지 않았습니다. 그래서 LaTeX은 1차 실행에서 찾아낸 \label을 모두 .aux 파일에 써 두고, 2차 실행 첫머리에 그것을 읽어 들여야 비로소 번호를 채웁니다. 그러니 첫 실행은 반드시 ??가 됩니다. 버그가 아니라 설계입니다. 게다가 문제는 순환합니다. 참조가 ??(두 글자)에서 12(역시 두 글자지만 다른 글자)로 바뀌면 줄 나누기가 바뀌고, 줄 나누기가 바뀌면 쪽 번호가 바뀌고, 쪽 번호가 바뀌면 \pageref 값이 바뀝니다. LaTeX은 이 순환을 “수렴할 때까지 반복”해서 풉니다. 수렴했는지를 알려 주는 것이 LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.입니다. 이 문장이 사라질 때까지 돌린다 — 올바른 운용은 그뿐입니다.
.aux의 내용을 한 번 들여다보면 이 구조가 단번에 이해됩니다. 아래는 \section{The section}\label{sec:s}를 포함한 문서를 두 번 돌린 뒤의 .aux 발췌로, 위가 순수 LaTeX, 아래가 hyperref를 불러온 경우입니다. \newlabel은 인자가 다섯 개인데, 첫 번째가 번호, 두 번째가 쪽입니다. \ref는 첫 번째를, \pageref는 두 번째를 꺼낼 뿐입니다. hyperref를 넣으면 세 번째에 제목 문자열, 네 번째에 앵커 이름이 들어갑니다. \nameref{sec:s}가 “The section”을 인쇄할 수 있는 것은 그 세 번째 덕분이고, \ref가 클릭 가능한 링크가 되는 것은 네 번째 덕분입니다. .aux가 망가졌을 때 지우고 다시 만들면 되는 이유도 여기서 드러납니다. 안에 든 것은 전부 파생물이고 실행 때마다 새로 만들어집니다.
% plain LaTeX: {number}{page}{}{}{}
\newlabel{sec:s}{{1}{1}{}{}{}}
% with hyperref: {number}{page}{title}{anchor}{}
\newlabel{sec:s}{{1}{1}{The section}{section.1}{}}“몇 번 돌려야 멈추는가”를 사람이 세는 것은 낭비이므로, 그 판단을 자동화하는 도구가 생겨났습니다. latexmk입니다. 로그를 읽어 Rerun to get cross-references right가 사라질 때까지 계속 돌리고, 필요하면 BibTeX나 색인 처리도 중간에 끼워 넣습니다. 이 도구의 내력은 조금 유쾌합니다. TeX Live 2024에 딸려 오는 latexmk의 첫머리에는, 원형이 1992년 미시간 대학의 David J. Musliner가 쓴 go라는 이름의 스크립트였다고 적혀 있습니다. 이후 Evan McLean이 손을 댔고, 1998년부터 John Collins가 이어받아, 동봉판은 2024년 1월자 4.83입니다. “대체 몇 번 컴파일해야 하나”라는 소박한 짜증이 30년 넘게 유지되는 도구를 낳은 셈입니다.
경고 구별하기 — 어느 것이 “아직”이고 어느 것이 “정말 없음”인가
판정은 간단합니다. 두 번째 실행에서도 같은 경고가 남는지만 보면 됩니다. 1차에만 나오는 경고는 정상 동작이고, 2차에도 남는 경고가 진짜 문제입니다. 존재하지 않는 라벨 \ref{nope}와 존재하는 라벨 \ref{sec:real}을 하나씩 담은 문서를 돌리면, 1차에서는 둘 다 undefined로 보고되고 끝에 There were undefined references.와 Label(s) may have changed. Rerun to get cross-references right.가 나란히 나옵니다. 2차에서는 sec:real이 사라지고 nope만 남으며, Label(s) may have changed도 없어집니다. 커널 파일 latex.ltx를 보면 종료 시 요약 경고는 There were undefined references 하나뿐이고, \ref 실패도 \cite 실패도 같은 깃발을 세웁니다. 즉 “undefined references”라고 쓰여 있어도 범인이 문헌 인용일 수 있습니다.
% first run
LaTeX Warning: Reference `nope' on page 1 undefined on input line 3.
LaTeX Warning: Reference `sec:real' 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.
% second run -- sec:real is resolved, nope is a real problem
LaTeX Warning: Reference `nope' on page 1 undefined on input line 3.
LaTeX Warning: There were undefined references.| 메시지 | 의미 | 할 일 |
|---|---|---|
Label(s) may have changed | 번호가 아직 수렴하지 않음 | 한 번 더 컴파일하면 끝 |
Reference `x' ... undefined | .aux에 \label{x}가 없음 | 2차에도 나오면 철자나 라벨 위치를 의심 |
There were undefined references | \ref나 \cite 중 무언가가 해결되지 않음 | 위쪽 개별 경고를 확인 |
Label `x' multiply defined | 같은 라벨이 두 곳 이상에 있음 | 고유하게 만들 것. 2차 실행부터만 나옴 |
Citation `x' ... undefined | .bbl에 그 문헌이 없음 | bibtex나 biber를 실행한 뒤 두 번 더 컴파일 |
rerunfilecheck: File ... has changed | hyperref의 북마크 파일이 바뀜 | 한 번 더 컴파일 |
두 번 돌려도 ??가 사라지지 않을 때
그 라벨이 정말로 .aux에 적히지 않았다는 뜻입니다. 확인은 순식간입니다. .aux를 열어 \newlabel{과 그 이름을 검색해 보십시오. 없다면 \label 쪽의 문제이고, 있는데도 ??가 나온다면 \ref 쪽의 철자 차이가 문제입니다. 참고로 ??가 굵게 인쇄되는 것은 우연이 아닙니다. 커널의 \@setref는 \nfss@text{\reset@font\bfseries ??}라고 쓰여 있어 놓치지 않도록 일부러 굵게 해 둔 것입니다. 종이에 뽑은 초고에서도 눈에 확 들어오는 이유가 여기 있습니다. 아래는 라벨이 .aux에 실리지 않는 대표적인 이유들입니다.
- 철자 차이.
\label{fig:setup}와\ref{fig:Setup}는 서로 다른 라벨입니다. 이름은 대소문자를 구별합니다. \includeonly로 해당 파일을 제외했다. 제외된 파일의\label은 아예 기록되지 않으므로 그것을 가리키는 참조는??로 남습니다. 제외가 걸려 있는 동안??가 나오는 것은 정상입니다..aux가 낡았거나 깨졌다. 이전 실행을 중단했거나 클래스·패키지를 바꿨을 때 일어납니다..aux,.toc,.out을 지우고 두 번 다시 돌리면 됩니다.\label이 움직이는 인자 안에 있다. 제목이나 캡션의 인자 안에 직접 쓰면.toc에 복제되어 거기서 다른 뜻이 될 수 있습니다.\caption중괄호 바깥, 바로 뒤에 두는 것이 안전합니다.- 애초에 다른 파일을 조판하고 있다. 여러 파일로 나뉜 구성에서는 자식 파일만 따로 컴파일해도 부모의
.aux는 갱신되지 않습니다. 반드시 부모에서 빌드하십시오. - 문헌 키가
.bib에 없다. 이것은??가 아니라[?]로 나옵니다.bibtex의 출력에I didn’t find a database entry가 있는지 확인하십시오.
\label을 \caption 앞에 두면 경고 없이 번호가 어긋난다
\label은 반드시 \caption 뒤에 두십시오. 앞에 두면 오류도 경고도 없이 전혀 다른 번호가 인쇄됩니다. 이것은 실측할 수 있습니다. \section을 셋 둔 문서에 그림을 둘 만들고, 첫 번째는 \label을 \caption 앞에, 두 번째는 뒤에 두고 두 번 컴파일하면 .aux에는 이렇게 적힙니다. \newlabel{fig:before}{{3}{1}{}{}{}}와 \newlabel{fig:after}{{2}{1}{}{}{}}. 앞에 둔 쪽은 본래 “그림 1”이어야 하는데 3, 곧 바로 앞 절 번호를 집어 갔습니다. 그리고 LaTeX의 경고는 0건입니다. PDF는 멀쩡히 생성되고 본문에는 당당히 “그림 3”이라고 인쇄됩니다. 구조는 이렇습니다. \label은 “지금 유효한 카운터”의 값을 기록하는데, 그 카운터를 figure로 바꾸고 번호를 올리는 일은 \caption의 몫입니다(\refstepcounter를 호출합니다). \caption 앞이라면 가장 최근에 \refstepcounter를 부른 것이 \section이므로 절 번호가 기록됩니다. 심사자가 “그림 3을 보라고 했는데 그림 3은 다른 것입니다”라고 써 보내야 비로소 알아채는 종류의 버그이며, \label은 \caption 바로 뒤라는 규칙을 외워 둘 값어치가 여기에 있습니다.
\begin{figure}[h]
\includegraphics{fig.pdf}
\caption{The right way round}
\label{fig:ok} % after \caption -- records the figure number
\end{figure}
\begin{figure}[h]
\label{fig:broken} % before \caption -- records the SECTION number
\includegraphics{fig.pdf}
\caption{Silently wrong}
\end{figure}환경 안의 \label: \item, equation, \section*
규칙은 하나뿐입니다. 번호를 올리는 명령 바로 뒤에 두는 것. enumerate라면 \item 바로 뒤, 수식이라면 환경 안, 절이라면 \section{…} 바로 뒤입니다. 여기서도 실수는 조용히 일어납니다. \begin{enumerate} 바로 뒤(첫 \item보다 앞)에 \label{it:bad}를 두고 두 번 컴파일하면 .aux에 \newlabel{it:bad}{{1}{1}{}{}{}}가 기록됩니다. 항목 번호가 아니라 바로 앞 절 번호인 1입니다. 경고는 역시 0건. \section*{…}에서도 같은 일이 일어납니다. 별표 제목은 카운터를 올리지 않으므로, 그 뒤의 \label은 그 앞에 있던 번호 붙은 절의 번호를 집어 갑니다. 별표 제목을 참조하고 싶다면 \phantomsection(hyperref)과 \addcontentsline을 함께 쓰거나, 애초에 참조하지 않도록 설계하십시오. 라벨 이름에 fig:, tab:, sec:, eq:, it: 같은 접두사를 붙이는 관행은 보기 좋게 정리하기 위해서만이 아닙니다. \ref{fig:x}라고 쓰는 순간 “그림을 가리키려 했다”는 의도가 지면에 드러나기 때문입니다. 나온 번호가 그림의 것이 아닐 때, 접두사가 있어야 알아챌 수 있습니다.
Label ... multiply defined는 두 번째 실행부터만 나온다
같은 \label{x}가 두 곳 이상에 있고, \ref{x}는 그중 나중에 읽힌 하나를 가리킵니다. 그리고 알아 둘 성질이 하나 있습니다. .aux를 지운 직후의 첫 실행에서는 이 경고가 아예 나오지 않습니다. 실측에서도 .aux를 삭제하고 한 번만 돌렸을 때 multiply defined 건수는 0이었습니다. 경고는 두 번째 실행 첫머리에, LaTeX이 .aux를 읽어 들이며 이미 정의된 라벨을 다시 정의하려 할 때 비로소 나옵니다. 즉 “한 번 컴파일했는데 경고가 없었다”는 라벨이 고유하다는 증거가 되지 못합니다. 문서 끝에는 LaTeX Warning: There were multiply-defined labels.라는 요약도 나옵니다. 원인은 거의 언제나 복사·붙여넣기입니다. 장별 파일을 복제했거나, \include로 같은 파일을 두 번 불러왔거나, subfiles 문서가 부모와 자식 양쪽에서 읽힌 경우입니다. 예방책은 파일 단위 접두사를 정해 두는 것으로(intro:fig:setup처럼), 그러면 다른 장에 붙여넣는 순간 눈으로 보고 알 수 있습니다.
Citation ... undefined와 BibTeX/Biber 실행 순서
문헌이 [?]에 머물러 있다면 필요한 실행은 네 번입니다. pdflatex, bibtex, pdflatex, pdflatex. 상호 참조는 두 번이면 되는데 문헌이 네 번인 이유는 중간에 다른 프로그램이 끼어들기 때문입니다. 첫 번째 pdflatex는 \cite된 키 목록을 .aux에 써 내고, 로그에는 No file document.bbl.과 Citation ... undefined가 나란히 나옵니다. 다음으로 bibtex가 .aux의 키 목록과 .bib을 대조해 .bbl(서식이 갖춰진 문헌 목록)을 만듭니다. 세 번째 pdflatex가 .bbl을 처음 읽어 각 문헌에 번호를 매겨 .aux에 기록합니다. 이 시점에도 본문의 \cite는 아직 해결되지 않았습니다. 네 번째에 이르러서야 번호가 들어갑니다. 실제 로그에서도 세 번째 실행에는 Citation ... undefined가 남은 채 Label(s) may have changed. Rerun to get cross-references right.가 나왔고, 네 번째에 사라졌습니다. biblatex를 쓸 때는 bibtex 대신 biber를 부르지만 횟수 구조는 같습니다. 존재하지 않는 키를 인용하면 bibtex 쪽이 Warning--I didn’t find a database entry for "missingkey"라고 알려 줍니다. 이런 유형의 문제에서는 LaTeX이 아니라 bibtex의 출력을 읽는 것이 결정적입니다.
pdflatex document # writes the cited keys into document.aux
bibtex document # reads document.aux + refs.bib, writes document.bbl
pdflatex document # reads the .bbl, numbers the entries
pdflatex document # finally resolves every \cite in the text
# with biblatex, swap the second line for:
# biber document\ref・\eqref・\cref・\nameref・\vref의 구분과 불러오기 순서
맨 \ref는 번호만 출력합니다. “그림”, “식” 같은 낱말과 괄호는 직접 써야 하는데, 손으로 쓰는 것은 언젠가 반드시 어긋납니다. 그 틈을 메우는 명령이 층으로 마련되어 있고 아래 표가 그 목록입니다. 다만 불러오기 순서에 엄격한 제약이 있습니다. cleveref는 hyperref보다 뒤, 그리고 varioref보다도 뒤에 불러와야 합니다. 순서를 어기면 cleveref.sty가 스스로 진단해 ! Package cleveref Error: cleveref must be loaded after hyperref!. 또는 ! Package cleveref Error: cleveref must be loaded after varioref!.를 보여 줍니다(둘 다 실제로 재현했습니다). 올바른 순서는 hyperref → varioref → cleveref입니다. 실무 지침으로는 새 문서라면 cleveref로 통일하는 것이 사고가 가장 적습니다. \cref{eq:e}는 “eq. (1)”, \Cref{sec:s}는 “Section 1”처럼 대상 종류에 맞는 낱말을 자동으로 붙여 주므로, 구성을 바꿔도 문구가 낡지 않습니다. 반대로 이어받은 공저 원고라면 혼용을 허용하고 공저자가 계속 \ref를 쓸 것을 전제하는 편이 현실적입니다.
| 명령 | 출력 예 | 제공처・주의 |
|---|---|---|
\ref | 1 | 커널. 번호만. hyperref가 있으면 링크가 됨 |
\pageref | 1 | 커널. .aux의 두 번째 값, 곧 쪽 번호 |
\eqref | (1) | amsmath. 수식 번호를 괄호와 함께 |
\cref | eq. (1) | cleveref. 대상 종류에 맞는 낱말을 자동으로 붙임 |
\Cref | Section 1 | cleveref. 문장 첫머리용으로 첫 글자를 대문자로 |
\autoref | section 1 | hyperref. \cref보다 단순하며 범위를 묶지 못함 |
\nameref | The section | nameref(hyperref가 불러옴). 제목 문자열을 출력 |
\vref | 1 on the previous page | varioref. 같은 쪽이면 번호만, 떨어져 있으면 쪽 안내를 덧붙임 |
% load order matters: hyperref, then varioref, then cleveref
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{varioref}
\usepackage{cleveref} % must come last of the three