교정(proofread)

심사자에게서 “12쪽 셋째 문단 넷째 줄”이라고 적힌 지적을 받아 본 적이 있다면, LaTeX 교정에 가장 먼저 필요한 것이 lineno 패키지의 행 번호라는 것을 이미 아실 겁니다. 상당수 학술지는 투고 단계에서 행 번호가 붙은 원고를 요구하고, 여백에 번호가 있으면 지적은 “L412” 한 마디로 끝납니다. 이 페이지는 교정 한 바퀴를 따라갑니다. 행에 번호를 매기고, 여백에 할 일을 붙이고, 공저자의 수정을 저자별 색으로 나누고, latexdiff로 “지난번 이후 무엇이 바뀌었는지”를 PDF로 보여 주고, chktexlachecknag 세 린터에게 기계적인 흠잡기를 맡깁니다. 아래의 모든 단계에는 실제로 돌린 출력이 붙어 있습니다.

심사용 원고에 행 번호 붙이기 — lineno 패키지

\usepackage{lineno}를 읽어 들이고 \linenumbers라고 쓰면, 그 뒤 본문의 모든 줄 왼쪽 여백에 행 번호가 나옵니다. 옵션으로 모양을 바꿀 수 있습니다. modulo는 다섯째 줄마다만 번호를 붙여 여백이 시끄러워지지 않게 하고, pagewise는 페이지마다 1부터 다시 세며, switch는 양면 조판에서 번호를 바깥쪽 여백에 놓습니다. 끄고 싶은 곳에서는 \nolinenumbers, 범위만 매기려면 linenumbers 환경을 씁니다. 문서의 일부에만, 이를테면 부록을 뺀 본문에만 번호를 매기는 것은 일상적인 요구입니다.

latex
\usepackage[modulo]{lineno}   % number every 5th line only
% \usepackage[pagewise]{lineno} % restart the count on each page
% \usepackage[switch]{lineno}   % outer margin, for twoside
\linenumbers
...
\nolinenumbers                % stop numbering (e.g. before the appendix)

여기에, 모르면 심사 대화가 어긋나는 함정이 있습니다. 기본값에서는 별행 수식에 행 번호가 붙지 않습니다. amsmathequationalign이 들어간 문서로 재어 보니 본문 행 번호는 1, 2, 3으로 수식을 건너뛰었습니다. 같은 문서에 \usepackage[mathlines]{lineno}를 지정하면 1부터 6까지 이어지며 수식의 각 줄에도 번호가 붙습니다. 즉 mathlines의 유무에 따라 “L4”가 가리키는 곳이 달라집니다. 심사자와 저자가 서로 다른 설정으로 만든 PDF를 보고 있으면 논의가 맞물리지 않습니다. 수식이 많은 원고라면 mathlines를 켜 두는 편이 안전합니다.

옵션 또는 명령효과
\linenumbers이후 본문에 행 번호를 매기기 시작
\nolinenumbers행 번호를 멈춤(부록 직전 등에 둠)
modulo5의 배수 줄에만 번호를 출력
pagewise페이지마다 1부터 다시 셈
switch양면 조판에서 번호를 바깥쪽 여백에 놓음
mathlines별행 수식의 각 줄에도 번호를 매김(기본은 끔)

lineno는 1995년부터 이어져 온 오래된 도구이며, TeX Live 2024에 들어 있는 것은 2023년 5월 20일자 v5.3입니다. 패키지 첫머리의 저작권 표시에는 세 세대의 관리자가 나란히 적혀 있습니다. 1995–2003년은 Stephan I. Böttcher, 4.x 계열은 Uwe Lück, 그리고 5.x 계열은 2021년부터 Karl Wette가 이어받았습니다. 30년치 호환성 뒤치다꺼리를 해 온 패키지라 다른 패키지와 함께 쓸 때의 동작이 늘 순순하지는 않습니다. 읽어 들이는 순서가 결과를 바꾸기도 하므로, 번호가 도무지 붙지 않으면 lineno를 맨 마지막에 읽어 보세요.

latexdiff 사용법 — 공저자에게 “무엇이 바뀌었는지” PDF로 보여 주기

latexdiff old.tex new.tex > diff.tex를 실행하고, 생긴 diff.tex를 평소처럼 pdflatex로 돌리면 끝입니다. 그것만으로 추가된 표현과 삭제된 표현이 시각적으로 구분된 PDF가 나옵니다. 실제로 돌려 보면 latexdiff는 원문에 \DIFaddbegin, \DIFadd{...}, \DIFdelbegin, \DIFdel{...} 같은 표시를 끼워 넣고, 프리앰블에 \RequirePackage[normalem]{ulem}과 색 정의를 더합니다. 이 도구가 교정 도구 가운데 유독 값어치가 높은 이유는 상대에게 “LaTeX을 읽는” 일을 시키지 않아도 되기 때문입니다. 차이가 git의 diff가 아니라 조판된 지면 위에 드러납니다.

terminal
# the whole workflow
latexdiff old.tex new.tex > diff.tex
pdflatex diff.tex

# one file made of many \input files: flatten first
latexdiff --flatten old/main.tex new/main.tex > diff.tex

# diff straight against a git revision (produces main-diffHEAD~1.tex)
latexdiff-vc --git --flatten -r HEAD~1 main.tex

# a different visual style, e.g. bold instead of underline
latexdiff --type=CFONT old.tex new.tex > diff.tex

기본 모양은 흔히 말하는 “추가는 밑줄, 삭제는 취소선”보다 조금 더 구체적입니다. TeX Live 2024에 들어 있는 것은 v1.3.3이고(latexdiff --version은 “(c) 2004-2022 F J Tilmann”이라고 밝힙니다), 삽입되는 프리앰블은 \DIFadd{\protect\color{blue}\uwave{#1}}, \DIFdel{\protect\color{red}\sout{#1}}로 정의합니다. 즉 추가는 파란 물결 밑줄, 삭제는 빨간 취소선입니다. 이 기본값에는 --type=UNDERLINE이라는 이름이 붙어 있고, --type에는 CTRADITIONAL, TRADITIONAL, CFONT, FONTSTRIKE, INVISIBLE, CHANGEBAR, CCHANGEBAR, CULINECHBAR, CFONTCHBAR, BOLD, PDFCOMMENT가 나열됩니다. 흑백으로 인쇄할 심사자에게 보낸다면 FONTSTRIKEBOLD 쪽이 훨씬 잘 읽힙니다.

latex
% What latexdiff actually writes into diff.tex (excerpt of a real run):
\RequirePackage[normalem]{ulem}
\providecommand{\DIFadd}[1]{{\protect\color{blue}\uwave{#1}}}
\providecommand{\DIFdel}[1]{{\protect\color{red}\sout{#1}}}
...
The measured value was \DIFdelbegin \DIFdel{3.2}\DIFdelend
  \DIFaddbegin \DIFadd{3.4}\DIFaddend \,mm.

실무에서 값을 하는 자리는 둘입니다. 하나는 수정 원고 제출입니다. 많은 학술지가 깨끗한 원고와 함께 “변경점을 강조한 판”을 요구하는데, latexdiff의 출력이 곧 그 제출물입니다. 다른 하나는 공저자에게 보내는 경우입니다. 여러 파일로 나뉜 문서라면 --flatten\input을 펼친 뒤 비교하고, latexdiff-vc --git --flatten -r HEAD~1 main.tex는 작업 트리를 예전 커밋과 곧바로 비교해 main-diffHEAD~1.tex를 써냅니다. 주의할 점 하나는, latexdiff가 LaTeX을 문법이 아니라 텍스트로 다루기 때문에 표나 그림 안의 변경에서는 마크업이 깨질 수 있다는 것입니다. 깨지면 --append-safecmd로 해당 명령을 안전 목록에 넣거나, --exclude-textcmd로 그 환경을 제외하세요.

공저자별로 색을 나누기 — changes 패키지

latexdiff가 두 판을 바깥에서 비교한다면, changes는 변경을 소스 안에 적어 둡니다. \definechangesauthor[name={Ada Lovelace}, color=blue]{AL}처럼 저자를 등록해 두고, \added[id=AL]{...}, \deleted[id=AL]{...}, \replaced[id=AT]{새}{옛}, \comment[id=AT]{...}로 표시합니다. \listofchanges를 놓으면 “List of changes” 목록이 나오고, 실측에서는 Added (AL): ..., Replaced (AT): ..., Deleted (AL): ..., Commented (AT): ...가 각각 누구의 손인지와 함께 나열되었습니다. TeX Live 2024에 들어 있는 것은 v4.2.1(2021/07/15) 입니다.

latex
\usepackage[markup=underlined]{changes}   % draft look; the default
\definechangesauthor[name={Ada Lovelace}, color=blue]{AL}
\definechangesauthor[name={Alan Turing},  color=orange]{AT}
\begin{document}
\listofchanges
The engine \added[id=AL]{weaves algebraical patterns} and
\replaced[id=AT]{computes}{calculates} the numbers.
\deleted[id=AL]{This sentence is redundant.}
\comment[id=AT]{Check the citation here.}

마무리 한 수가 중요합니다. \usepackage[final]{changes}로 바꾸면 이 패키지는 마크업을 숨기는 것이 아니라 편집을 적용합니다. 실측에서는 \added의 표현이 남고, \replaced는 새 쪽만 남았으며, \deleted의 한 문장과 \comment는 사라져 “The engine weaves algebraical patterns and computes the numbers. Ordinary text.”라는 완성 원고가 되었습니다. 즉 최종판을 만들려고 명령을 손으로 지우고 다닐 필요가 없습니다. 뒤집어 말하면 final로 바꾸기 전과 후의 PDF는 내용이 다르므로, 투고 전에 반드시 final로 다시 조판해 읽어 보아야 합니다.

여백에 할 일 붙이기 — todonotes\todo

%로 쓴 주석은 컴파일하면 사라집니다. 그래서 공저자에게는 닿지 않습니다. todonotes는 할 일을 출력에 남기기 위한 도구입니다. \todo{check this number}는 여백에 쪽지를 그리고, \todo[inline]{...}는 본문 흐름 속에 띠로 놓이며, \missingfigure{plot of the residuals}는 “여기에 그림이 들어간다”는 자리표시 상자를 그립니다. \listoftodos는 앞머리에 “Todo list”라는 목록을 만들고, 실측에서는 각 항목이 페이지 번호와 함께 나열되었습니다. 다 읽기도 전에 무엇이 남았는지 한눈에 보입니다.

latex
\usepackage[textwidth=3cm]{todonotes}
% \usepackage[disable]{todonotes}   % final version: hides notes AND the list
\begin{document}
\listoftodos
Some text.\todo{check this number}
More text.\todo[inline,color=green!30]{rewrite this paragraph}
\missingfigure{plot of the residuals}

완성판을 만들 때는 \usepackage[disable]{todonotes}로 바꿔 넣습니다. 실측해 보면 여백의 쪽지도, \missingfigure 상자도, 앞머리의 “Todo list”까지 통째로 사라졌습니다. 명령을 쓴 자리는 그대로 남으므로 다음 개정에서 disable을 빼면 할 일이 다시 나타납니다. % 주석과의 결정적 차이가 여기에 있습니다. 메모가 파괴되는 것이 아니라 숨겨지는 것이죠. 위험은 disable을 켠 채 투고해 미처리 항목이 남은 것을 아무도 알아채지 못하는 경우입니다. 투고 직전에 “disable을 한 번 끄고 \listoftodos를 확인한다”를 고정 절차로 넣어 두면 안전합니다.

chktexlacheck — pdflatex가 아무 말 없는 오류를 잡는다

이 둘은 조판으로는 맞지만 독자에게는 불친절한 잘못을 잡아냅니다. pdflatex는 “컴파일이 되는가”만 보기 때문입니다. 실례를 들겠습니다. As shown in Fig. 1 and in Table \ref{tab:one}, see also \cite{knuth1984}.라는 한 줄을 pdflatex에 넣어도 돌아오는 것은 LaTeX Warning: Reference 'tab:one' on page 1 undefined라는 상호 참조 이야기뿐입니다. 같은 줄을 chktex에 넣으면 다음 경고가 두 번 나옵니다.

terminal
$ chktex -q ref.tex
Warning 2 in ref.tex line 3: Non-breaking space (`~') should have been used.
As shown in Fig. 1 and in Table \ref{tab:one}, see also \cite{knuth1984}.
                               ^
Warning 2 in ref.tex line 3: Non-breaking space (`~') should have been used.
As shown in Fig. 1 and in Table \ref{tab:one}, see also \cite{knuth1984}.
                                                       ^

여기서 한 가지 바로잡을 것이 있습니다. 흔히 “chktexFig. 1~ 누락을 지적한다”고 하지만, 기본 설정에서는 지적하지 않습니다. chktex~ 검사(경고 2)가 지켜보는 것은 chktexrcLinker 목록에 있는 것들——\ref, \vref, \pageref, \eqref, \cite——의 바로 앞뿐이고, Fig. 1 같은 맨 문자열은 대상이 아닙니다. Fig.를 잡게 하려면 Abbrev 목록에 직접 더해야 합니다. 더한 뒤 같은 파일을 검사하니 이번에는 Warning 12 in ref.tex line 3: Interword spacing should perhaps be used.Fig. 자리를 가리켰습니다. 마침표 뒤에 문장 끝의 넓은 공백이 들어가 버린다는 지적입니다.

경고 번호지적하는 내용무엇이 걸리는가
2Non-breaking space (~) should have been used.Table \ref{...}see \cite{...} 바로 앞의 보통 공백
8Wrong length of dash may have been used.범위나 대시에 - 하나만 쓴 경우
11You should use \ldots to achieve an ellipsis....를 줄임표 명령 대신 친 경우
12Interword spacing should perhaps be used.Abbrev에 등록한 약어의 마침표 뒤
18Use TeX quotes as an alternative to the straight double quote.곧은 큰따옴표 "를 쓴 경우
26You ought to remove spaces in front of punctuation.문장 부호 앞에 공백이 있는 경우
29$\times$ may look prettier here.5x10^3처럼 x를 곱셈 기호로 쓴 경우
9'itemize' expected, found 'enumerate'.환경 중첩이 어긋난 경우
terminal
# The messages exactly as chktex prints them (one run, one bad file):
Warning 18 in bad.tex line 4: Use either `` or '' as an alternative to `"'.
Warning 11 in bad.tex line 4: You should use \ldots to achieve an ellipsis.
Warning 26 in bad.tex line 4: You ought to remove spaces in front of punctuation.
Warning 29 in bad.tex line 5: $\times$ may look prettier here.
Warning  8 in bad.tex line 7: Wrong length of dash may have been used.
Warning  9 in mm.tex  line 5: `itemize' expected, found `enumerate'.

lacheck는 훨씬 과묵하고, 그만큼 역할이 분명합니다. chktex가 정규 표현식으로 글쓰기 버릇을 감시한다면, lacheck는 문서의 구조적 정합성을 봅니다. \begin{itemize}\end{enumerate}로 닫은 파일을 건네면 양쪽 위치를 짝지어 알려 줍니다. "mm.tex", line 5: <- unmatched "\end{enumerate}""mm.tex", line 3: -> unmatched "\begin{itemize}"입니다. 같은 잘못을 chktexWarning 9로 잡지만, 가리키는 것은 닫은 쪽 한 곳뿐입니다. 긴 문서에서 “중첩이 어디서부터 어긋났는가”를 알고 싶을 때는 lacheck가 답에 더 빨리 데려다줍니다. 둘 다 돌리는 것이 정답입니다.

terminal
$ lacheck mm.tex
"mm.tex", line 5: <- unmatched "\end{enumerate}"
"mm.tex", line 3: -> unmatched "\begin{itemize}"

# chktex exits with the number of warnings, so CI can gate on it
$ chktex -q ref.tex >/dev/null; echo $?
2
# mute one check by number and it passes
$ chktex -q -n2 ref.tex >/dev/null; echo $?
0
# teach it your abbreviations via a local rc file
$ printf 'Abbrev { Fig. Eq. Sec. }\n' > my.chktexrc
$ chktex -q -l my.chktexrc ref.tex

chktex의 종료 상태가 경고 개수 그 자체라는 점은 CI에 넣을 때 힘을 발휘합니다. 위 실측에서 chktex -q ref.tex의 종료 코드는 2(경고 2건)였고, -n2로 2번 경고를 잠재우니 0이 되었습니다. 즉 “이 검사만은 허용한다”는 방침을 저장소 설정으로 적어 둘 수 있습니다. 프로젝트별 설정은 -l my.chktexrc로 읽히거나, % chktex-file 26 같은 소스 내 지시로 국소적으로 억제합니다.

낡은 명령과 쓰이지 않은 레이블을 꾸짖기 — nagrefcheck

nag는 “돌아가기는 하지만 낡은” 표기를 컴파일 시점에 지적하는 패키지입니다. \RequirePackage[l2tabu,orthodox]{nag}\documentclass 앞에 두고 돌리면 로그에 쓴소리가 줄줄이 나옵니다. Package nag Warning: Command \bf is an old LaTeX 2.09 command.에 이어 Use \bfseries or \textbf instead on input line 5., 그리고 $$...$$ is obsolete. Use \[...\] et al. instead, Command \centerline is TeX. Use \centering or center environment instead, Package epsf is obsolete. Use the graphicx package instead., 마무리로 Package nag Warning: 5 complaints in total.입니다. 수십 년 전 템플릿을 물려받은 원고를 처음 통과시킬 때 효과가 큰 도구입니다.

투고 전에 한 번은 돌려 볼 만한 것이 refcheck입니다(TeX Live 2024 수록은 v1.9.1). \usepackage{refcheck}를 더하고 두 번 컴파일하면, 아무도 참조하지 않는 레이블을 로그에 올려 줍니다. 실측에서는 Package refcheck Warning: Unused label 'sec:unused' on input line 5.Package refcheck Warning: Unused label 'eq:never' on input line 7.이 나왔습니다. 번호를 붙였는데 한 번도 인용하지 않은 식은 대개 “번호를 떼야 할 식”이고, 분량을 줄이라는 요구를 받았을 때 가장 먼저 덜어낼 수 있는 자리이기도 합니다.

latex
\RequirePackage[l2tabu,orthodox]{nag}   % MUST come before \documentclass
\documentclass{article}
\usepackage{refcheck}                   % lists labels nobody refers to
% Then: pdflatex paper.tex && grep -E "nag Warning|refcheck Warning" paper.log

교정 한 바퀴를 어떻게 돌릴 것인가

어떤 도구를 집을지는 누구에게 무엇을 보여 줄 것인가로 정해집니다. 자신과 공저자에게만 필요한 작업 중의 할 일은 todonotes, 누구의 손에 의한 수정인지 남겨야 할 때는 changes, 심사자에게 “이전 판에서 무엇이 바뀌었는지” 보여 주는 것은 latexdiff, 그리고 기계에 맡길 수 있는 흠잡기는 chktexlachecknag입니다. 이 넷은 서로 겹치지 않습니다. 아래 순서로 돌리면 같은 원고를 두 번 손대지 않아도 됩니다.

  • 집필 중에는 todonotes\todo{...}\missingfigure{...}로 할 일을 남기고, \listoftodos로 남은 개수를 셉니다.
  • 공저자에게 돌리기 전에 chktexlachecknag를 돌려 기계가 잡을 수 있는 흠을 먼저 지웁니다. 사람의 눈을 그런 데 쓰지 않습니다.
  • 보낼 때는 lineno\linenumbers를 켠 판을 만듭니다. 수식이 많으면 mathlines도 붙입니다.
  • 돌아온 교정 표시는 changes\added / \replaced / \deleted로 받고, \listofchanges로 처리 누락을 막습니다.
  • 판을 넘길 때마다 latexdiff --flatten old.tex new.tex > diff.tex로 만든 차이 PDF를 함께 보냅니다. 상대는 그것만 열면 됩니다.
  • 투고 직전에 refcheck로 쓰이지 않은 레이블을 정리하고, todonotesdisable, changesfinal로 바꿔 다시 조판합니다.