itemize

LaTeX의 itemize이 줄 머리에 찍는 검은 점은 예전에 기울임꼴 문장 안에 들어가면 함께 기울어지곤 했습니다. 기호가 주변의 글꼴 지정을 그대로 물려받았기 때문입니다. 2020-02-02 릴리스에서 \labelitemfont이라는 명령이 들어오면서 이 버릇은 비로소 멈췄습니다. 즉 글머리 기호 목록의 점은 장식이 아니라 다른 글자와 똑같이 조판되는 문자입니다. 이 페이지에서는 itemize을 세 방향에서 살펴봅니다. 그 기호들이 어디에 정의되어 있는지, \item[...]으로 하나만 바꾸면 무슨 일이 벌어지는지, 그리고 항목 사이의 간격을 정하는 두 개의 길이는 무엇인지입니다.

검은 점은 어디에 정의되어 있는가: \labelitemi부터 \labelitemiv까지

행 머리 기호는 itemize 환경이 아니라 문서 클래스에 적혀 있습니다. article.cls를 열면 \labelitemi부터 \labelitemiv까지 네 개의 명령이 나란히 있고, 차례대로 \textbullet(•), 굵은 \textendash(–), \textasteriskcentered(*), \textperiodcentered(·)를 담고 있습니다. itemize이 하는 일이라고는 지금 몇 단계에 있는지 세어 \labelitem + 로마 숫자라는 이름을 조립하고 그 이름이 담고 있는 것을 부르는 것뿐입니다. 그래서 기호를 바꾸려고 환경에 손댈 필요는 없고 명령을 갈아 끼우면 됩니다. 네 번째 뒤에 아무것도 없는 이유도 마찬가지로 단순합니다. 다섯 번째 이름이 애초에 정의되지 않았기 때문입니다.

명령단계기호article.cls의 내용
\labelitemi1\labelitemfont \textbullet
\labelitemii2\labelitemfont \bfseries \textendash(굵게)
\labelitemiii3*\labelitemfont \textasteriskcentered
\labelitemiv4·\labelitemfont \textperiodcentered
\labelitemfont\normalfont. 기호를 주변 서체에서 떼어 놓습니다

표 마지막 줄의 \labelitemfont이 서두에서 언급한 2020년의 수정입니다. 네 기호 모두 이것을 앞에 붙여 호출되므로, 이제는 기울임꼴이나 굵은 글씨 블록 안에 목록을 넣어도 기호만은 곧게 유지됩니다. 반대로 이를 활용해 기호만 다른 서체로 조판할 수도 있습니다. \renewcommand{\labelitemfont}{\normalfont\sffamily}라고 쓰면 본문은 세리프 그대로인 채 행 머리 기호만 산세리프가 됩니다. 한 단계 전체의 기호를 갈아 끼우려면 해당하는 \labelitem…\renewcommand으로 덮어씁니다. 전처리부에 두면 문서 전체에, 환경이나 그룹 안에 두면 그 안에서만 적용됩니다.

latex
% swap the first-level marker for the whole document
\renewcommand{\labelitemi}{$\diamond$}

\begin{itemize}
  \item this level now uses a diamond
  \item so does this one
\end{itemize}

\item[...]으로 한 항목만 기호를 바꾸기

\item에 대괄호로 인수를 주면 그 항목만 행 머리 기호가 대괄호 안의 내용으로 바뀝니다. \item[$\star$]이면 별표, \item[--]이면 대시, \item[Note]이면 “Note”라는 낱말이 기호 자리에 그대로 조판됩니다. 여기서 널리 퍼진 오해 하나를 바로잡아 두겠습니다. itemize의 대괄호 라벨은 굵게 조판되지 않습니다. 굵어지는 것은 description 환경이며, 그쪽의 \descriptionlabel\bfseries를 포함합니다. itemize의 라벨 정렬은 그저 \hss\llap{…}일 뿐, 오른쪽 정렬만 할 뿐 서체는 건드리지 않습니다.

latex
\begin{itemize}
  \item the default marker
  \item[$\star$] a star for this one entry
  \item[Note] a whole word in the marker slot
\end{itemize}

\llap은 함정도 만듭니다. 라벨은 \labelwidth 너비의 상자에 오른쪽 정렬로 놓이는데, \llap넘쳐도 아무 말이 없습니다. \item[Note] 정도는 넉넉히 들어가지만 \item[Prerequisite]처럼 긴 낱말을 넣으면 라벨이 왼쪽으로 죽 늘어나 본문 영역을 넘고, 심하면 판면 밖까지 나갑니다. 행간이 무너지지도 않고 Overfull \hbox 경고도 나오지 않으므로 PDF를 볼 때까지 알아챌 수 없습니다. 낱말을 라벨로 쓰고 싶다면 그것은 이미 itemize이 아니라 description의 일입니다. enumitem을 쓴다면 labelwidth=leftmargin=으로 너비를 확보할 수 있습니다.

항목 사이가 너무 벌어질 때: \itemsep과 \parsep

항목과 항목 사이의 간격은 하나의 길이가 아니라 둘의 합입니다. \item은 다음 항목을 시작하기 전에 \itemsep을 넣고, 그 직후 새 단락이 시작되면서 \parskip만큼이 또 들어갑니다. 그리고 목록 안에서 \parskip\parsep으로 설정되어 있습니다. 즉 실제 간격은 \itemsep + \parsep입니다. 10pt article이라면 1단계에서 둘 다 4pt, 합해서 8pt입니다. 행간이 12pt이니 항목 사이에 3분의 2행만큼의 공백이 들어가는 셈입니다. 목록이 “늘어져 보인다”고 할 때 보고 있는 것이 대개 이것이고, \itemsep만 0으로 만들어도 절반밖에 좁혀지지 않는 이유도 여기에 있습니다.

길이1단계 기본값(10pt article)어디에 작용하는가
\itemsep4pt plus 2pt minus 1pt\item이 다음 항목 앞에 넣는 공백
\parsep4pt plus 2pt minus 1pt한 항목 안의 단락 사이. 목록 안에서는 \parskip이 되므로 항목 사이에도 작용합니다
\topsep8pt plus 2pt minus 4pt목록 위아래에 들어가는 공백
\partopsep2pt plus 1pt minus 1pt바로 앞에 빈 줄이 있을 때만 \topsep에 더해집니다

표의 마지막 줄은 누구나 한 번쯤 걸려 넘어지는 동작입니다. \begin{itemize} 바로 앞에 빈 줄을 두느냐 마느냐에 따라 목록 위의 공백이 8pt와 10pt로 달라집니다. 빈 줄이 있으면 목록이 수직 모드에서 시작하고, 바로 그때 \partopsep이 더해지기 때문입니다. 원고를 다시 정리했을 뿐인데 판면이 움직였다면 대개 이것이 원인입니다. 값 자체에도 장치가 있어서, plusminus가 붙은 고무 길이이므로 페이지를 짤 때 6pt까지 줄고 12pt까지 늘어납니다. 적어 놓은 수치가 그대로 종이에 나오지는 않습니다. 또한 단계가 깊어지면 기본값이 자동으로 좁아져서, 2단계에서는 \itemsep\parsep이 모두 2pt, 3단계에서는 \parsep이 0pt가 됩니다. 중첩된 목록이 더 조밀해 보이는 것은 그렇게 설계했기 때문입니다.

latex
% tighten one list by hand: inside the body it is \parskip,
% not \parsep, that still carries the second half of the gap
\begin{itemize}
  \setlength{\itemsep}{0pt}
  \setlength{\parskip}{0pt}
  \item first
  \item second
  \item third
\end{itemize}

여기서 0으로 만드는 것이 \parsep이 아니라 \parskip이라는 점에 주의하세요. \parsep에서 \parskip으로의 복사는 \begin{itemize}이 끝나는 시점에 이미 이루어지므로, 본문에 들어와서 \parsep을 바꿔도 뒤쪽 4pt는 그대로 남습니다. 실제로 재어 보면 기본값에서 항목 사이가 19.93pt인데, \parsep만 0으로 해서는 15.94pt까지밖에 줄지 않고, \parskip을 0으로 해야 비로소 11.96pt——딱 한 행——이 됩니다. 매번 이렇게 되풀이하느니 enumitem을 불러 [noitemsep](\itemsep\parsep을 0으로)이나 [nosep](거기에 \topsep\partopsep까지 0으로)을 건네는 편이 빠르고, \setlist로 문서 전체의 기본값으로 삼을 수도 있습니다. 이와 맞물린 또 다른 하소연은 목록이 엉뚱한 곳에서 쪽을 넘는다는 것입니다. 이는 사고가 아니라 설계입니다. article 클래스는 \@itempenalty−51로 설정합니다. 음수 페널티는 “여기서 쪽을 넘겨도 된다”가 아니라 “여기서 쪽을 넘기고 싶다”는 뜻이므로, LaTeX은 두 항목 사이의 이음매를 적극적으로 좋은 쪽 끝으로 취급합니다. 갈라지면 안 되는 목록은 명시적으로 붙들어야 합니다. minipage에 넣거나 \samepage로 묶습니다.

\item과 관련된 두 가지 오류

itemize은 비워 둘 수 없습니다. \item을 하나도 쓰지 않고 \begin{itemize}\end{itemize}을 닫으면 ! LaTeX Error: Something's wrong--perhaps a missing \item.으로 멈춥니다. 실제로는 “\item을 빠뜨렸다”보다 목록 맨 앞에 본문이나 그림을 그대로 써 넣었을 때 더 자주 나오는 오류입니다. 환경을 연 바로 다음 자리는 예약석이라고 생각하세요. 거기에는 \item이나 간격・길이 설정만 놓을 수 있습니다. 반대 방향의 실수도 있습니다. 목록 밖에서 \item을 쓰면 ! LaTeX Error: Lonely \item--perhaps a missing list environment.이 나옵니다. 보통 \end{itemize}을 하나 더 썼거나, \item이 든 블록을 복사하면서 \begin을 두고 왔을 때입니다. 둘 다 메시지가 원인을 그대로 말해 주므로, 행 번호를 따라 앞뒤 \begin\end의 짝만 확인하면 끝납니다.

글머리 기호를 써야 할 때와 쓰지 말아야 할 때

itemize은 접속사를 걷어낸 산문이 아니라, 항목들을 나란히 놓고 비교하게 하는 도구입니다. 번호가 없다는 것 자체가 독자에게 “이 넷 사이에는 순서도 우열도 없다”고 선언하는 셈입니다. 그러니 크기도 문체도 길이도 제각각인 항목을 늘어놓으면 독자는 있지도 않은 비교의 축을 찾아 헤매게 됩니다. 반대로 순서나 우선순위가 의미를 갖기 시작하는 순간 목록은 enumerate으로 옮겨 가야 하며, 순서를 은근히 담은 채 늘어선 점들이 가장 읽기 어렵습니다. 아래는 자기 원고를 다시 읽을 때 쓰는 점검 목록입니다.

  • 문법을 나란히 맞춥니다. 첫 항목이 명사구라면 모두 명사구로, 명령형이라면 모두 명령형으로 씁니다.
  • 설명이 길어지면 단락으로 돌아갑니다. 항목마다 세 문장을 넘어간다면 그것은 목록이 아니라 소제목이 붙은 본문입니다.
  • 중첩은 2단계까지. LaTeX은 4단계까지 허용하지만 독자가 따라올 수 있는 것은 대개 2단계까지입니다.
  • 순서가 의미를 가지면 enumerate으로. “먼저”, “다음으로”라고 쓰고 싶어지면 그것이 번호 목록의 신호입니다.
  • 기호를 바꾸는 것은 뜻이 있을 때만. \item[$\star$]은 한 항목을 도드라지게 하는 도구이지 장식이 아닙니다.