중첩 목록

목록을 중첩하면 LaTeX은 단계에 따라 행 머리의 라벨을 바꿉니다. 그런데 itemize 항목 안에 놓은 enumerate은 지면에서는 2단계로 보이는데도 “(a)”가 아니라 “1.”부터 시작합니다. LaTeX이 세는 것은 종이 위의 깊이가 아니라 같은 종류의 목록이 몇 겹으로 감싸고 있는가이기 때문입니다. 이 한 가지를 모른 채 중첩 목록을 쓰면 출력을 보고 고개를 갸웃하게 됩니다. 이 페이지에서는 라벨이 어떻게 선택되는지, Too deeply nested이라는 벽이 왜 네 번째 단계에 서 있는지, 모든 목록 환경이 함께 쓰는 또 하나의 상한, 그리고 깊어질수록 확실히 잃게 되는 행의 너비를 다룹니다.

라벨을 정하는 것은 지면의 깊이가 아니라 같은 종류가 몇 겹인가

LaTeX은 깊이를 두 개의 카운터로 따로따로 셉니다. latex.ltx에서 itemize\@itemdepth을, enumerate\@enumdepth을 올리고, 각자 자기 값으로 \labelitem 또는 \labelenum에 로마 숫자를 붙인 명령 이름을 만들어 부릅니다. 둘은 서로 참견하지 않으므로 itemize 안의 enumerate\@enumdepth으로는 1단계입니다. 그래서 \labelenumi이 쓰이고 번호는 “1.”부터 시작합니다. 흔히 “라벨은 종류와 무관하게 중첩 깊이로 정해진다”고 설명하지만 그것은 틀렸습니다. 한 번 조판해 보면 바로 드러납니다.

latex
\begin{enumerate}
  \item Make the dough
  \begin{itemize}
    \item Flour
    \item Water
    \begin{enumerate}
      \item Add half
      \item Add the rest
    \end{enumerate}
  \end{itemize}
  \item Let it rest
\end{enumerate}

이 세 겹의 중첩이 실제로 어떻게 나오는가 하면, 바깥 enumerate은 “1.”, “2.”를 내고, 그 안의 itemize은 2단계 대시 –가 아니라 1단계 검은 점 •을, 가장 안쪽 enumerate은 3단계 로마 숫자 i.가 아니라 2단계 (a)를 받습니다. itemize 입장에서는 이것이 처음 만나는 itemize이고, 안쪽 enumerate 입장에서는 바깥에 enumerate이 하나 있을 뿐이라고 세기 때문입니다. 종류를 번갈아 겹치면 각 종류가 자기 라벨 차례를 따로따로 써 나가게 됩니다.

깊이환경그 종류에서의 단계나오는 라벨
1itemize1• (\labelitemi)
2enumerate11. (\labelenumi)
3itemize2– (\labelitemii)
4enumerate2(a) (\labelenumii)
5itemize3* (\labelitemiii)
6enumerate3i. (\labelenumiii)

네 번째 단계의 벽: ! LaTeX Error: Too deeply nested.

같은 종류의 목록을 다섯 겹으로 만들면 컴파일은 ! LaTeX Error: Too deeply nested.으로 멈춥니다. 이 메시지는 깊이의 숫자를 알려 주지 않습니다. 이 세 단어뿐이므로 어느 \begin이 다섯 번째인지는 직접 세어야 하고, 보고되는 행 번호가 다섯 번째를 연 \begin{itemize}의 행입니다. 이유는 조판상의 금지가 아니라 그저 이름이 모자라기 때문입니다. latex.ltx의 판정은 \ifnum \@itemdepth >\thr@@, 곧 “3보다 큰가”일 뿐입니다. 라벨 명령이 네 단계까지만 정의되어 있어 다섯 번째에 건넬 이름이 없으니 멈추는 것입니다. 그러니 enumitem을 불러오기만 해서는 이 벽이 사라지지 않습니다. 설정을 하나도 더하지 않은 채 다섯 번째를 열면 똑같은 오류가 똑같이 나옵니다.

latex
% raising the ceiling really does take all four lines
\usepackage{enumitem}
\setlistdepth{9}
\renewlist{itemize}{itemize}{9}
\setlist[itemize]{label=$\cdot$}
\setlist[itemize,1]{label=$\bullet$}

벽을 정말로 걷어 내려면 enumitem\setlistdepth 하나로는 부족합니다. \setlistdepth{9}은 목록 전체의 상한을 올릴 뿐이고 itemize 자체는 여전히 네 단계에서 멈춥니다. \renewlist{itemize}{itemize}{9}으로 환경을 아홉 단계용으로 다시 만들고, 그다음 \setlist[itemize]{label=…}으로 새 단계의 라벨을 주어야 비로소 다섯 번째 단계가 조판됩니다. 라벨을 주지 않고 다섯 번째를 열면 오류가 ! Package enumitem Error: Undefined label.으로 바뀔 뿐입니다. 숫자가 붙은 깊은 계층이 필요할 뿐이라면, 처음부터 임의의 깊이를 전제로 설계된 easylist 같은 패키지가 더 정직한 도구입니다. 다만 손대기 전에 한 번 멈춰 서세요. 다섯 겹의 중첩을 따라 읽을 수 있는 독자는 거의 없습니다. 그만큼 깊은 중첩은 대개 그 구조를 절 제목으로 바꿔야 한다는 신호입니다.

또 하나의 상한: 목록 환경이 함께 쓰는 6단계의 예산

종류별 4단계 위에는 모든 목록이 함께 쓰는 6단계의 상한이 한 겹 더 있습니다. itemizeenumerate도 내부적으로는 같은 list 환경 위에 만들어져 있고, 그 list\ifnum \@listdepth >5라는 판정을 갖고 있기 때문입니다. itemizeenumerate을 번갈아 겹치면 6단계까지는 통과하지만 7단계에서는 역시 Too deeply nested이 됩니다. 성가신 점은 이 예산을 쓰는 것이 목록만이 아니라는 것입니다. article.cls를 보면 quote, quotation, verse, description, 심지어 thebibliography까지 모두 \list 위에 만들어져 있습니다. 깊은 글머리 기호 목록 안에 인용 블록을 두 겹으로 넣으면 그것만으로 남은 예산을 다 써 버립니다. 원인이 “인용”처럼 보이지 않기 때문에 이 형태의 사고가 가장 찾아내기 어렵습니다.

들여쓰기는 더해진다: 네 번째 단계에서 행폭의 4분의 1이 사라집니다

중첩의 진짜 비용은 라벨이 아니라 행의 너비에 나타납니다. list 환경은 열릴 때마다 자기 \leftmargin을 왼쪽 총합에 더하므로 들여쓰기가 단계마다 쌓입니다. 1단 조판의 10pt article이라면 \leftmargini이 25.0pt, \leftmarginii이 22.0pt, \leftmarginiii이 18.7pt, \leftmarginiv이 17.0pt입니다. 네 번째 단계에 이르면 왼쪽 여백의 합이 82.7pt로, 본문 너비 345pt에 대해 약 4분의 1이 사라지고 남는 행폭은 262.3pt가 됩니다. 값이 단계마다 조금씩 작아지도록 설정된 것은 이 누적을 늦추기 위한 배려이지만, 그래도 네 단계면 이만큼이 깎입니다.

길이누계남는 행폭(textwidth 345pt)
\leftmargini25.0pt(2.5em)25.0pt320.0pt
\leftmarginii22.0pt(2.2em)47.0pt298.0pt
\leftmarginiii18.7pt(1.87em)65.7pt279.3pt
\leftmarginiv17.0pt(1.7em)82.7pt262.3pt

숫자로 놓고 보면 깊은 중첩이 읽기 어려운 이유가 구체적으로 드러납니다. 262pt의 너비는 2단 조판의 한 단에 가깝고, 거기에 긴 문장을 흘려 넣으면 줄바꿈이 움직일 여지가 줄어 단어 사이가 부자연스럽게 벌어지고 Overfull \hbox 경고도 늘어납니다. 게다가 2단 조판에서는 애초에 \leftmargini이 2em으로 줄어 있어서, 판면이 좁을수록 중첩의 여유는 사라집니다. 왼쪽 여백을 좁히고 싶다면 enumitemleftmargin=을 단계별로 지정하는 것이 실용적입니다. 다만 거기까지 손대기 전에 구조를 다시 보는 편이 빠른 경우가 더 많습니다. 3단계보다 깊은 항목은 대개 부모 항목에 산문으로 접어 넣을 수 있습니다.

중첩을 어디에서 멈출 것인가

LaTeX은 4단계까지 허용하지만, 그것이 “4단계까지 읽을 수 있다”는 뜻은 아닙니다. 깊은 중첩이 전달하는 것은 계층 구조 자체이고, 독자는 그 구조를 행 머리 기호의 모양과 왼쪽 끝의 위치만으로 복원해야 합니다. 3단계의 별표와 4단계의 가운데 점은 모양이 비슷하고, 둘의 들여쓰기 차이는 겨우 17pt입니다. 즉 4단계까지 오면 독자가 구조를 가려낼 단서가 거의 남지 않습니다. 아래는 중첩을 다 쓴 뒤에 다시 볼 때 쓰는 점검 목록입니다.

  • 2단계로 충분한지 먼저 확인합니다. 3단계 이상은 대개 부모 항목에 산문으로 접어 넣을 수 있습니다.
  • 그 깊이가 정말 필요하다면 목록이 아니라 절 제목으로 바꿉니다. \subsection\subsubsection에는 목차와 상호 참조가 따라옵니다.
  • 종류를 섞었다면 반드시 출력을 눈으로 확인합니다. 라벨은 같은 종류가 몇 겹 감싸는지로 정해지므로 예상과 다른 기호가 나올 수 있습니다.
  • quoteverse를 깊은 중첩 안에서 쓰지 않습니다. 같은 6단계 예산을 소비하며, 원인을 알아채기 어려운 오류를 부릅니다.
  • Too deeply nested이 나오면 깊이를 다시 셉니다. 메시지는 단계를 알려 주지 않으므로 보고된 행부터 \begin을 거슬러 올라갑니다.