# XML은 값싼 DSL이다

> Clean Markdown view of GeekNews topic #27507. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=27507](https://news.hada.io/topic?id=27507)
- GeekNews Markdown: [https://news.hada.io/topic/27507.md](https://news.hada.io/topic/27507.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2026-03-15T06:57:27+09:00
- Updated: 2026-03-15T06:57:27+09:00
- Original source: [unplannedobsolescence.com](https://unplannedobsolescence.com/blog/xml-cheap-dsl/)
- Points: 10
- Comments: 1

## Summary

미국 국세청이 **Tax Withholding Estimator(TWE)** 를 오픈소스로 공개하며, 세법 계산 로직을 **XML 기반 선언적 명세**로 모델링했습니다. 각 세금 항목은 XML로 정의된 팩트들의 의존 그래프로 표현되어, 실행 순서나 중간값 관리 없이도 계산 과정을 투명하게 추적할 수 있습니다. JSON보다 XML을 선택한 이유는 DSL 구축에 유리하고, XPath 등 **성숙한 도구 생태계**를 그대로 활용할 수 있기 때문입니다. IRS의 이 접근은 복잡한 세법 로직을 코드가 아닌 데이터로 다루는 새로운 표준을 제시합니다.

## Topic Body

- 미국 국세청이 새로운 **Tax Withholding Estimator(TWE)** 를 오픈소스로 공개했으며, 미국 세법을 **XML 기반의 선언적 명세로 모델링한 구조**가 핵심 설계 원칙  
- TWE의 세금 계산 로직은 **Fact Graph**라는 로직 엔진 위에 구축되며, 각 세금 항목을 XML로 정의된 "팩트(Fact)"들의 의존 관계 그래프로 표현  
- JavaScript 같은 **명령형 언어**로 세금 로직을 구현하면 실행 순서 관리, 중간값 소실, 구현 세부사항 노출 등의 문제가 발생하므로 선언적 접근이 필수적  
- JSON은 임의 중첩 표현식 처리에 부적합하고, XML은 태그 자체가 객체 종류를 나타내므로 **DSL 구축에 훨씬 유리**  
- XML은 XPath 등 **성숙한 도구 생태계**를 무료로 활용할 수 있어, 크로스 플랫폼 선언적 명세의 가장 비용 효율적인 선택지임  
  
---  
  
### Fact Graph: XML로 표현한 미국 세법  
- 미국 국세청 IRS가 공개한 **Tax Withholding Estimator(TWE)** 는 납세자가 **소득과 공제액을 입력해 세금과 원천징수액을 추정**하는 도구  
  - 프로젝트는 **오픈소스**로 공개되어 있으며, 일반인의 기여도 허용됨  
- TWE는 **두 개의 XML 설정**으로 생성되는 정적 사이트이며, 첫 번째는 미국 세법을 표현한 Fact Dictionary  
- **Fact Graph**는 IRS Direct File 프로젝트에서 원래 구축된 로직 엔진으로, 납세자의 세금 의무와 원천징수를 Fact Dictionary에 정의된 팩트 기반으로 계산  
- 각 팩트는 XML로 정의되며, 예를 들어 `/totalOwed`는 `/totalTax`에서 `/totalPayments`를 빼는 파생(Derived) 팩트로 표현  
  - "총 납부액(total owed)"은 소득에 대한 총 세금(total tax)과 이미 납부한 금액(total payments)의 차이  
- **환급 가능 크레딧(refundable credits)** 은 세금 잔액을 음수로 만들 수 있는 세액공제로, Earned Income Credit, Child Tax Credit, American Opportunity Credit 등을 `&lt;Add&gt;`로 합산  
- **환급 불가 크레딧(non-refundable credits)** 은 세금 부담을 0까지만 줄일 수 있으며, `&lt;GreaterOf&gt;` 연산자로 0과 (잠정세액 - 환급불가 크레딧) 중 큰 값을 선택  
- 사용자 입력값은 `&lt;Derived&gt;` 대신 `&lt;Writable&gt;` 태그를 사용하며, `<Dollar/>`, `<Boolean/>` 등으로 값 타입을 지정  
- 팩트들이 서로 의존하며 최종 세금 수치를 도출하는 **그래프 구조**  
  
### 세금 로직에 선언적 명세가 필요한 이유  
  
- JavaScript로 동일한 계산을 작성하면 `const totalOwed = totalTax - totalPayments`처럼 간결하지만, 이는 **명령형(imperative)** 방식으로 순차 실행 후 중간 단계가 소실됨  
- 의존 관계가 깊어지면 **실행 순서 문제**가 발생: `getInput()` 같은 사용자 입력 함수가 이후 계산을 모두 차단하며, 배우자 유무 등에 따라 질문 자체가 달라져야 함  
- Social Security 소득 합산 로직에서 `map`/`reduce` 같은 JavaScript 구현 세부사항이 노출되지만, XML의 `&lt;CollectionSum&gt;`은 **세금 수학 개념 자체**를 표현  
  - `&lt;Dependency path="/socialSecuritySources/*/totalFederalTaxesPaid"/&gt;`로 컬렉션 내 항목을 합산  
- Fact Dictionary는 **선언적(declarative)** 방식으로, 계산의 구체적 실행 단계나 순서를 기술하지 않고 명명된 계산과 의존 관계만 기술하면 엔진이 자동으로 실행 방법을 결정  
- 선언적 세금 모델의 가장 중요한 이점은 **감사 가능성(auditability)** 과 **내부 조사(introspection)**: 프로그램에 "이 숫자를 어떻게 도출했는가"를 질문 가능  
  - 명령형 프로그램은 중간값이 이미 폐기되어 로그나 디버거로만 확인 가능하며, 미국 세법처럼 수백 개의 중간 계산이 있는 경우 확장 불가  
- Fact Graph의 원 저자 Chris Given에 따르면, Fact Graph는 "질문하지 않은 항목들이 세금 신고 결과를 바꾸지 않았고, 자격이 되는 모든 세금 혜택을 받고 있음을 **증명하는 수단**"  
- TurboTax를 만든 **Intuit**도 동일한 결론에 도달하여 2020년 "Tax Knowledge Graph" 백서를 발표했으나 구현은 비공개  
  - IRS Fact Graph는 **오픈소스이자 퍼블릭 도메인**으로 누구나 연구, 공유, 확장 가능  
  
### XML이 JSON보다 DSL에 훨씬 적합한 이유  
  
- 세법의 선언적 데이터 표현 형식으로 JSON을 시도하면, **임의 중첩 표현식 처리가 매우 불편**  
  - JSON의 유일한 복합 데이터 구조는 객체이므로, 모든 자식 객체가 `"type"`, `"kind"` 등으로 자신의 종류를 명시해야 함  
  - XML에서는 **태그 이름 자체가 객체의 종류**를 나타내므로 별도 선언 불필요  
- 동일한 `/tentativeTaxNetNonRefundableCredits` 팩트의 JSON 표현이 XML보다 오히려 길고 복잡  
- XML은 **주석(comment)** 지원, 합리적인 공백/줄바꿈 처리 등 JSON에서 당연시되는 불편함이 없음  
- 속성(attribute)과 명명된 자식 요소(named children)가 언어 설계에서 **무엇을 강조할지 선택할 수 있는 표현력**을 제공  
- "달러"와 "정수"의 구분처럼 **고유한 데이터 타입 정의**가 가능  
- 긴 설명 텍스트를 다룰 때 XML이 JSON보다 **읽기와 수동 편집에 훨씬 쾌적**  
  
### XML의 범용성과 도구 생태계  
  
- S-expression, Prolog, KDL 등 대안 문법이 XML보다 읽기 좋을 수 있지만, XML을 사용하면 **파서와 범용 도구 생태계를 무료로** 획득  
  - S-expression은 Lisp에서, Prolog 항은 Prolog에서 잘 동작하지만, XML은 **어떤 형식으로든 변환 가능**  
- Prolog에서 XML을 Prolog 항으로 변환하는 것은 **단일 술어(predicate) 하나**로 가능  
- Hacker News 사용자 ok123456의 "Prolog/Datalog를 쓰면 되지 않느냐"는 질문도 언급되었으며, 가능하지만 범용성에서 XML이 우위  
- Chris Given은 YAML에 대해 "절대로 미국 세법 로직을 YAML로 표현하려 하지 말 것"이라고 언급  
- **XPath**를 활용한 실제 사례: 셸 명령 한 줄로 팩트 경로를 퍼지 검색하고, 선택한 경로의 정의를 즉시 조회하는 스크립트를 작성  
  - `cat facts.xml | xpath -q -e '//Fact/@path' | grep -o '/[^"]*' | fzf`로 팩트 검색  
  - 의존성 체인을 거슬러 올라가 어떤 팩트가 해당 팩트에 의존하는지 추적하는 기능도 추가  
  - 약 60줄의 bash 스크립트로 거의 매일 사용하는 디버깅 도구로 발전  
- 팀원들도 유사한 **빠른 디버깅 도구**를 각자 만들었으며, 모두 XML을 사소하게 파싱하여 Fact Graph의 Scala 구현을 건드리지 않고 각자의 언어로 작업  
- 핵심 교훈: **범용 데이터 표현은 매우 가치가 높으며**, 이 범주에는 JSON과 XML 두 가지만 존재  
  - 대부분의 경우 JSON을 선택해야 하지만, DSL이 필요하면 **XML이 가장 저렴한 선택지**이며, 그 비용 효율성 덕분에 팀이 혁신 예산을 다른 곳에 사용 가능  
  
### 부가 사항  
  
- 프로그래머가 아닌 사람도 스키마 설계가 잘 되어 있으면 XML을 읽을 수 있으며, 다만 **대안 뷰를 별도 구축**하는 것이 바람직  
- 최근 XML에 대한 관심이 증가하는 추세: Jake Low의 XML 문서를 평면 라인 지향 표현으로 변환하는 도구 `grex`, Martijn Faassen의 **Rust로 구현한 최신 XPath/XSLT 엔진** Xee 등  
- TWE의 팩트는 원천징수 추정용이므로 **세금 신고에 직접 사용하면 안 됨**

## Comments



### Comment 53027

- Author: neo
- Created: 2026-03-15T06:57:28+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=47375764) 
- XML은 여러 언어에서 제대로 파싱하려면 **비용이 많이 드는 포맷**임  
  실제로 표준에 가깝게 구현하려면 libxml2, expat, Xerces 같은 세 가지 오픈소스 구현체에 의존해야 함  
  SGML 계열 언어의 핵심은 “리스트”를 1급 객체로, 중첩을 2급 객체로 취급한다는 점이며, 태그 이름과 속성이라는 두 축으로 메타데이터를 추가할 수 있음  
  XML은 DSL로서 여전히 유용하지만, 진짜 XML을 쓰려면 “cheap”이라는 단어는 버려야 함  
  또, 선언형 DSL을 **명령형 수식처럼 보이게** 만들 수도 있음. 예를 들어 `totalOwed = totalTax - totalPayments` 같은 식은 XML DSL과 동일한 의미를 가질 수 있음  
  METAFONT 같은 언어가 이런 접근을 보여줌 ([예시 링크](https://en.wikipedia.org/wiki/Metafont#Example))

  - XML이 반복해서 같은 실수를 하는 걸 자주 봄  
    **포맷에 기능을 많이 넣을수록 파싱이 어려워진다**는 단순한 진리를 잊는 경우가 많음  
    JSON이 인기 있는 이유는 기능이 적어서 파싱이 쉽기 때문임  
    반면 XML은 attributes, namespaces, CDATA, DTDs 등 너무 많은 걸 넣었음  
    SQLite를 교환 포맷으로 쓰자는 논의도 있었지만, 그것도 XML처럼 복잡해질 위험이 있음  
    CSV가 단순해서 여전히 사랑받는 이유도 여기에 있음  
    요즘 JSON에 주석이나 타입 정보를 억지로 넣는 시도는 **나쁜 XML 속성의 재현**임

  - 글쓴이로서 동의함  
    선언형 명세를 수학식처럼 보이게 만드는 건 가능하지만, 그건 결국 **새 언어를 만드는 일**임  
    그렇게 되면 파서를 모든 환경에 이식해야 하는 문제가 생김  
    연산자 우선순위나 switch 표현 같은 문법적 결정도 직접 해야 하고, 결국 복잡도가 폭증함  
    그래서 “cheap”이란 단어를 쓴 이유가 바로 여기에 있음 — 이미 모든 환경에 파서와 툴링이 존재하는 포맷을 쓰는 게 비용 절감임  
    표현력은 줄지만, **소규모 팀에게는 현명한 선택**임

  - 엔터프라이즈 Java에서 XML을 많이 써봤는데, **메모리와 CPU 병목의 주범**이었음  
    XML은 절대 cheap하지 않음

  - SGML의 핵심은 요소의 **정규식 기반 콘텐츠 모델**임  
    단순히 리스트 구조만이 아니라, BNF처럼 문법 생산 규칙을 정의할 수 있음

  - “XML proper”이 아니라 “XML lookalike”라고 한 건 너무 까다로운 표현 같음  
    모든 XML 기능을 쓰지 않아도 XML은 여전히 XML임  
    마치 컵홀더가 없다고 스쿨버스를 “버스 흉내”라고 부르는 것과 같음

- XML 대신 **eDSL을 잘 지원하는 언어**를 쓰면 된다고 생각함  
  Haskell, OCaml, Scala 같은 언어는 applicative나 arrow 같은 개념으로 병렬 계산을 쉽게 표현할 수 있음  
  JavaScript에서도 `.reduce()` 대신 `sum` 같은 추상화를 만들면 됨  
  XML DSL을 만들면 결국 병렬화, 가독성, **새로운 문법 발명** 같은 문제를 다시 풀어야 함  
  복잡한 도메인에서는 Greenspun의 10번째 법칙에 부딪힐 가능성이 큼

  - 하지만 Haskell 같은 언어는 배우기 어렵다는 문제가 있음  
    30년 경력의 개발자라도 진입 장벽이 높다고 느낌

  - Raku도 좋은 선택임  
    Haskell 기반으로 시작했고, **내장 Grammar와 함수형 스타일**을 지원해서 DSL 작성에 유리함

  - HTML! (농담 섞인 짧은 반응)

  - Lisp도 가능함  
    S-expression을 보면 XML이 얼마나 **장황하고 무겁게** 느껴지는지 바로 알 수 있음

- JSON 구조를 좀 더 잘 설계할 수 있음  
  각 노드를 타입 키 하나와 배열 값으로 구성하면 S-expression처럼 표현 가능함  
  이렇게 하면 **스트리밍 파싱**이 가능하고, 타입을 먼저 알 수 있음  
  대규모 데이터셋에서 유용함

  - JSON은 XML보다 훨씬 단순하고 **파싱 비용이 낮음**  
    XML은 태그 짝 맞추기, 속성 처리 등 상태 관리가 복잡하지만  
    JSON은 `{}`, `[]`만 맞추면 됨  
    이런 단순함이 누적되어 **지연 시간 감소**로 이어짐

  - 하지만 JSON의 따옴표가 너무 많아서 **시각적 노이즈**처럼 느껴짐  
    개인적으로는 Clojure의 EDN이 더 깔끔하다고 생각함

  - 이런 JSON 구조는 미학적으로 **퇴화된 형태**라고 느낌  
    태그가 필요한 데이터라면 그에 맞는 표현 방식을 쓰는 게 낫다고 생각함

- [The Lost Art of XML](https://marcosmagueta.com/blog/the-lost-art-of-xml/) 글이 더 흥미로웠음  
  웹 개발 도구의 상당수가 **XML이 브라우저 전쟁에서 패배한 결과**로 생겨났다는 관점이 인상적이었음

  - 하지만 “XML이 버려진 건 JavaScript가 이겼기 때문”이라는 주장은 동의하기 어려움  
    브라우저는 원래 XML도 지원했음 (AJAX의 X가 XML임)  
    단지 개발자들이 XML을 싫어했을 뿐임  
    XML은 **과도한 설계와 복잡성** 때문에 외면받았다고 생각함

  - 예전 XML API 시대를 직접 겪어본 입장에서, XML은 정말 고통스러웠음  
    각 언어마다 인코더/디코더를 따로 만들어야 했고, 유지보수도 힘들었음  
    JSON은 단순히 배열과 객체로 매핑되어서 **언어 간 호환성이 뛰어남**  
    XML 스키마 설계 회의에 시간을 낭비하던 시절을 생각하면, JSON은 **Prettier가 탭 vs 스페이스 논쟁을 끝낸 것처럼** API 설계를 단순화했음

  - 결국 “복잡한 걸 배우기 싫다”는 태도에서 출발했지만, 시간이 지나면 다시 기능이 필요해지는 패턴임

- 폴란드 세무당국은 XML을 사랑함  
  하지만 그들의 XML은 **사람이 읽을 수 없을 정도로 난해**함  
  필드명이 `P_19N` 같은 식으로 되어 있고, 실제 의미를 알기 위해 스키마를 봐야 함  
  심지어 법 조항 번호까지 들어감  
  아이러니하게도 VAT 법안 작성자가 현재 세무 컨설팅을 하고 있음

- 나는 **S-expression 기반 DSL**을 직접 사용 중임  
  WebAssembly 기반 데스크톱 브라우저 런타임에서 HTML과 CSS 역할을 하며,  
  문서 동기화 문제를 해결하는 자체 마크업 언어에도 재활용함  
  관련 예시는 [CanvasUI 예제 코드](https://gitlab.com/canvasui/canvasui-engine/-/blame/main/examples/counter/app.cuiml?ref_type=heads#L10)와 [스타일 파일](https://gitlab.com/canvasui/canvasui-engine/-/blob/main/examples/counter/styles/app.cuiss?ref_type=heads#L3), [문서화 도구](https://gitlab.com/sablelang/libcuidoc)에서 볼 수 있음

  - S-expression은 **파서 구현이 매우 단순**해서 인터뷰 문제로도 썼었음  
    지원자가 직접 간단한 언어를 구현해내는 순간의 반응이 인상적이었음

- XML은 DSL이라기보다 **일반 파서/렉서 도구**임  
  텍스트를 AST로 변환할 뿐, 실제 DSL은 그 위에 정의하는 명세임  
  기능이 많고 복잡하지만, **툴링 생태계가 풍부**하다는 장점이 있음  
  수동 작성보다는 생성된 텍스트 처리에 적합함

- XSD 스키마 검증이 내장되어 있어서 문서의 **정합성을 즉시 확인**할 수 있음  
  자동화 도구를 쓰지 않고 XML이 어렵다고 불평하는 건, 디스어셈블러 없이 바이너리를 다루는 것과 같음

  - 하지만 스키마 검증만으로 **내용의 올바름**을 보장할 수는 없음  
    타입 체크가 프로그램의 정확성을 보장하지 않는 것과 같은 이치임

  - XSD는 유용하지만 복잡하고 제약이 많음  
    그래서 일부 XML 커뮤니티는 RELAX-NG로 옮겨갔지만 완전히 대체되진 못했음

  - 어떤 작업이 XSD 검증을 꼭 필요로 하는지 궁금함

- XML은 마크업 언어로는 괜찮고, 데이터 교환 포맷으로도 쓸 만하지만, **프로그래밍 언어로 쓰면 끔찍함**  
  JSON도 마찬가지로, 데이터 교환에는 좋지만 언어로 쓰면 후회함  
  Ansible처럼 YAML 기반 언어들이 그 예임  
  반면 Lisp의 S-expression은 JSON과 비슷한 구조이면서도 **훌륭한 언어**로 발전했음

- XML의 문제는 XML 자체보다 **좋은 XML을 생성하기 어려운 점**임  
  표준이 복잡하고, 생산자마다 표현 방식이 달라서 일관성이 떨어짐  
  JSON은 이 표준편차가 훨씬 작음  
  금융기관의 XML을 보면 절망감이 들 정도임

  - XML의 문제는 결국 **툴링의 느림과 불완전한 검증기** 때문임  
    데이터 표현의 복잡성보다 도구 품질이 더 큰 병목이었음

  - 사실 문제는 “좋은 XML”보다 “**엉망인 XML**”을 너무 쉽게 만들 수 있었다는 점임  
    그래서 커뮤니티는 네임스페이스, 검증, 변환, 시맨틱 웹 등으로 상호운용성을 확보하려고 노력했음  
    완벽한 합의가 불가능한 환경에서도 일을 진행하기 위한 타협이었음
