여러 선택 인수나 제대로 된 별표형 변종을 가진 명령을 만들고 싶어지면, \newcommand식으로 인수 개수만 세는 방식은 감당하기 어렵습니다. 이때 쓰는 현대적 인터페이스가 \NewDocumentCommand입니다. 각 인수의 *종류*를 문자들의 나열로 선언합니다. 원래는 xparse 패키지 기능이었지만 지금은 LaTeX 커널의 일부입니다. 이 페이지에서는 인수 지정 미니 언어와, 사용자가 실제로 무엇을 주었는지 확인하는 판정 명령을 다룹니다.
지금은 커널의 일부
예전에는 \usepackage{xparse}가 필요했지만, 2020년 10월 LaTeX 이후 이 구조는 LaTeX 커널에 들어갔습니다. \NewDocumentCommand 같은 명령은 처음부터 사용할 수 있고, xparse를 읽어들일 필요는 거의 없습니다. 커널 안에서 이 기능을 구현하는 파일은 ltcmd 라고 불리며, xparse 패키지 자체는 공식적으로 “obsolete”로 설명됩니다. 일부 비권장 인수형을 제외하면 새 코드는 xparse를 불러오지 않고 그대로 쓸 수 있습니다.
이 현대적 정의법의 생각은 의미(인터페이스)와 구현을 분리한다 는 것입니다. 사용자에게 보이는 인수 배열(필수, 선택, 별표)을 “인수 지정(argument specification, arg-spec)”으로 선언하면, 본문 코드는 언제나 #1, #2 같은 정규화된 형태로 인수를 받습니다. \newcommand의 기초는 “매크로 정의” 페이지에서 다루었으니, 아직 읽지 않았다면 먼저 보면 차이가 더 잘 보입니다.
명령과 환경 정의하기
기본 형태는 \NewDocumentCommand{\cmd}{⟨arg-spec⟩}{⟨code⟩} 입니다. 첫 번째 인수는 만들 명령 이름, 두 번째는 인수 지정, 세 번째는 본문입니다. \newcommand와 마찬가지로 목적이 다른 세 가지 친척이 있고, 차이는 이미 정의된 이름에 어떻게 동작하느냐입니다.
| 명령 | 기존 이름에 대해 |
|---|---|
\NewDocumentCommand | 이미 정의되어 있으면 오류 |
\RenewDocumentCommand | 아직 정의되지 않았으면 오류(기존 것을 다시 만듦) |
\ProvideDocumentCommand | 아직 없을 때만 정의 |
\DeclareDocumentCommand | 항상 덮어씀(신중히 사용) |
환경에도 같은 구조가 있어 \NewDocumentEnvironment{⟨env⟩}{⟨arg-spec⟩}{⟨start code⟩}{⟨end code⟩} 로 정의합니다(\Renew…, \Provide…, \Declare…도 동일). 인수는 \begin{⟨env⟩} 뒤에 주어지고, 시작 코드와 종료 코드 양쪽에서 참조할 수 있습니다. 자세한 내용은 “사용자 환경” 페이지를 보세요. 또한 이렇게 만든 모든 명령은 자동으로 robust 해집니다(내부적으로 ε-TeX의 \protected 구조 사용). 따라서 제목이나 캡션에서 \protect를 걱정하지 않아도 됩니다.
\NewExpandableDocumentCommand 라는 전개 가능한 버전도 있습니다. \edef나 \write 안처럼 명령이 전개되는 문맥에서 쓸 수 있지만, 대신 허용되는 인수형에 제한이 있습니다. 뒤에서 설명할 s/t 같은 참거짓 판정이나 v 등은 사용할 수 없습니다. 보통 문서 작업에는 일반 버전이면 충분합니다.
인수 지정 언어
인수 지정은 한 글자씩 하나의 인수 “타입”을 나타내는 문자열 입니다. 필수인지, 선택인지, 별표인지, 특정 문자로 구분되는지 등을 글자로 나열합니다. 각 타입은 본문에 #1, #2 등으로 도착하지만, 핵심은 주어지지 않은 선택 인수에는 특수한 값이 들어간다 는 점입니다. 주요 타입은 다음 표와 같습니다.
| 타입 | 쓰는 법 | 의미 | 본문에서 받는 형태 |
|---|---|---|---|
m | m | 필수 {} 인수 | 보통의 #1 |
o | o | 선택 [...] 인수 | 없으면 -NoValue- |
O | O{default} | 기본값 있는 선택 인수 | 없으면 default |
s | s | 선택 별표 * | \BooleanTrue / \BooleanFalse |
t | t⟨char⟩ | 지정한 문자 토큰의 선택 여부 | \BooleanTrue / \BooleanFalse |
r | r⟨d1⟩⟨d2⟩ | d1…d2로 구분되는 필수 인수 | 없으면 오류 뒤 -NoValue- |
d | d⟨d1⟩⟨d2⟩ | d1…d2로 구분되는 선택 인수 | 없으면 -NoValue- |
e | e{⟨tokens⟩} | 장식자(^, _ 등) | 각각 없으면 -NoValue- |
v | v | verbatim 인수 | 그대로의 문자들 |
m 은 가장 기본적인 필수 인수입니다. {...}나 단일 토큰을 받고, 바깥 {}는 제거되어 본문으로 전달됩니다. o 는 표준 선택 인수 [...], O{default} 는 여기에 기본값을 준 버전입니다. O, D 같은 대문자 타입은 기본값을 지정할 수 있고, o, d 같은 소문자판은 대신 특수 표시 -NoValue-를 반환합니다.
s 는 별표 *의 유무를 보는 타입입니다. *가 있으면 \BooleanTrue, 없으면 \BooleanFalse가 들어갑니다. 임의의 한 문자로 일반화한 것이 t⟨char⟩ 이며, 예를 들어 t+는 +의 유무를 참거짓으로 받습니다. r⟨d1⟩⟨d2⟩ 는 직접 고른 구분자(예: r()의 괄호)로 감싸는 “필수” 구분 인수이고, 여는 쪽이 없으면 오류를 내고 -NoValue-를 채웁니다. 구분자는 있지만 선택 사항인 것이 d⟨d1⟩⟨d2⟩ 입니다.
조금 특이한 것은 e{⟨tokens⟩} 로, ^나 _ 같은 장식자(embellishments) 를 모아 받습니다. e{^_}라고 하면 ^{...}와 _{...}를 임의의 순서로 주워 오고, 주어지지 않은 것은 -NoValue-가 됩니다. 나열하는 토큰은 모두 서로 달라야 합니다. v 는 \verb처럼 verbatim 인수를 읽는 타입입니다. 구분형(r, d 등)의 구분자로는 TeX 그룹화 문자 {와 }를 쓸 수 없으므로, 보통 [], (), ""처럼 자연스럽게 짝이 되는 문자를 선택합니다.
인수 검사: 판정 명령
선택 인수나 별표를 다루려면, 본문 안에서 “주어졌는가”를 판정해야 합니다. 이를 위한 전용 명령은 두 계열입니다. 먼저 \IfNoValueTF{#1}{⟨if no value⟩}{⟨if value⟩} 는 인수가 -NoValue-인지, 즉 주어지지 않았는지 확인합니다. 값이 없으면 첫 번째 분기, 있으면 두 번째 분기를 실행합니다. 반대 방향의 \IfValueTF 도 있고, T만 또는 F만 있는 형태(\IfNoValueT / \IfNoValueF, \IfValueT / \IfValueF)도 쓸 수 있습니다.
s나 t로 받은 참거짓 값에는 전용 \IfBooleanTF{#1}{⟨if starred⟩}{⟨if not⟩} 를 씁니다. #1이 \BooleanTrue이면 첫 번째 분기, \BooleanFalse이면 두 번째 분기를 실행합니다(\IfBooleanT / \IfBooleanF도 있음). 요령은 \IfNoValueTF 계열은 o/d 같은 선택 인수에, \IfBooleanTF 계열은 s/t의 참거짓 인수에 쓴다는 것입니다. 또한 -NoValue-는 문자열 -NoValue-와 일치하지 않도록 만들어져 있으므로, 문자열 비교가 아니라 반드시 \IfNoValueTF로 판정하세요.
실례: 별표와 기본값 있는 선택 인수
s O{} m이라는 인수 지정으로 별표형 변종과 기본값 있는 선택 인수를 모두 가진 명령을 만들어 봅니다. 여기서는 제목 명령을 예로 듭니다. #1이 별표(\IfBooleanTF로 판정), #2가 선택 짧은 제목(기본값은 빈 문자열), #3가 본 제목입니다.
\NewDocumentCommand{\heading}{s O{} m}{%
\IfBooleanTF{#1}%
{\textbf{#3}}% 星つき: 番号なしの見出し / starred: unnumbered
{\section%
\IfNoValueTF{#2}%
{{#3}}% 短縮見出しなし / no short title
{[#2]{#3}}% 短縮見出しあり / with short title
}%
}
% 使用例 / usage
\heading{Introduction} % 通常の番号つき節
\heading[Intro]{A Long Introduction} % 目次用に短縮見出しを指定
\heading*{Preface} % 星つき: 太字の無番号見出し세부 사항 하나: o와 O{}를 섞어 생각하지 마세요. o를 쓰면 지정되지 않은 인수가 -NoValue-가 되므로 위처럼 \IfNoValueTF로 분기할 수 있습니다. 반면 O{}처럼 기본값을 주면 값이 항상 존재합니다(미지정 시 빈 값). 이때는 \IfNoValueTF가 아니라 \tl_if_blank:nTF(expl3)나 \ifblank(etoolbox)으로 “내용이 비었는가”를 검사해야 합니다. 위 예제가 의도대로 동작하려면 선택 인수를 o(기본값 없음)로 두어야 합니다.
\newcommand보다 나은 이유
\newcommand의 선택 인수는 맨 앞 하나뿐 이고, 별표형 변종도 기본 제공하지 않습니다. 전통적으로는 이를 보완하려고 \@ifstar나 \@ifnextchar 같은 저수준 장치를 직접 썼지만, 이들은 \makeatletter가 필요하고 공백 처리나 중첩에서 깨지기 쉬우며 읽기도 어렵습니다.
- 여러 독립 선택 인수 를 가질 수 있습니다(
o o m,O{a} O{b} m등). s와\IfBooleanTF로 진짜 별표형 변종 을 다룹니다.\@ifstar해킹이 필요 없습니다.- 기본값이 선언적 입니다.
O{default}라고 쓰기만 하면 미지정 시 값이 정해집니다. - 처음부터 robust 합니다. 제목과 캡션에서도
\protect가 필요 없습니다. - 구분 인수(
r,d)나 장식자(e)처럼\newcommand로는 표현할 수 없는 입력 문법도 표현할 수 있습니다.
정리하면 아주 단순한 축약 매크로라면 \newcommand로 충분하지만, 인수 구조가 조금이라도 복잡해지면 \NewDocumentCommand가 첫 번째 후보 입니다. 더 나아가 본문 처리를 프로그램처럼 쓰고 싶다면 expl3(LaTeX3 프로그래밍 계층)와 조합하는 것이 자연스러운 발전입니다. 클래스와 패키지를 만드는 법은 “클래스와 패키지 작성” 페이지를 보세요.