- 미국 국세청이 새로운 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 등을
<Add>로 합산 -
환급 불가 크레딧(non-refundable credits) 은 세금 부담을 0까지만 줄일 수 있으며,
<GreaterOf>연산자로 0과 (잠정세액 - 환급불가 크레딧) 중 큰 값을 선택 - 사용자 입력값은
<Derived>대신<Writable>태그를 사용하며,<Dollar/>,<Boolean/>등으로 값 타입을 지정 - 팩트들이 서로 의존하며 최종 세금 수치를 도출하는 그래프 구조
세금 로직에 선언적 명세가 필요한 이유
- JavaScript로 동일한 계산을 작성하면
const totalOwed = totalTax - totalPayments처럼 간결하지만, 이는 명령형(imperative) 방식으로 순차 실행 후 중간 단계가 소실됨 - 의존 관계가 깊어지면 실행 순서 문제가 발생:
getInput()같은 사용자 입력 함수가 이후 계산을 모두 차단하며, 배우자 유무 등에 따라 질문 자체가 달라져야 함 - Social Security 소득 합산 로직에서
map/reduce같은 JavaScript 구현 세부사항이 노출되지만, XML의<CollectionSum>은 세금 수학 개념 자체를 표현-
<Dependency path="/socialSecuritySources/*/totalFederalTaxesPaid"/>로 컬렉션 내 항목을 합산
-
- 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에서는 태그 이름 자체가 객체의 종류를 나타내므로 별도 선언 불필요
- JSON의 유일한 복합 데이터 구조는 객체이므로, 모든 자식 객체가
- 동일한
/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의 팩트는 원천징수 추정용이므로 세금 신고에 직접 사용하면 안 됨