그대로 출력(verbatim 계열)

LaTeX에서 적분 기호를 찍는 것보다 백슬래시 한 글자를 찍는 것이 더 어렵습니다. 농담이 아닙니다. 입력한 대로 인쇄하는 verbatim, 즉 조각용 \verb와 블록용 verbatim 환경은 LaTeX가 자기 문법을 잠시 꺼야만 성립하는 시스템 유일의 영역이기 때문입니다. 언뜻 부당해 보이는 규칙은 모두 여기서 나옵니다. \verb가 줄바꿈을 넘지 못하는 것도, \end {verbatim}처럼 공백을 넣으면 조판이 멈추는 것도, \section{...}이나 \footnote{...} 안에서 쓰면 verbatim을 전혀 언급하지 않는 오류가 나는 것도 원인은 하나입니다. 이 페이지에서는 verbatim 환경과 \verb, 공백을 보이게 하는 별표 형태, 파일 전체를 읽는 \verbatiminput\VerbatimInput, 명령 일부를 살려 두는 alltt, 행 번호와 테두리를 붙이는 fancyvrb, 그리고 본문에서 백슬래시나 밑줄을 입력하는 방법까지 다룹니다.

verbatim 환경이 실제로 끄는 것

\begin{verbatim}\end{verbatim} 사이에 쓴 것은 줄바꿈과 공백까지 포함해 입력한 그대로 등폭 글꼴로 인쇄되며, 추가 패키지가 필요 없습니다. 원리는 이스케이프가 아니라 강등 입니다. 환경에 들어갈 때 LaTeX는 \, {, }, $, &, #, ^, _, %, ~의 범주 코드(category code)를 한꺼번에 "평범한 문자"로 바꿔 씁니다. 범주 코드란 TeX가 입력을 읽으며 문자마다 붙이는 역할 표지로, 그 문자가 명령을 시작하는지, 그룹을 여는지, 그저 잉크인지를 결정합니다. verbatim 안의 백슬래시가 명령을 시작하지 않는 것은 명령을 무시해서가 아니라, 그 순간 백슬래시가 백슬래시처럼 생긴 평범한 기호일 뿐이기 때문 입니다.

latex
\begin{verbatim}
for i in range(3):
    print("100% & $5 \n")   # none of this is interpreted
\end{verbatim}

이것을 구현하는 일은 만드는 쪽에도 성가십니다. 그 성가심은 LaTeX 자신의 소스에 그대로 남아 있습니다. LaTeX 본체를 정의하는 latex.ltx 안에서, verbatim의 끝을 찾는 매크로는 이스케이프 문자를 |로, 그룹 여닫이를 []로 바꾼 상태에서 정의되어 있습니다. 그 정의 안에서는 \, {, } 세 문자가 모두 평범한 인쇄 문자여야 하므로, 이 언어가 평소의 표기법으로 자기 자신을 적을 수 없기 때문입니다. verbatim을 정의하는 그 동안만큼은 LaTeX의 소스가 LaTeX로 쓰이기를 멈춥니다. 이 두 줄은 TeX Live 2024에 들어 있는 latex.ltx에서 그대로 확인할 수 있습니다.

끝을 찾는 이 방식에서 실무적인 규칙 두 가지가 나옵니다. 첫째, 환경 안에 문자열 \end{verbatim}을 그대로 쓸 수 없습니다. LaTeX는 그것을 보는 순간을 환경의 끝으로 판단합니다. 둘째, \end{verbatim} 사이에 공백을 넣으면 안 됩니다. 종료 표시는 매크로 인수의 구분자로서 문자 단위로 대조되므로 \end {verbatim}은 아예 인식되지 않고, TeX는 파일 끝까지 읽어 나가다가 Runaway argument?에 이어 ! File ended while scanning use of \@xverbatim.로 멈춥니다. 공백을 정리해 주는 원고 포매터가 바로 이 사고를 일으키곤 합니다. 공백 개수를 보여 주어야 한다면 별표가 붙은 verbatim* 환경이 공백 하나하나를 ␣로 인쇄합니다.

\verb의 구분 문자 고르기와 줄을 넘지 못하는 이유

줄 중간에 짧은 verbatim을 넣으려면 \verb를 씁니다. \verb 바로 뒤에 구분 문자를 하나 두고, 그대로 인쇄할 문자열을 쓴 뒤, 같은 문자로 다시 닫습니다. \verb|\textbf{x}|처럼 쓰면 됩니다. 구분 문자는 내용에 나타나지 않는 문자 라면 거의 무엇이든 좋습니다. 내용에 |가 들어 있으면 \verb!...!, \verb+...+, \verb/.../로 바꿉니다. 다만 고를 수 없는 문자가 둘 있습니다. 알파벳은 안 됩니다. \verbx라고 이어 쓰면 TeX가 그것을 전혀 다른 명령 이름으로 읽기 때문입니다. 그리고 *도 안 됩니다. \verb*가 공백을 ␣로 인쇄하는 별표 형태로 예약되어 있기 때문입니다.

latex
The macro \verb|\textbf{...}| sets bold text;
a pipe in the content needs another delimiter, as in \verb!a|b!.

Count the gaps: \verb*|a  b| prints the spaces as visible marks.

\verb에는 강한 제약이 하나 더 있습니다. 닫는 구분 문자가 같은 줄에 있어야 합니다. 그 전에 줄이 끝나면 ! LaTeX Error: \verb ended by end of line.로 멈춥니다. 실제 원인은 대개 닫는 문자를 빠뜨린 것이지만, 편집기의 자동 줄바꿈이나 포매팅이 긴 줄을 둘로 쪼개는 경우도 의외로 흔합니다. 내용이 길어지면 \verb 대신 블록 환경으로 옮기는 편이 안전합니다. 한편 URL처럼 ~, #, %, _가 들어가기 쉬운 문자열만 다루는 것이 목적이라면 url 또는 hyperref 패키지의 \url{...}이 더 적합합니다. 문자 그대로 출력하면서 적절한 위치에서 줄바꿈까지 해 주기 때문입니다.

본문에서 백슬래시와 밑줄 입력하기

문자 한두 개를 내보내는 것이라면 verbatim까지 갈 필요가 없습니다. 백슬래시는 \textbackslash, 밑줄은 \_로 나옵니다. 여기서 가장 흔한 사고는 \\가 백슬래시가 아니라 줄바꿈 명령이라는 점 입니다. \\라고 써도 백슬래시는 인쇄되지 않고 그 자리에서 줄이 바뀝니다. 수식 모드의 $\backslash$로도 모양은 나오지만 글자체가 수식 글꼴이 되므로, 본문에서는 \textbackslash가 정답입니다. 밑줄을 그대로 쓰면 ! Missing $ inserted.가 나옵니다. TeX에게 _는 "아래 첨자가 뒤따른다"는 뜻이기 때문이며, file\_name처럼 이스케이프하면 해결됩니다.

입력출력유의점
\textbackslash\\\는 줄바꿈 명령이라 백슬래시가 나오지 않음
\__그냥 쓴 _! Missing $ inserted.를 일으킴
\% \& \# \$% & # $앞에 \만 붙이면 됨
\{ \}{ }그룹 기호를 문자로 출력
\textasciitilde~그냥 쓴 ~는 줄바꿈 없는 공백이 됨
\textasciicircum^그냥 쓴 ^는 위 첨자 지시가 됨

\verb가 \section이나 \footnote 안에서 실패하는 이유

\verbverbatim 환경도 다른 명령의 인수 안에는 쓸 수 없습니다. 이유는 금지 규칙이 아니라 단순한 시간 순서입니다. \verb는 자기 텍스트를 읽기 바로 직전에 범주 코드를 바꿉니다. 그런데 \section{...}의 내용은 \section이 호출되는 그 시점에 이미 일반 범주 코드 상태로 토큰 열로 변환되어 있습니다. \verb의 차례가 올 무렵이면 \foo는 이미 "인쇄할 네 글자"가 아니라 "\foo라는 명령"입니다. verbatim은 이미 읽힌 것을 다시 읽을 수 없습니다. 그것이 전부입니다.

까다로운 점은 이 실패가 verbatim이라는 단어를 전혀 언급하지 않는 오류로 나타난다는 것입니다. \section{The \verb|\foo| command}! Undefined control sequence.로 멈춥니다. \foo가 실제로 명령으로 읽혔는데 그런 명령이 없기 때문입니다. 내용에 특수 문자가 하나도 없는 \mbox{\verb|abc|}라면 드물게 친절한 ! LaTeX Error: \verb illegal in argument.가 나옵니다. 블록 환경은 더 읽기 어렵습니다. \footnote{\begin{verbatim} ... \end{verbatim}}Runaway argument?에 이어 ! Paragraph ended before \@xverbatim was complete.를, \parbox{5cm}{...} 안이라면 ! Argument of \@xverbatim has an extra }.를, \caption{...} 안이라면 ! Argument of \@caption has an extra }.를 냅니다. 모두 하나의 원인이 다른 얼굴로 나타난 것입니다.

반면에 표의 셀은 인수가 아닙니다. tabular의 각 셀은 TeX가 열과 행 구분자를 지켜보는 동안 읽히므로, p{4cm} 열을 포함해 \verb가 그대로 작동합니다. 다만 같은 표 안이라도 \multicolumn{2}{c}{...}의 세 번째 인수는 진짜 인수이므로 거기서는 실패합니다. 그러니 "표 안에서는 못 쓴다"가 아니라 "중괄호로 둘러싸인 인수 안에서는 못 쓴다" 로 기억하는 편이 정확합니다.

우회책은 세 가지입니다. 첫째는 cprotect 패키지(Bruno Le Floch, v1.0e)로, 매크로 인수 안의 verbatim을 위해 존재하는 패키지입니다. 문제가 되는 명령 앞에 \cprotect를 붙이기만 하면 \cprotect\section{The \verb|\foo| command}이 통과합니다. 환경의 \begin을 보호하는 \cprotEnv도 함께 들어 있습니다. 둘째는 fancyvrb의 \SaveVerb / \UseVerb 입니다. verbatim 텍스트를 먼저 이름을 붙여 저장해 두고, 인수 안에서는 그 이름만 부르는 두 단계 방식입니다. 셋째는 각주 전용으로, 프리앰블에서 fancyvrb의 \VerbatimFootnotes 를 선언해 두면 \footnote 안에서 verbatim을 쓸 수 있습니다. 참고로 \verb를 fancyvrb의 \Verb로 바꿔도 해결되지 않습니다. 똑같은 시점 문제를 안고 있기 때문입니다. 마지막 수단으로, 제목 하나뿐이라면 \texttt{\textbackslash foo}라고 손으로 적는 것이 가장 빠른 길입니다.

latex
% Fails: \foo was already a command token before \verb could act
% \section{The \verb|\foo| command}   -> Undefined control sequence

% Workaround 1 -- cprotect
\usepackage{cprotect}
\cprotect\section{The \verb|\foo| command}

% Workaround 2 -- save it first, use it later
\usepackage{fancyvrb}
\SaveVerb{cmd}|\foo|
\section{The \UseVerb{cmd} command}

% Workaround 3 -- verbatim inside footnotes
\VerbatimFootnotes

파일 전체 읽어 오기: \verbatiminput과 \VerbatimInput

프리앰블에 \usepackage{verbatim}을 쓰고 본문에서 \verbatiminput{hello.py}라고 하면, 그 외부 파일의 모든 줄이 verbatim으로 조판됩니다. 원고에 복사해 넣는 방식과 달리 원본 파일을 고치면 PDF도 자동으로 따라오므로 코드와 문서가 서로 어긋나지 않습니다. 실제로 동작하는 코드를 싣는 것이 목적이라면 이것이 가장 안전한 방법입니다.

표준 verbatim 환경이 이미 있는데 왜 verbatim이라는 이름의 패키지가 따로 존재하는지 물어볼 만합니다. 답은 \verbatiminput이 아닙니다. LaTeX Tools 번들에 속한 Rainer Schöpf의 이 패키지 문서는 동기를 분명히 밝힙니다. 내장 환경은 \end{verbatim}까지의 모든 내용을 하나의 매크로 인수로 다 읽기 전에는 한 줄도 출력할 수 없다 는 것이고, 그래서 긴 목록은 TeX의 메모리를 넘길 수 있다는 것입니다. 패키지는 이를 한 줄씩 읽어 바로 조판하는 구현으로 바꾸었고(문서는 이 수법을 AMS-TeX의 \comment 매크로에서 빌려 왔다고 적고 있습니다), verbatim을 줄 단위로 읽을 수 있게 되자 \verbatiminput은 사실상 덤으로 얻어졌습니다. 눈에 보이는 부작용이 하나 있습니다. \end{verbatim}같은 줄에 이어 쓴 문자는 내장 환경에서는 인쇄되지만 패키지 판에서는 조용히 버려집니다. 이 차이는 의도된 것이며 문서에 명시되어 있습니다.

document.tex
\usepackage{verbatim}
% ...
\verbatiminput{hello.py}

\begin{comment}
This paragraph is skipped entirely -- not printed, not typeset.
\end{comment}

같은 패키지는 \begin{comment}부터 \end{comment}까지를 통째로 건너뛰는 comment 환경 도 추가합니다. 이는 verbatim 출력이 아니라 아예 출력하지 않기 위한 도구로, 초안 일부를 잠시 빼 둘 때 유용합니다. 파일 삽입을 더 세밀하게 제어하고 싶다면 다음 절의 fancyvrb에 있는 \VerbatimInput[options]{filename} 을 씁니다. \verbatiminput과 달리 테두리와 행 번호를 넘길 수 있고, firstline=10, lastline=25처럼 파일의 일부만 잘라 올 수 있어 긴 소스에서 해당 부분만 보여 주고 싶을 때 잘 맞습니다.

alltt: 일부 명령이 살아 있는 verbatim

코드 예시의 일부만 굵게 하거나 색을 넣고 싶을 때, 일반 verbatim으로는 손을 쓸 수 없습니다. 명령이 전부 꺼져 있기 때문입니다. 이때 표준 LaTeX에 들어 있는 alltt 패키지alltt 환경이 통합니다. allttverbatim과 거의 같이 등폭으로 입력한 그대로 조판하지만, 백슬래시 \와 중괄호 {, } 세 문자만은 평소의 의미를 유지합니다. 겉모습은 verbatim 그대로이면서 내부에서 LaTeX 명령을 실행할 수 있습니다.

latex
\usepackage{alltt}
% ...
\begin{alltt}
def \textbf{greet}(name):
    return "Hi, " + name   \textit{# a comment}
\end{alltt}

이 예에서는 함수 이름 greet가 굵게, 주석이 기울임으로 나오고 나머지는 입력한 그대로 남습니다. 대가는 분명합니다. \, {, } 세 문자를 문자로서 인쇄하려면 이제 \textbackslash, \{, \}라고 적어야 합니다. verbatim 환경이라면 그대로 나옵니다. 즉 alltt는 세 문자만큼의 문자 그대로임을 내주고 나머지를 꾸밀 권리를 사는 거래입니다. 손으로 가볍게 강조하고 싶으면 alltt, 한 글자도 움직여서는 안 되면 verbatim을 씁니다.

행 번호와 테두리: fancyvrb의 Verbatim 환경

행 번호도 테두리도 내장 verbatim으로는 붙일 수 없습니다. 그 일을 맡는 것이 fancyvrb 패키지 이며, 중심은 대문자 V로 시작하는 Verbatim 환경 입니다. 소문자 verbatim과는 별개입니다. 옵션은 \begin{Verbatim}[numbers=left, frame=single]처럼 환경마다 넘기거나, 프리앰블에서 \fvset{numbers=left, ...}로 문서 전체의 기본값으로 삼습니다. fancyvrb는 PSTricks의 저자이기도 한 Timothy Van Zandt가 1992년에 시작했고 2000년부터는 Herbert Voß가 유지 관리하고 있습니다(TeX Live 2024 수록판은 v4.5c). 거의 모든 요구에 답이 있는 이유는 30년치 실용 옵션이 쌓여 있기 때문입니다.

옵션주요 값역할
numbersnone / left / right행 번호 위치, 기본값은 none. 간격은 numbersep으로 조정
framenone / single / lines / leftline / topline / bottomline테두리 종류, 기본값은 none. 굵기는 framerule, 여백은 framesep
fontsize\small, \footnotesize글자 크기, 기본은 본문과 동일
showspacestrue / false공백을 보이는 기호로 표시. 탭은 showtabs, 폭은 tabsize
firstline / lastline정수\VerbatimInput과 함께 파일의 일부만 가져옴
commandchars예: \\\{\}이스케이프 문자와 그룹 여닫이 문자를 지정해 내부에서 명령을 다시 활성화
latex
\usepackage{fancyvrb}
\fvset{fontsize=\small}          % document-wide default
% ...
\begin{Verbatim}[numbers=left, frame=single]
def greet(name):
    return "Hello, " + name
\end{Verbatim}

% only lines 10-25 of an external file, framed
\VerbatimInput[firstline=10, lastline=25, frame=lines]{server.py}

commandchars=\\\{\}를 지정하면 verbatim 안에서도 \, {, }가 이스케이프 문자와 그룹 구분자로 작동하여 alltt와 같은 요령으로 명령을 넣을 수 있습니다. 같은 옵션 묶음을 매번 쓰기 싫다면 \DefineVerbatimEnvironment{Code}{Verbatim}{numbers=left, frame=single}처럼 직접 환경을 정의 하는 것이 정석입니다. 그다음부터는 \begin{Code}만 쓰면 됩니다. 다만 이 페이지의 도구는 모두 "입력한 그대로 내보내기"를 위한 것이며, 키워드에 색을 입히는 일, 곧 구문 강조 는 하지 않습니다. 색과 서식을 갖춘 소스를 싣고 싶다면 TeX 매크로만으로 색을 입히는 listings나 Python의 Pygments에 맡기는 minted가 담당입니다. 관련 페이지 "소스 코드 게재"에서 둘을 비교합니다.