LaTeX으로 짠 단락이 워드프로세서의 단락보다 차분해 보이는 이유는 무엇일까요. 답은 텍스트 정렬 한 층 아래에 있습니다. 워드프로세서는 한 줄씩 채워 나가지만, LaTeX은 어느 한 줄이 끝나는 자리를 정하기 전에 단락 전체를 저울질합니다. 기본값은 양쪽 맞춤이고, 여기서 벗어나는 길은 두 가지입니다. \centering 같은 선언과 center 같은 환경은 똑같은 정렬을 만들지만 앞뒤에 남기는 세로 여백이 다릅니다. 이 페이지에서는 양쪽 맞춤이 기본인 이유, \centering과 center의 선택 기준, \raggedright이 조용히 하이픈 넣기를 멈추는 까닭과 ragged2e의 해법, 그리고 Overfull \hbox 경고에 답하는 법을 한 줄기로 따라갑니다.
양쪽 맞춤이 기본인 이유: 단락 전체를 보는 줄바꿈
따로 지정하지 않으면 LaTeX은 단락을 양쪽 맞춤으로, 좌우 여백에 나란히 맞춰 조판합니다. 이 기본값은 단순한 관습이 아닙니다. TeX은 줄을 위에서부터 차례로 채우지 않고, 단락 전체의 벌점(demerits)이 가장 적은 줄바꿈 위치의 조합을 고릅니다. 이 알고리즘을 발표한 논문이 도널드 커누스와 마이클 플래스의 「Breaking Paragraphs into Lines」(Software: Practice and Experience 제11권, 1981년, 1119–1184쪽)입니다. 한 줄씩 욕심껏 채우는 방식은 이익을 앞에서 챙기고 그 대가를 마지막 줄에 떠넘깁니다. TeX은 반대로 앞쪽 한 줄을 일부러 조금 나쁘게 짜서 뒤에서 크게 무너지는 일을 막을 수 있습니다. 이 최적화가 움직일 여지는 낱말 사이 공백이 늘고 줄어들 수 있을 때에만 생기는데, 그것이 바로 양쪽 맞춤이 마련해 주는 조건입니다. 그래서 양쪽 맞춤이 출발점입니다.
구조는 구체적입니다. TeX은 각 줄에 대해 공백을 얼마나 늘리거나 줄였는지를 badness라는 값으로 바꾸고, 줄바꿈 자리에 걸린 벌점을 p로 둡니다. l을 \linepenalty, b를 badness라 하면 그 줄의 demerits는 d = (l+b)^2 + p^2입니다. 단락의 demerits는 이 값들의 합에, 이웃한 두 줄의 조임새가 어긋날 때(\adjdemerits)와 하이픈으로 끝나는 줄이 연달아 나올 때(\doublehyphendemerits)의 가산분을 더한 것입니다. 커누스와 플래스는 이 식들을 스스로 "quite arbitrary"라고 적었지만, 실제로는 잘 작동합니다. 하나 더 알아 두면 좋은 것은 줄바꿈이 최대 세 번 시도된다는 점입니다. 첫 번째는 하이픈을 전혀 쓰지 않고 짜 보며, 모든 줄이 \pretolerance 안에 들어와야 받아들입니다. 두 번째에 이르러서야 하이픈 패턴과 더 느슨한 \tolerance가 동원됩니다. 즉 여유 있게 들어맞는 단락은 애초에 하이픈으로 잘리지 않습니다. 그래도 실패할 때만 \emergencystretch를 쓰는 세 번째가 돌아갑니다.
\centering과 center 환경의 차이
결론부터 말하면 정렬 결과는 완전히 같고, 다른 것은 앞뒤에 들어가는 세로 여백뿐입니다. latex.ltx를 열어 보면 center는 \trivlist \centering\item\relax로 정의되어 있습니다. 즉 이 환경은 \centering에 보이지 않는 목록 껍데기를 씌운 것이고, 목록인 이상 본문과의 사이에 \topsep만큼의 간격이 자동으로 들어갑니다. flushleft는 같은 방식으로 \raggedright를, flushright는 \raggedleft를 감쌉니다. 10pt article에서 재어 보면 center 환경은 위아래로 약 8pt를 더하고, 선언은 아무것도 더하지 않습니다.
| 선언 | 환경 | 맞춰지는 쪽 | 앞뒤 세로 여백 |
|---|---|---|---|
\centering | center | 양쪽 다 맞지 않고 줄이 가운데에 놓임 | 선언은 0, 환경은 \topsep |
\raggedright | flushleft | 왼쪽이 맞고 오른쪽이 들쭉날쭉 | 위와 같음 |
\raggedleft | flushright | 오른쪽이 맞고 왼쪽이 들쭉날쭉 | 위와 같음 |
\justifying | justify | 양쪽 모두 맞음(양쪽 맞춤) | ragged2e 필요, 선언은 0 |
\begin{figure}[htbp]
\centering % declaration: adds no vertical space
\includegraphics[width=0.6\textwidth]{plot}
\caption{Measured values against the model}
\label{fig:plot}
\end{figure}figure 안에서 \begin{center}가 아니라 \centering을 쓰는 이유가 여기에 있습니다. 플로트는 이미 위아래에 자체 간격을 갖고 있어서, 여기에 center 환경의 \topsep이 겹치면 그림과 캡션 주위가 늘어져 보입니다. 같은 이유로 tabular의 셀, \parbox, minipage, \caption의 내용도 선언으로 정렬합니다. 반대로 본문 흐름 속에 한 줄짜리 표어를 넣으면서 앞뒤 여백까지 함께 원할 때는 center 환경이 더 간단합니다.
선언에는 사고를 부르는 성질이 하나 더 있습니다. 선언은 새 단락을 시작하지 않습니다. 정렬은 단락이 닫히는 순간, 곧 \par(빈 줄)를 읽는 시점에 확정되며, 그때 유효했던 값이 단락 전체에 적용됩니다. 그래서 다음 한 줄은 대부분의 직관과 어긋나는 결과를 냅니다.
ww {\centering xx \\ yy} zz
% result: the line "ww xx" comes out centred, "yy zz" does not실제로 조판해 보면 ww xx는 가운데로 모이고 yy zz는 왼쪽 여백에서 시작합니다. 이유는 두 겹입니다. 먼저 \centering은 \\의 뜻을 그 자리에서 \@centercr로 바꿔 씁니다. \@centercr는 줄바꿈이 아니라 \par, 곧 단락의 끝입니다. 그래서 xx는 \centering이 살아 있는 동안 단락을 닫고, 그 단락이 가운데로 모입니다. 이어지는 yy는 새 단락을 열지만, }로 그룹이 닫히면서 \rightskip 등이 원래대로 돌아가고, 그 단락이 끝날 때는 이미 양쪽 맞춤 상태입니다. 선언을 쓸 때의 유일한 규칙이 여기서 나옵니다. 정렬하려는 단락의 끝——빈 줄, 또는 환경의 \end——이 그룹 안에 들어와야 합니다.
\raggedright이 하이픈 넣기를 멈추는 이유와 ragged2e
원인은 코드 한 줄입니다. \raggedright은 줄 끝을 채우는 글루 \rightskip을 0pt plus 1fil, 곧 무한히 늘어나는 공백으로 설정합니다. 줄 끝마다 무한히 늘어나는 것이 놓여 있으면 어떤 줄이든 badness는 0이 됩니다. 그러면 TeX으로서는 단어를 잘라 하이픈을 넣는 일이 demerits만 늘릴 뿐 아무 이득이 없는 선택이 됩니다. 그래서 하이픈을 아예 쓰지 않게 되고, 오른쪽 끝은 심하게 들쭉날쭉해집니다. 같은 문장을 4cm 폭으로 실제 조판해 보면 \raggedright은 낱말 하나가 한 줄에 도저히 들어가지 않는 자리를 빼면 하이픈을 하나도 넣지 않았고, 줄 길이 편차가 컸습니다.
ragged2e 패키지가 고치는 것이 바로 이 지점입니다. 무한한 늘어남을 유한한 0pt plus 2em으로 바꾸기만 하면 너무 짧은 줄에 다시 실질적인 badness가 붙고, TeX은 하이픈 넣기를 다시 저울질하기 시작합니다. 이 패키지는 첫 글자를 대문자로 쓴 명령 \RaggedRight, \RaggedLeft, \Centering과 환경 FlushLeft, FlushRight, Center, 그리고 양쪽 맞춤으로 되돌리는 \justifying과 justify 환경을 제공합니다. 하이픈을 유지한 채 문서 전체를 왼쪽 맞춤으로 하려면 \usepackage[document]{ragged2e}를 씁니다. \begin{document} 시점에 \RaggedRight이 실행되고, 같은 설정이 \parbox와 tabular의 p 열에도 적용됩니다. 이 패키지는 Martin Schröder가 만들었고 현재는 Marei Peischl이 관리합니다.
\usepackage[document]{ragged2e} % whole document flush left, hyphenation kept
% or locally, only where it is needed:
\begin{FlushLeft}
A paragraph set flush left with hyphenation still available,
so the right edge stays gently ragged instead of jagged.
\end{FlushLeft}
\justifying % back to justified text그렇다면 왼쪽 맞춤이 정말로 옳은 선택인 때는 언제일까요. 기준은 한 줄의 길이입니다. 다단 조판, 방주, tabular의 좁은 p 열처럼 줄이 짧아지는 자리에서 양쪽 맞춤을 밀어붙이면 낱말 사이 공백을 크게 늘릴 수밖에 없습니다. 늘어난 공백은 줄에서 줄로 이어지며 지면에 허연 물길——조판에서 강(rivers)이라고 부르는 것——을 냅니다. 읽기에 도움이 되기는커녕 방해가 됩니다. 줄이 짧을수록 위험이 크므로, 좁은 단은 실제로 왼쪽 맞춤이 더 잘 읽힙니다. 2단 논문에서 본문은 양쪽 맞춤으로 두고 캡션과 각주에만 \RaggedRight을 적용하는 것이 흔한 절충안입니다. 반대로 1단이고 줄이 넉넉하다면 양쪽 맞춤을 그대로 두는 편이 현명합니다.
Overfull \hbox 경고를 없애는 법: \sloppy와 \emergencystretch
로그에 Overfull \hbox (84.8932pt too wide) in paragraph at lines 10--10 같은 줄이 보이면, 그것은 그 단락을 허용 범위 안에 담을 줄바꿈 조합을 끝내 찾지 못했다는 보고입니다. TeX은 포기하고 한 줄이 판면 밖으로 튀어나오도록 둡니다. 범인은 대개 길면서 쪼갤 수 없는 것——URL, 이어 붙인 식별자, \texttt 안의 긴 낱말, 또는 그냥 좁은 단——입니다. 정렬 방식을 의심하기 전에 그 낱말부터 의심해야 합니다.
대처에는 순서가 있습니다. 먼저 그 한 낱말을 손봅니다. \hyphenation{...}으로 나눌 자리를 알려 주거나, \-를 넣거나, URL이라면 url 또는 hyperref의 \url에 맡기는 것이 근본 해결입니다. 그래도 경고가 남으면 \sloppy를 씁니다. \sloppy는 \tolerance를 9999로, \emergencystretch를 3em으로, \hfuzz를 0.5pt로 완화합니다. 곧 낱말 사이가 벌어지는 것을 감수하는 대신 넘침을 없애는 거래입니다. 문서 전체에 걸면 본문이 전반적으로 헐거워 보이므로, 문제가 된 단락만 sloppypar 환경으로 감싸거나 \emergencystretch=3em만 지정하는 편이 온건합니다. 엄격한 값으로 되돌리는 것은 \fussy(\tolerance 200, \emergencystretch 0)입니다. 그리고 좁은 단에서 이 경고가 되풀이된다면, 그 단은 왼쪽 맞춤으로 바꾸라는 신호이기도 합니다.
% 1. tell LaTeX where the offending word may be split
\hyphenation{Bib-lio-the-ken-ver-bund}
% 2. relax the line breaker for one paragraph only
\begin{sloppypar}
A paragraph containing a very long unbreakable token.
\end{sloppypar}
% 3. or globally, without going all the way to \sloppy
\emergencystretch=3em단락을 끝낼 때 \\를 쓰면 안 되는 이유
\\는 줄을 꺾을 뿐 단락을 끝내지 않습니다. 단락을 끝내는 것은 빈 줄, 곧 \par입니다. 이 구별은 정렬과 곧바로 이어집니다. 앞 절들에서 보았듯 정렬은 단락이 닫히는 순간에 결정되기 때문입니다. \\로 두 덩어리의 글을 나누면 뒤쪽은 새 단락으로 취급되지 않아 들여쓰기도 \parskip도 붙지 않습니다. 한 단락 가운데에서 줄만 꺾인 것입니다. 더 나쁜 경우로, 단락 끝에 \\를 두고 빈 줄을 이으면 로그가 Underfull \hbox (badness 10000) in paragraph at lines 3--4로 채워집니다. 빈 줄이 하나 더 생기고 그것이 판면 폭까지 늘어나기 때문입니다.
그렇다면 \\는 어디에 쓰는가. 줄만 꺾고 싶은 자리입니다. center나 flushright 안에서 줄을 나눌 때, tabular에서 한 행을 끝낼 때, verse에서 시행을 가를 때, 긴 \caption을 접을 때——모두 줄의 경계이지 단락의 경계가 아닙니다. 덧붙이면, center 블록의 마지막 줄 뒤에 \\를 하나 더 써도 손해가 없습니다. 여백도 늘지 않고 경고도 나지 않는데, \@centercr가 \addvspace{-\parskip}로 상쇄하기 때문입니다. 단락은 빈 줄로, 줄바꿈은 \\로——이 한 줄의 경계만 지켜도 영문 모를 정렬 사고가 상당 부분 사라집니다.
어느 정렬을 어디에 쓸 것인가
판단은 취향이 아니라 대상이 무엇인가에서 시작합니다. 본문 단락은 양쪽 맞춤 그대로 두고, 짧은 부품——제목, 캡션, 서명 줄, 표의 셀——만 그 자리에서 정렬합니다. 그리고 선언과 환경 중 무엇을 쓸지는 오직 한 가지 물음으로 갈립니다. 앞뒤의 세로 여백이 필요한가, 아닌가. 이 순서로 정하면 같은 물음으로 두 번 고민할 일이 거의 없습니다.
- 그림, 표의 셀,
\parbox,minipage의 내용은\centering같은 선언으로 정렬합니다. 환경을 쓰면 이미 있는 여백 위에\topsep이 겹쳐 쌓입니다. - 본문 흐름에 놓는 한 줄짜리 표어나 제자는
center환경이 자연스럽습니다. 앞뒤 여백까지가 원하는 결과이기 때문입니다. - 서양 문자의 왼쪽·오른쪽 맞춤에는 표준 명령 대신
ragged2e의\RaggedRight,\RaggedLeft를 써서 하이픈 넣기를 살려 둡니다. - 단이 좁을 때(다단 조판, 방주,
tabular의 p 열)는 양쪽 맞춤을 고집하지 않습니다. 줄이 짧을수록 왼쪽 맞춤이 유리합니다. - 선언을 쓸 때는 단락의 끝을 그룹 안에 둡니다. 그룹 안에 빈 줄도
\end도 없으면 정렬이 먹지 않거나, 건드릴 생각이 없던 글까지 번집니다. - 단락은 빈 줄로, 줄은
\\로 끝냅니다. 둘을 뒤섞으면Underfull \hbox과 사라진 들여쓰기가 함께 찾아옵니다.