\begin{tabular}{lcr}의 {lcr}는 LaTeX에서 글이 아니라 프로그램을 쓰는 몇 안 되는 자리입니다. 열의 개수와 정렬 방식뿐 아니라, 각 셀 앞뒤에 선언을 끼워 넣고, 열 사이의 공간을 다른 것으로 바꾸고, 열 묶음을 반복하라고 지시할 수 있습니다. 게다가 이 열 지정 의 어휘는 하나의 설계에서 나온 것이 아니라 기증으로 쌓였습니다. \newcolumntype은 원래 David Carlisle의 별도 패키지 newarray.sty의 기능이었고, array 설명서에는 “David가 흔쾌히 포함에 동의해 주었다”고 적혀 있습니다. 이 페이지에서는 l, c, r, p{width}에서 출발해 m{width}와 b{width}, >{…}와 <{…}, @{…}와 !{…}, *{n}{…}, w와 W, 그리고 표의 너비를 정확히 맞추는 tabularx의 X 열까지 따라갑니다.
어떤 열 지정에 어떤 패키지가 필요한가
순수 LaTeX이 가진 것은 l, c, r, p{width}, |, @{…}, *{n}{…} 뿐입니다. 나머지, 즉 m{width}, b{width}, >{…}, <{…}, !{…}, w와 W, \newcolumntype은 모두 array 패키지 가 더하는 것입니다(TeX Live 2024에는 2023년 10월 16일자 v2.5g, Frank Mittelbach). X 열은 tabularx(2023년 7월 8일자 v2.11c, David Carlisle)에서 오며, tabularx 자체가 array를 필요로 합니다. booktabs와 tabularx가 array를 내부에서 불러오므로 이미 활성화된 경우도 많지만, \usepackage{array}라고 명시해 두는 편이 안전합니다.
자기 문서에서 어느 쪽이 돌고 있는지는 일부러 존재하지 않는 열 지정 글자를 써 보면 한 번에 알 수 있습니다. 순수 LaTeX은 ! LaTeX Error: Illegal character in array arg. 라고 답하고, array를 불러왔다면 불평이 패키지에서 나옵니다. ! Package array Error: Illegal pream-token 입니다. 그러니 m{width}를 썼는데 “Illegal character”라는 말을 들었다면, 그 메시지의 진짜 뜻은 array를 불러오는 것을 잊었다는 것입니다.
단락 열의 세로 위치 — p, m, b, 그리고 폭을 고정하는 w, W
줄바꿈하는 단락 열은 세 종류이며 차이는 셀의 세로 위치 뿐입니다. array 설명서의 표현을 그대로 옮기면 p{width} 는 \parbox[t]{width}와 같아 위쪽 정렬, m{width} 는 내용을 행의 세로 가운데 에 두고, b{width} 는 \parbox[b]와 일치하여 아래쪽 정렬 입니다. 이 차이는 같은 행에 키가 큰 셀(여러 줄로 접힌 p 열이나 큰 이미지)과 한 줄짜리 셀이 섞일 때 드러납니다. 기본 p에서는 옆의 짧은 셀이 행 위쪽에 붙어 키 큰 셀의 중간과 시선이 맞지 않지만, m{width}로 바꾸면 자연스럽게 맞습니다.
폭은 고정하되 줄바꿈은 원하지 않는다면 array의 w{align}{width} 와 W{align}{width} 가 있습니다. 둘 다 사실상 \makebox[width][align]{cell}과 같은 일을 하여, l, c, r처럼 동작하면서 열의 폭을 고정합니다. 차이는 사고가 나는 방식입니다. w는 내용이 폭을 넘어도 말없이 옆으로 겹쳐 찍히고, W는 같은 상황에서 overfull box 경고를 냅니다. 다만 W는 넘칠 때 항상 오른쪽으로 삐져나오므로 정렬이 달라 보인다는 점에 주의하세요. “폭은 고정하되 넘치면 알고 싶다”면 W가 정답입니다.
| 지정자 | 세로 위치 / 너비 | 줄바꿈? | 제공 |
|---|---|---|---|
p{wd} | 위쪽 정렬, \parbox[t]{wd}와 동등 | 예 | 표준 |
m{wd} | 행의 세로 가운데에 놓임 | 예 | array |
b{wd} | 아래쪽 정렬, \parbox[b]{wd}와 일치 | 예 | array |
w{l}{wd} | \makebox[wd][l] 상당. 넘쳐도 조용히 삐져나감 | 아니요 | array |
W{l}{wd} | w와 같지만 넘치면 경고. 넘친 부분은 오른쪽으로 | 아니요 | array |
X | 너비를 계산한 p 열. 표를 목표 너비에 맞춤 | 예 | tabularx |
단락 열을 쓸 때 기억해 둘 작은 기본값이 둘 있습니다. 첫째, p, m, b 안에서는 단락 들여쓰기 \parindent가 0 이 됩니다. 되돌리려면 >{\setlength{\parindent}{1em}}p{5cm}처럼 씁니다. 둘째, array는 \extrarowheight 라는 길이도 더합니다. 양수 값을 주면 각 행의 높이만 늘고 깊이는 그대로입니다. \hline이 대문자 윗부분에 닿아 보일 때 가장 단순한 처방입니다. array 설명서 자신도 문서에 실린 표에 1pt를 설정했다고 밝히고 있습니다.
모든 셀 앞뒤에 끼워 넣기 — >{…}와 <{…}
array 패키지의 핵심은 >{decl} 와 <{decl} 입니다. >{decl}는 그 열의 각 셀 내용 바로 앞에, <{decl}는 바로 뒤에 decl을 삽입합니다. >{\bfseries}l이라고 쓰면 왼쪽 정렬 열의 모든 셀이 셀마다 무언가를 치지 않아도 굵게 나옵니다. >{\itshape}c는 가운데 정렬 기울임, >{\color{red}}r은 오른쪽 정렬 빨간색입니다. 단락 열에는 정렬 선언도 넣을 수 있어서 >{\raggedright}p{4cm}는 “줄바꿈은 하되 양쪽 정렬은 하지 않는” 열을 만듭니다.
다만 중대한 함정 이 있습니다. \raggedright, \raggedleft, \centering 셋은 모두 내부에서 행 끝의 \\를 다시 정의합니다. 그래서 이들을 >{…}에 넣은 열이 표의 마지막 열 이면 \\가 행을 끝내지 못하고, 다음 행의 &가 넘쳐 ! Extra alignment tab has been changed to \cr. 가 납니다. 원인은 열 지정에 있는데 오류는 본문 행을 가리키므로 처음에는 알아채기 어렵습니다. 해결책은 선언의 끝 에 array의 \arraybackslash 를 붙이는 것입니다. >{\raggedright\arraybackslash}p{4cm}라고 쓰면 행 끝 기능이 되살아납니다. 마지막 열이 아니어도 해가 없으니 늘 붙이는 습관이 안전합니다.
\arraybackslash의 출신에는 반전이 있습니다. 원래 이 명령을 도입한 것은 tabularx였고, 바로 이 \raggedright 충돌을 고치기 위해서였습니다. 지금은 array.sty 본체가 \def\arraybackslash{\let\\\tabularnewline}를 담고 있고, tabularx 쪽에는 “아직 정의되지 않았다면 직접 정의한다”는 보험만 남아 있습니다. 즉 도구가 태어난 곳과 지금 사는 곳이 다릅니다.
\usepackage{array}
% ...
% bold labels; a ragged-right wrapping column; a whole column in math mode
\begin{tabular}{ >{\bfseries}l >{\raggedright\arraybackslash}p{5cm} >{$}c<{$} }
\hline
Symbol & Meaning & \multicolumn{1}{c}{Value} \\
\hline
c & Speed of light in vacuum, a defining constant. & 2.998\times10^{8} \\
g & Standard gravitational acceleration near the surface. & 9.807 \\
\hline
\end{tabular}세 번째 열에 쓴 >{$}c<{$}는 >와 <가 처음부터 상정했던 바로 그 용도입니다. 각 셀 앞뒤에 $가 들어가므로 그 열의 모든 셀이 인라인 수식 으로 조판되고, 숫자나 기호가 많은 열에서 매번 $…$를 쓸 필요가 없습니다. 반대로 셀이 이미 수식 모드인 array 환경에서 같은 표기를 쓰면 두 $가 상쇄되어 그 열만 텍스트 모드 가 됩니다. 다만 머리글 행은 주의가 필요합니다. Value를 그대로 쓰면 수식으로 조판되어 자간이 벌어진 기울임체가 됩니다. 위 예처럼 \multicolumn{1}{c}{Value}라고 쓰면 그 셀만 텍스트 모드로 되돌릴 수 있습니다.
>{…}와 <{…}에는 선언(declaration) 만 들어갈 수 있다는 제약도 기억해 두세요. \bfseries나 \itshape처럼 뒤에 인수를 받지 않는 형태는 괜찮지만, 인수를 받는 명령으로 끝내면 안 됩니다. >{\textbf}는 이어지는 셀 내용을 자신의 인수로 읽기 시작해 가장 곤란한 방식으로 실패합니다. 오류도 나지 않고 아무것도 굵어지지 않는 것입니다. array 설명서도 분명히 적고 있습니다. >{\textbf}는 “열 전체를 굵게 하지도, 첫 글자를 굵게 하지도 않는다”. 굵게 하려면 선언형 \bfseries를 씁니다.
열 경계 다루기 — @{…}와 !{…}
열과 열 사이에는 기본적으로 \tabcolsep 만큼의 공간이 들어갑니다. 한쪽 6pt이므로 합쳐서 12pt입니다. 열 지정의 그 자리에 @{decl} 를 두면 array 설명서의 표현대로 “열 사이 공간을 억제하고 대신 decl.을 삽입”합니다. 즉 그 자리의 공간이 사라지고 대신 쓴 것이 들어갑니다. 주된 용도는 둘입니다. 첫째, 표 바깥 여백을 잘라내는 것 입니다. 열 지정 양끝에 @{}(빈 선언)을 두면 첫 열 왼쪽과 마지막 열 오른쪽의 \tabcolsep가 사라져 표가 본문 왼쪽 끝에 딱 맞습니다. booktabs 표에서 자주 보는 관용구입니다. 둘째, 구분 문자를 끼워 넣는 것 입니다. 숫자를 정수부 열과 소수부 열로 나누고 사이에 @{.}를 두면 마침표가 고정 위치에 나란히 서서 소수점 정렬이 됩니다.
@{…}와 짝을 이루는 것이 !{decl} 입니다. 세로줄 |를 쓸 수 있는 자리에 놓이며 | 대신 임의의 decl을 삽입합니다. 결정적 차이는 !{…}가 일반적인 열 사이 공간을 없애지 않는다 는 점입니다. array 설명서의 예가 알기 쉽습니다. c!{\hspace{1cm}}c는 기존 공간에 1cm를 더한 간격이 되는 반면, c@{\hspace{1cm}}c는 정확히 1cm 가 됩니다. 열 사이에 점선을 긋되 여백은 유지하고 싶을 때(예를 들어 arydshln의 점선 명령을 넣을 때)는 !{…}가 정답입니다. 참고로 array를 불러오면 |의 처리도 달라져 선의 두께만큼 열 사이가 넓어집니다. 표준 LaTeX에서는 선이 기존 공간 안에 놓여 폭이 변하지 않습니다.
열을 반복하고 이름 붙이기 — *{n}{…}와 \newcolumntype
같은 지정의 열이 여러 개 이어질 때는 *{count}{spec} 로 접을 수 있습니다. array 설명서의 정의로는 spec을 count번 복사한 것과 같으므로, 가운데 정렬 여섯 개는 cccccc 대신 *{6}{c}가 됩니다. 반복 단위는 여러 지정을 담아도 되어 *{3}{|c}|는 |c|c|c|로 펼쳐집니다. 진가는 >{…}나 @{…}가 들어간 긴 지정을 반복할 때 드러납니다. *{4}{>{$}c<{$}}라고 쓰면 수식 모드 가운데 열 네 개가 한 번에 만들어집니다.
같은 조합을 문서 여기저기에서 쓴다면 \newcolumntype{letter}{…} 로 이름을 붙이는 쪽이 훨씬 읽기 좋습니다. array 설명서 자체가 \newcolumntype{C}{>{$}c<{$}}를 예로 들고 있고, 그 뒤로는 {C C C}라고만 쓰면 됩니다. \newcolumntype은 \newcommand와 같은 선택 인수를 받으므로 너비를 매개변수로 삼는 열 유형도 만들 수 있습니다. 정의가 쌓여 헷갈리기 시작하면 \showcols 를 써 보세요. 현재 유효한 \newcolumntype 정의 목록이 로그에 출력됩니다.
표를 목표 너비에 맞추기 — tabularx의 X 열
여기까지의 열 지정은 폭을 내가 정하는 것이었습니다. 반대로 “표 전체를 \textwidth에 딱 맞추고 싶다”는 경우에 쓰는 것이 tabularx의 X 열 입니다. \begin{tabularx}{\textwidth}{l X r}처럼 목표 너비 를 먼저 넘기면 X라고 쓴 열의 폭이 계산으로 채워집니다. X가 다가가기 쉬운 이유는 구조가 단순하기 때문입니다. 설명서가 분명히 밝히듯 X 지정은 올바른 폭이 계산된 뒤 p{어떤 값}으로 바뀝니다. 소스에서도 기본값은 \newcommand{\tabularxcolumn}[1]{p{#1}} 한 줄입니다. 즉 X는 “폭을 나중에 채우는 p 열”일 뿐이고, 그래서 p 열과 같은 장식이 그대로 듣습니다. >{\raggedright\arraybackslash}X는 기대한 대로 동작합니다. 내용을 세로 가운데에 두고 싶다면 \renewcommand{\tabularxcolumn}[1]{m{#1}}로 바꾸기만 하면 됩니다.
\usepackage{tabularx}
% ...
% two X columns, the second twice as wide as the first
\begin{tabularx}{\textwidth}{@{}
l
>{\hsize=.667\hsize\linewidth=\hsize}X
>{\hsize=1.333\hsize\linewidth=\hsize}X @{}}
\hline
Key & Short note & Longer note \\
\hline
alpha & fits in the narrow column & twice as wide, so prose wraps less often \\
\hline
\end{tabularx}기본적으로 모든 X 열은 같은 폭이 되지만, 위처럼 >{\hsize=…\hsize\linewidth=\hsize}X를 쓰면 가중치를 바꿀 수 있습니다. 다만 설명서는 두 가지 규칙을 둡니다. X 열 폭의 합을 바꾸지 말 것(위 예에서 0.667과 1.333의 합은 2로, 여전히 표준 X 열 두 개분입니다), 그리고 X 열을 가로지르는 \multicolumn을 쓰지 말 것 입니다. 두 번째는 폭 계산이 \multicolumn의 존재를 전제하지 않기 때문입니다. 잘 되지 않으면 \tracingtabularx를 선언해 보세요. 폭을 탐색하는 각 회차의 값이 로그에 출력됩니다.
X와 관련된 대표적인 증상 둘도 알아 두면 좋습니다. X 이외의 열 합계가 이미 목표 너비를 넘으면 tabularx는 X 열에 음수 너비를 주기를 거부하고 Package tabularx Warning: X Columns too narrow (table too wide) 경고를 낸 뒤 그 열들을 1em 폭으로 떨어뜨립니다. 결과적으로 표는 지정한 너비보다 넓어집니다. 또 하나, tabularx의 본문은 실제로는 명령의 인수로 읽히고 너비를 찾는 동안 표가 여러 번 다시 조판되므로, 그 안에 \verb를 쓰면 LaTeX Warning: \verb may be unreliable inside tabularx가 나옵니다. tabularx와 tabulary, tabularray의 비교나 애초에 지면에 들어가지 않는 표의 처리는 “넓은 표와 자동 너비”에서 다룹니다.
여러 기능을 조합한 예, 그리고 숫자 정렬의 현대적 해답
마지막으로 지금까지의 도구를 하나의 열 지정에 담은 예입니다. 1열은 굵은 레이블 열(>{\bfseries}l), 2열은 왼쪽 정렬로 줄바꿈하는 단락 열(>{\raggedright\arraybackslash}p{5cm}), 3・4열은 *{2}{…}로 함께 만든 수식 모드 가운데 열이며, 바깥 여백은 양끝의 @{}로 잘라냈습니다. 한 줄에 여러 장치를 쓸 수 있다는 것이 열 지정 언어의 강점이지만 그만큼 읽기 어려워지므로, 자주 쓰는 조합에는 \newcolumntype으로 이름을 붙이는 것이 실무적입니다.
\usepackage{array}
% ...
\begin{tabular}{@{} >{\bfseries}l >{\raggedright\arraybackslash}p{5cm} *{2}{c} @{}}
\hline
Symbol & Meaning & Value & Unit \\
\hline
$c$ & Speed of light in vacuum, a defining constant. & $2.998\times10^{8}$ & m/s \\
$g$ & Standard gravity, an approximation near the surface. & $9.807$ & m/s$^2$ \\
\hline
\end{tabular}이 열 지정 언어로 하지 않는 편이 나은 일이 하나 있습니다. 숫자를 소수점 기준으로 맞추는 일입니다. 앞에서 스케치한 @{.}가 아니라 siunitx 패키지의 S 열 을 쓰세요. S 열은 각 숫자를 해석해 정수부, 소수점, 소수부, 지수, 심지어 불확실성까지 올바르게 맞추고, 자릿수 구분과 유효 숫자 서식도 함께 정리합니다. 참고로 array 설명서는 자체 소수점 정렬 코드 예를 실은 뒤 “이 코드는 좋지 않다”는 주의를 직접 달고, David Carlisle의 dcolumn.sty에 훨씬 나은 판이 있다고 안내합니다. 패키지 설명서가 자기 예제를 이렇게 정직하게 평하는 일은 드뭅니다.