블로그

Mustang으로 ZUGFeRD 검증하기: 무엇이 통과하고 무엇이 실패하는가

Mustang은 Factur-X / ZUGFeRD의 de-facto reference checker입니다. PDF/A-3에 CII XML을 embed할 때의 common failure modes와 발송 전 verification을 정리합니다.

2026년에 독일 B2B 고객에게 e-invoice를 보내는 경우, 파일은 ZUGFeRD-compliant이거나 수신 시 bounce됩니다. 프랑스의 Factur-X도 마찬가지입니다. 형식은 EN 16931 CII XML이 첨부된 PDF/A-3 wrapper입니다. 처음부터 생성하기 어렵고, 검증에는 reference engine이 필요합니다.

실무에서 그 engine은 Mustang(mustangproject.org)입니다. PDF/A-3에서 embedded XML을 추출하고 EN 16931 Schematron에 대해 validate하는 open-source Java project입니다. ZUGFeRD와 Factur-X에 대한 open-source support가 가장 깊고, 많은 independent verifiers가 Mustang을 실행합니다.

이 글은 Mustang이 flag하는 failure modes와 더 빠르게 실행하는 방법을 설명합니다.

Mustang이 실제로 확인하는 것

Factur-X 또는 ZUGFeRD PDF를 Mustang에 넣으면 대략 다음을 수행합니다.

  1. Embedded file 추출. PDF/A-3는 attachments를 /EmbeddedFiles name tree에 저장합니다. Mustang은 canonical filename(Factur-X는 factur-x.xml, ZUGFeRD 2.x는 zugferd-invoice.xml)을 찾아 bytes를 읽습니다.
  2. AFRelationship 확인. Attached file은 Factur-X / ZUGFeRD baseline에 따라 AFRelationship="Alternative"로 선언되어야 합니다. 다른 값(Source, Data, Supplement)은 fail입니다.
  3. XMP namespace와 version 확인. Factur-X 1.0은 urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#를 씁니다. ZUGFeRD 2.x는 urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#를 씁니다. Namespace나 version string이 틀리면 fail입니다.
  4. XML을 Cross-Industry Invoice (CII)로 parse. XML은 well-formed여야 하고 올바른 CII root element(rsm:CrossIndustryInvoice)로 시작해야 합니다.
  5. EN 16931 Schematron 실행. 검증의 대부분입니다. Field semantics, mandatory codes, totals math, VAT logic, party identifiers 등 약 200개 business rules를 확인합니다.

Pass는 invoice가 EU 내 EN 16931-conformant AP system에서 받아들여질 수 있다는 뜻입니다. Fail은 고객 AP automation이 receipt에서 invoice를 reject하고 AR team이 manual exception을 처리하게 된다는 뜻입니다.

가장 자주 보는 5가지 failure modes

팀들이 첫 e-invoices를 테스트할 때 validator의 Mustang 쪽에서 반복적으로 나타납니다.

1. Wrong AFRelationship

ERROR: Embedded file factur-x.xml uses AFRelationship="Source",
expected "Alternative".

PDF spec은 attached files에 여러 relationship types를 허용합니다. Factur-X / ZUGFeRD는 Alternative를 요구합니다. 즉 attached XML은 visible PDF content의 alternative representation입니다. PDF generator가 Data를 사용한다면(많은 libraries의 default), Mustang은 즉시 fail합니다. Visible PDF는 정상 render되지만 structured payload는 AP system에 유효하지 않습니다.

2. Wrong / missing XMP namespace

ERROR: XMP metadata missing fx:DocumentType or fx:DocumentFileName under
namespace urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#.

PDF의 XMP packet은 어떤 Factur-X profile인지(MINIMUM, BASIC, EN 16931, EXTENDED)와 어떤 filename을 찾아야 하는지 선언해야 합니다. PDF/A-3 wrapper를 손으로 작성하면 빠뜨리기 쉽습니다. gPdf의 /api/v1/e-invoice/render endpoint는 이를 자동으로 emit합니다.

3. CII XML은 well-formed이지만 EN 16931 Schematron fail

ERROR: BR-CO-25 — In an invoice (BR-01) the
  ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime is required when
  ram:DocumentTypeCode is 380.

현실의 failures 대부분은 여기입니다. XML 문법은 valid하지만 business rules가 fail합니다. EN 16931 Schematron rules에는 stable IDs(BR-01, BR-CO-25 등)가 있어 specification에서 lookup할 수 있습니다. 흔한 것:

  • BR-01: invoice에는 unique invoice number가 있어야 합니다.
  • BR-04: issue date가 있어야 합니다.
  • BR-05: invoice type code가 있어야 합니다.
  • BR-CO-25: document type이 “Commercial invoice”이면 payment terms가 필요합니다.
  • BR-Z-01: VAT category codes는 S, Z, E, AE, K, G, O, L, M 중 하나여야 합니다.

Source data를 고치고 rebuild한 뒤 re-validate하세요.

4. PDF/A wrapper 자체가 validate되지 않음

INFO: CII XML extracted and validates against EN 16931.
ERROR: PDF/A-3b conformance check failed: missing Output Intent.

이 경우 Mustang의 XML check는 pass하지만 underlying PDF/A-3 wrapper는 fail합니다. 흔한 원인은 XML은 제대로 작성했지만 PDF/A-3가 아니라 ordinary PDF를 emit한 것입니다. Embedded file은 있지만 archival wrapper rules가 충족되지 않습니다. gpdf.com/validator/는 veraPDF를 parallel 실행해 이를 잡습니다. PDF/A-3 fail은 veraPDF column에, Mustang은 XML pass로 표시됩니다.

5. Encoding / declaration mismatch

ERROR: XML declares <?xml version="1.0" encoding="UTF-8"?> but the
embedded byte stream is UTF-8 with BOM. Mustang strict mode rejects BOM.

XML tool이 UTF-8 BOM을 emit하고 그 bytes를 raw로 embed할 때 자주 생깁니다. 해결은 embedding 전에 BOM을 strip하는 것입니다. gPdf e-invoice endpoint는 이를 normalise합니다.

Java 설치 없이 Mustang 실행하기

One-off check라면 Java + Mustang CLI 설치도 괜찮습니다. 하지만 지속적 verification, 즉 invoice 생성마다, e-invoice compliance를 assert하는 CI run마다 사용한다면 불필요한 friction입니다.

gpdf.com/validator/는 Mustang을 browser에서 실행합니다.

  1. Factur-X / ZUGFeRD PDF를 upload zone에 drag합니다.
  2. Validator가 embedded XML을 추출하고 Mustang의 Schematron engine을 실행합니다(JavaScript / WebAssembly로 compiled, Cloudflare Worker에서 run).
  3. Mustang report는 veraPDF의 PDF/A-3 report와 side-by-side로 돌아옵니다. 두 layer가 모두 pass해야 하기 때문입니다.
  4. QA evidence용 JSON report를 download합니다.

Login 없음. Quota 없음. Maven으로 설치하는 Mustang과 같은 종류의 check를 free public service로 제공합니다.

TL;DR

Mustang은 5가지 common failure modes를 flag합니다. 대부분은 “fully-conformant Factur-X / ZUGFeRD PDF/A-3를 emit하지 않는 tool로 생성된 파일”이라는 뜻입니다. gPdf의 E-invoice API는 한 번의 call로 이를 emit합니다. validator는 Mustang + veraPDF로 결과를 parallel verify합니다.

Mustang이 잡는 bugs 대부분은 wrapper 또는 AFRelationship 문제이며 XML semantics만의 문제가 아닙니다. File을 올바르게 generate하는 것이 대부분의 작업이고, validator는 그것을 증명하는 receipt입니다.