LaTeX의 페이지 나누기는 줄 나누기와 전혀 다른 원리로 결정됩니다. 문단은 가능한 모든 분할을 한꺼번에 저울질해 전체적으로 가장 좋은 것을 고르지만, 페이지는 한 번 끊고 나면 되돌아가지 않습니다. 그래서 “이 페이지가 왜 여기서 끝났는가”를 모른 채 \newpage를 찍으면 뒤따르는 마흔 페이지가 함께 딸려 갑니다. 이 페이지에서는 문서 리듬의 장치를 실측합니다. widow와 orphan(\widowpenalty와 \clubpenalty, 기본값은 둘 다 150), \raggedbottom과 \flushbottom의 차이, 그리고 플로트가 본문을 밀어내는 방식을 살펴보고, 나쁜 페이지 나누기를 힘으로 밀어붙이는 대신 진단하는 절차를 세웁니다.
페이지는 왜 거기서 끝나는가 — 줄 나누기와의 결정적 차이
페이지가 끝나는 위치는 그 페이지를 조판하는 시점에 알고 있는 정보만으로 정해집니다. TeX는 본문을 위에서 아래로 흘려 넣으면서 줄과 줄 사이에 나타나는 끊어도 되는 지점을 후보로 지켜보다가, 쌓인 높이가 목표를 넘는 순간 지금까지 본 후보 가운데 비용이 가장 낮은 곳으로 되돌아가 끊습니다. 문단 안의 줄 나누기가 가능한 모든 배치를 훑는 전역 최적화인 데 비해, 페이지 나누기는 앞으로 나아가면서 내리는 순차적 결정입니다. 실무에서 가장 크게 작용하는 차이가 이것이고, 40쪽의 페이지 나누기를 고치면 41쪽부터 전부 밀리는 이유도 여기에 있습니다.
순차적으로 결정한다는 것은 나중에 협상할 여지가 없다는 뜻이기도 합니다. TeX에 남은 수단은 후보마다 “여기서 끊고 싶지 않은 정도”를 숫자로 신고하는 것뿐이며, 그것이 페널티입니다. 페널티는 끊을 수 있는 지점에 붙는 정수로, 값이 클수록 그곳에서 끊기를 꺼립니다. 10000은 “절대 끊지 않는다”를 뜻하는 특별한 값이고, -10000은 반대로 “반드시 끊는다”입니다. 문단 끝의 한 줄이 홀로 남는 사고를 막는 장치도, 플로트를 놓는 장치도, 제목 바로 뒤에서 끊지 못하게 하는 장치도 모두 이 한 종류의 숫자로 표현되어 있습니다. 그러므로 페이지 나누기를 조정한다는 것은 사실상 페널티를 조정한다는 뜻입니다.
widow와 orphan — \widowpenalty와 \clubpenalty의 기본값은 150
article 클래스에서 재어 보면 \widowpenalty와 \clubpenalty는 모두 150, \displaywidowpenalty는 50입니다. widow는 문단의 마지막 줄이 페이지 맨 위에 한 줄만 남는 것이고, orphan은 문단의 첫 줄이 페이지 맨 아래에 한 줄만 남는 것입니다. 독일어권에서는 앞의 것을 Hurenkind, 뒤의 것을 Schusterjunge라고 구분해 부릅니다. 여기서 처음 걸려 넘어지는 것이 이름입니다. orphan을 막는 매개변수는 \orphanpenalty가 아니라 \clubpenalty이며, 그런 이름의 변수는 TeX에도 LaTeX에도 없습니다. 옛 인쇄 현장에서 문단 첫머리의 고립된 줄을 club line이라 부르던 흔적입니다.
% Print the defaults into the .log yourself:
% pdflatex penalties.tex -> grep MEASURE penalties.log
\documentclass{article}
\begin{document}
\typeout{MEASURE widowpenalty=\the\widowpenalty}
\typeout{MEASURE clubpenalty=\the\clubpenalty}
\typeout{MEASURE displaywidowpenalty=\the\displaywidowpenalty}
\typeout{MEASURE brokenpenalty=\the\brokenpenalty}
x
\end{document}150이라는 값에는 내력이 있습니다. TeX Live 2024의 latex.ltx 514–516행에 이 세 대입이 나란히 있는데, 똑같은 세 줄이 커누스의 plain.tex 290–292행에도 있습니다. 즉 LaTeX는 plain TeX의 기본값을 그대로 물려받았을 뿐이고 40년 동안 한 번도 손보지 않았습니다. 그리고 150은 실무적으로 거의 힘을 쓰지 못합니다. 페이지를 한 줄 줄일 때 생기는 흉함(badness)은 세로로 늘어날 여백이 거의 없는 본문에서 손쉽게 1000을 넘습니다. 150짜리 페널티는 그 싸움에서 대개 집니다.
| 매개변수 | 기본값 | 무엇을 막는가 |
|---|---|---|
\widowpenalty | 150 | 문단의 마지막 줄이 페이지 맨 위에 홀로 남는 것 |
\clubpenalty | 150 | 문단의 첫 줄이 페이지 맨 아래에 홀로 남는 것 |
\displaywidowpenalty | 50 | 별행 수식 바로 앞에 한 줄만 남는 것 |
\brokenpenalty | 100 | 하이픈으로 끊긴 줄 바로 뒤에서 페이지를 나누는 것 |
\interlinepenalty | 0 | 문단 도중 아무 곳에서나 나누는 것(기본값에서는 막지 않음) |
\predisplaypenalty | 10000 | 별행 수식 바로 앞에서 나누는 것(사실상 금지) |
LaTeX 자신이 이 기본값을 포기하는 곳이 딱 한 군데 있습니다. article.cls의 thebibliography 환경은 시작하자마자 \sloppy를 부르고 \clubpenalty4000, \widowpenalty4000을 설정합니다(TeX Live 2024에 들어 있는 article.cls 576–579행). 참고문헌 목록은 짧은 항목이 길게 이어지므로 widow가 자주 생기고, 한 항목이 두 페이지에 걸치면 정말 읽기 어렵습니다. 표준 클래스가 이 한 곳에서만 힘을 쓰는 이유가 그것입니다. 뒤집어 말하면 본문에서는 표준 클래스가 아무것도 해 주지 않는다는 뜻이기도 합니다.
\widowpenalty=10000으로 하면 무슨 일이 생기는가 — 실측한 거래
widow는 사라지지만 대신 짧은 페이지가 생깁니다. 한 페이지 20줄, 문단 6줄짜리 문서를 \flushbottom으로 조판해 재어 보면, 기본값 그대로는 1쪽에 23줄이 들어가고 네 번째 문단의 마지막 줄만 2쪽 맨 위에 남습니다. \widowpenalty=10000을 더하면 1쪽은 22줄로 줄고 마지막 두 줄이 함께 다음 쪽으로 넘어가 widow가 사라집니다. 다만 로그에 다음 한 줄이 늘어납니다.
Underfull \vbox (badness 10000) has occurred while \output is active []이 경고는 “페이지의 세로 빈틈을 다 채우지 못했다”는 뜻이며 \flushbottom일 때만 나옵니다. 같은 실험을 \raggedbottom으로 하면 경고가 한 번도 나오지 않습니다. 페이지가 아래끝까지 닿지 않아도 되므로 채울 필요 자체가 없기 때문입니다. 즉 \widowpenalty=10000의 대가는 \flushbottom에서는 행간이 벌어져 고르지 않게 되고, \raggedbottom에서는 페이지 아래쪽 여백이 들쭉날쭉해지는 형태로 나타납니다. 어느 쪽이든 대가가 0은 아닙니다. 그래도 본문 위주의 원고라면 10000은 충분히 방어할 수 있는 설정입니다. 홀로 남은 한 줄은 독자가 반드시 알아채지만, 한 줄 일찍 끝난 페이지는 대부분 알아채지 못하기 때문입니다.
% A defensible pair of settings for a text-heavy manuscript.
\widowpenalty=10000
\clubpenalty=10000
% Also stop a two-line gap opening before a displayed formula:
\displaywidowpenalty=10000
% Prefer this over sprinkling \newpage through the source.\raggedbottom과 \flushbottom의 차이, 그리고 지금 쓰고 있는 쪽
\flushbottom은 모든 페이지의 아래끝을 맞추기 위해 세로 여백을 늘였다 줄였다 하고, \raggedbottom은 아래끝이 들쭉날쭉해지는 것을 허용합니다. 많은 해설이 여기서 틀립니다. 단면 조판 article의 기본값은 \flushbottom이 아니라 \raggedbottom입니다. article.cls, report.cls, book.cls는 모두 끝부분에서 \if@twoside \else \raggedbottom \fi를 실행하므로, twoside를 지정하지 않았다면 \raggedbottom이 걸려 있습니다. 기본이 twoside인 book만 flush-bottom 쪽에 남습니다. 두 단으로 지정하면 이 클래스들 모두 \flushbottom을 명시적으로 부릅니다.
| 클래스 지정 | 실제로 걸리는 것 | 페이지 아래끝의 모습 |
|---|---|---|
\documentclass{article} | \raggedbottom | 페이지마다 아래끝 높이가 달라짐 |
\documentclass[twoside]{article} | \flushbottom | 아래끝이 맞음. 행간이 늘어날 수 있음 |
\documentclass{book} | \flushbottom | book은 기본이 twoside이므로 이쪽 |
\documentclass[twocolumn]{article} | \flushbottom | 클래스가 명시적으로 호출. 두 단은 어긋나면 눈에 띔 |
장치는 맥이 빠질 만큼 단순합니다. latex.ltx에서 \raggedbottom은 \def\@textbottom{\vskip \z@ \@plus.0001fil}, \flushbottom은 \let\@textbottom\relax로 정의되어 있을 뿐입니다. 앞의 것은 “길이는 0이지만 무한히 늘어나는” 글루를 페이지 맨 아래에 놓는 것으로, 이 fil이 본문 쪽의 얼마 안 되는 신축을 압도하므로 남는 양이 전부 아래쪽 여백으로 빨려 들어갑니다. \meaning\@textbottom을 출력해 보면 article은 macro:->\vskip \z@ \@plus .0001fil을, book은 \relax를 돌려줍니다. 프리앰블에 한 줄만 써 보면 자기 문서가 어느 쪽인지 그 자리에서 확인할 수 있습니다.
% Which bottom mode am I in? Put this in the preamble and read the .log.
\makeatletter
\AtBeginDocument{\typeout{MEASURE textbottom=\meaning\@textbottom}}
\makeatother
% article -> macro:->\vskip \z@ \@plus .0001fil (\raggedbottom)
% book -> \relax (\flushbottom)플로트가 페이지를 밀어낼 때 — \topfraction과 \textfraction
페이지 나누기가 알 수 없어 보일 때 범인은 대개 플로트입니다. LaTeX는 그림이나 표를 놓기 전에 그 페이지에서 차지해도 되는 비율과 개수를 확인합니다. 표준 클래스에서 실측한 값은 이렇습니다. 위쪽 플로트는 본문 높이의 \topfraction = .7까지, 아래쪽은 \bottomfraction = .3까지, 본문은 최소 \textfraction = .2가 남아야 하고, 한 페이지의 플로트는 합쳐서 totalnumber = 3개(위 topnumber = 2, 아래 bottomnumber = 1)까지입니다. 이 가운데 하나라도 어기는 그림은 그 페이지에 놓이지 않고 뒤쪽 페이지로 미뤄집니다. 미뤄진 플로트는 뒤따르는 본문보다 먼저 놓이므로, 결과적으로 “글은 12쪽, 그림은 14쪽”이라는 어긋남이 생깁니다.
| 매개변수 | 기본값 | 의미 |
|---|---|---|
\topfraction | .7 | 위쪽 플로트가 차지해도 되는 본문 높이의 비율 |
\bottomfraction | .3 | 아래쪽 플로트가 차지해도 되는 비율 |
\textfraction | .2 | 플로트가 있는 페이지에 최소한 남겨야 할 본문 비율 |
\floatpagefraction | .5 | 플로트 전용 페이지를 만들어도 되는 최소 채움 비율 |
totalnumber | 3 | 한 페이지에 놓을 수 있는 플로트 총수 |
topnumber | 2 | 페이지 위쪽에 놓을 수 있는 플로트 수 |
bottomnumber | 1 | 페이지 아래쪽에 놓을 수 있는 플로트 수 |
플로트가 원인일 때는 로그가 먼저 알려 줍니다. [h]만 지정한 그림이 그 자리에 들어가지 못하면 LaTeX는 LaTeX Warning: 'h' float specifier changed to 'ht'.라고 말하고 알아서 위쪽으로 돌립니다. 미뤄진 플로트가 너무 쌓이면 ! LaTeX Error: Too many unprocessed floats.(latex.ltx의 \@fltovf)로 멈춥니다. 실무에서는 절의 경계에 placeins의 \FloatBarrier를 두어 그림이 자기 절 밖으로 새어 나가지 못하게 하거나, afterpage 패키지의 \afterpage{\clearpage}로 “지금 페이지를 다 조판한 뒤에 비우는” 방법이 정석입니다. 맨 \clearpage를 그 자리에 쓰면 본문 한가운데에 큰 여백이 생깁니다.
\usepackage{placeins} % \FloatBarrier
\usepackage{afterpage} % \afterpage{\clearpage}
% Keep every figure inside the section that discusses it:
\let\oldsection\section
\renewcommand\section{\FloatBarrier\oldsection}
% Or loosen the rules instead of forcing breaks:
\renewcommand{\topfraction}{0.85}
\renewcommand{\textfraction}{0.1}
\renewcommand{\floatpagefraction}{0.75}나쁜 페이지 나누기를 고치는 법 — 힘으로 밀기 전에 할 일
순서가 정해져 있습니다. 먼저 원인을 짚고, 다음으로 전역 설정을 고치고, 마지막으로 그래도 남은 한 곳만 손으로 건드립니다. 거꾸로 하면 원고에 문장 하나만 더해도 손작업이 전부 무너집니다. 원인 파악은 .log에서 시작합니다. Underfull \vbox가 나오면 \flushbottom과 페널티가 서로 잡아당기는 중이고, float specifier changed가 나오면 플로트가 원인이며, Overfull \hbox가 줄줄이 나오면 줄 나누기 쪽 문제라 다른 페이지의 영역입니다. 원인을 알고 나서 페널티, 플로트 비율, 문단 자체의 길이 가운데 무엇을 움직일지 정합니다.
.log를 읽습니다.Underfull \vbox,float specifier changed,Overfull \hbox가운데 무엇이 나왔는지로 범인이 갈립니다.- 프리앰블에서
\widowpenalty와\clubpenalty를 올립니다. 문서 전체에 걸리므로 고친 곳이 다시 무너지지 않습니다. - 플로트가 원인이면
\FloatBarrier를 쓰거나\topfraction을 조정합니다. 위치 지정을[htbp]처럼 넓히는 것도 효과가 있습니다. - 문단 자체를 한 줄 줄이거나 늘립니다. 원고의 한 마디를 고치는 것이 실은 가장 오래가는 수정일 때가 많습니다.
\enlargethispage{\baselineskip}으로 그 페이지만 한 줄 높입니다. 여기까지 와서야 비로소 손작업입니다.\newpage와\pagebreak는 마지막 수단입니다. 넣고 나면 원고를 고칠 때마다 위치를 다시 확인해야 합니다.
“문단을 한 줄 줄여 달라”고 TeX에게 부탁하는 도구가 \looseness입니다. 문단 안에서 \looseness=-1이라 쓰면 “가능하면 한 줄 짧게 조판하라”, +1이면 한 줄 길게라는 뜻입니다. 다만 실측 결과는 냉정합니다. \tolerance=9999, \emergencystretch=3em을 설정한 문단에서 \looseness=1은 확실히 6줄을 7줄로 늘렸지만, 대신 Underfull \hbox가 6개 나왔습니다. 그리고 정작 사람들이 원하는 쪽인 \looseness=-1은 줄 수를 전혀 바꾸지 못했습니다. \tolerance 범위 안에 더 짧은 해가 존재하지 않았기 때문입니다. \looseness는 시도해 볼 만한 한 수이지 반드시 통하는 지렛대가 아닙니다. 듣지 않을 때는 원고에서 낱말 하나를 지우는 편이 빠릅니다.
% Ask for one line fewer inside the paragraph itself.
% \looseness is reset by every \par, so it must sit inside the paragraph.
\tolerance=9999 \emergencystretch=3em
Some paragraph text ... \looseness=-1
% Only after the above has failed: one page, one line taller.
\enlargethispage{\baselineskip}\pagebreak・\linebreak・\-는 어디서 다루는가
이 페이지는 문서 전체의 리듬을 다루고, 개별 끊기 명령은 다른 페이지의 담당입니다. 페이지 쪽의 \pagebreak[n]・\nopagebreak[n](n은 0–4), 무조건적인 \newpage, 플로트까지 비워 내는 \clearpage, 양면 조판에서 빈 페이지를 채우는 \cleardoublepage는 “페이지 나누기 조정”에서 다룹니다. 줄 쪽의 \\, \newline, \linebreak[n], \nolinebreak[n], 임의 하이픈 \-, 프리앰블의 \hyphenation{man-u-script}, hyphenat 패키지는 “줄 나누기 조정”에서 다룹니다. 문단 자체의 구조와 \par는 “문단과 줄바꿈”이 주인입니다. 그리고 Overfull \hbox, \sloppy, \emergencystretch는 전용 오류 페이지에서 자세히 다루며, \sloppy가 실은 네 개의 대입(\tolerance 9999, \emergencystretch 3em, \hfuzz .5\p@, \vfuzz\hfuzz)에 지나지 않는다는 것도 그쪽에서 측정해 두었습니다.
끝으로, 이 무리 가운데 LaTeX가 말없이 이미 쓰고 있는 것이 하나 있습니다. \section 같은 제목 명령은 바로 뒤 문단에 대해 \clubpenalty를 일시적으로 10000으로 올려, 제목 직후에 페이지가 나뉘지 않게 합니다(latex.ltx의 \@clubpenalty 장치). 그래서 “제목만 페이지 맨 아래에 홀로 남는” 사고는 표준 LaTeX에서는 사실상 일어나지 않습니다. 만약 일어났다면, 그 제목 명령을 직접 만든 매크로로 갈아 끼웠기 때문입니다.