
AI에게 작성하게 한 세금 계산 코드를 1차 정보로 검증했더니, 2년 전 세제로 되어 있었다
요약
AI를 활용해 부업 실수령액 계산 도구를 작성했으나, 검증 결과 최신 세제 개정 사항이 반영되지 않은 과거 데이터를 사용하고 있음을 발견했습니다. AI가 생성한 코드의 논리적 완성도는 높지만, 지식의 시점(Snapshot)에 따른 데이터 정확성 검증이 필수적임을 시사합니다.
핵심 포인트
- AI 생성 코드는 작동 여부와 별개로 최신 정보 반영 여부를 반드시 검증해야 함
- AI의 지식은 특정 시점의 스냅샷이므로 법률·세무 등 민감한 분야에 주의 필요
- 코드 내 상수는 수학적 단차를 보정하기 위한 최적화 기법으로 활용됨
- 속산표의 마법 같은 숫자는 누진세 계산을 대수적으로 축약한 결과임
3줄 요약
- AI에게 부업 실수령액 계산 도구를 작성하게 했다.
하룻밤 만에 작동하는 결과물이 나왔다. 솔직히 대단하다 - 다만
돈을 계산하는 것이기에, 무작정 믿지 않고 1차 정보로 검증해 보았다 - 결과,
세제 개정 사항을 반영하지 못하고 있었다 (급여소득공제 55만 → 65만). 작동도 하고 테스트도 통과한다. 다만 **AI의 지식은 "어느 시점의 스냅샷"**이었다.
AI에게 작성하게 하고, 확인해 보았다
회사원을 하면서 부업을 하고 있습니다. 부업의 실수령액을 알고 싶어서 AI에게 계산 도구를 작성하게 했습니다. 소득세, 주민세, 사회보험료로부터 실수령액을 산출하는 도구입니다.
하룻밤 만에 작동하는 결과물이 나왔습니다. 몇 년 전이라면 몇 주가 걸렸을 작업입니다. 솔직히 매번 대단하다고 생각합니다.
다만, 이것은 돈을 계산하는 것입니다. 게다가 세금입니다. 틀리면 보통 곤란해집니다.
그래서 무작정 믿지 않고 내용을 읽고, 1차 정보와 대조해 보기로 했습니다. 이 기사는 그 기록입니다.
읽어보니, 의외로 재미있었다
급여소득공제(給与所得控除)의 상수는 "단차를 흡수하는 장치"였다
function kyuyoKojo(income){
if(income<=1625000) return Math.min(550000, income);
if(income<=1800000) return income*0.4 - 100000;
...
끝부분의 -100000
이나 +80000
이 처음에는 수수께끼였습니다. 하지만 경계값에서 양쪽 식을 계산해 보니 납득이 갔습니다.
연봉 162.5만 엔일 때:
Math.min(550000, 1625000) = 550,000
1,625,000 × 0.4 − 100,000 = 550,000 ← 일치
상수는 구분의 경계에서 단차가 발생하지 않도록 하기 위한 보정값이었습니다. 이것이 없으면 "연봉이 1엔 늘어나면 실수령액이 몇만 엔 줄어드는" 사고가 발생합니다.
참고로 Math.min(550000, income)
도 은근히 역할을 하고 있는데, 이것이 없으면 연봉 55만 엔 미만인 사람의 급여소득이 마이너스가 됩니다. 그 마이너스가 다른 소득을 상쇄하여 세액이 부당하게 낮아집니다. 가드는 발생 지점에 두라는 이야기군요.
속산표(速算表)의 "마법의 숫자"의 정체
소득세는 이렇게 적혀 있었습니다.
if (kazei<=1950000) tax = kazei*0.05;
else if (kazei<=3300000) tax = kazei*0.10 - 97500;
else if (kazei<=6950000) tax = kazei*0.20 - 427500;
97500
이나 427500
이 의미불명했습니다. 누진세라면 본래 브래킷(Bracket)마다 나누어 더해야 합니다.
// 정직하게 쓰면 이렇게
function naive(kazei){
let tax = 0, prev = 0;
...
그런데 속산표는 루프(Loop)가 없습니다. 전액에 최고 세율을 곱하고, 상수(Constant)를 뺄 뿐. 왜 일치하는 걸까요.
계산해 보니 다음과 같았습니다.
공제액 = Σ(경계 × 세율의 단차)
공제(10%) = 1,950,000 × (0.10−0.05) = 97,500 ✓
공제(20%) = 1,950,000 × 0.05 + 3,300,000 × (0.20−0.10) = 427,500 ✓
...
6개의 마법의 숫자가 전부 이 하나의 식에서 도출되었습니다.
즉 속산표는 방금 전의 루프를 대수적으로 축약하여 상수로 만든 것입니다. 종이로 수계산하던 시대에는 루프를 돌릴 수 없었습니다. 그 시대의 최적화가 그대로 코드에 남아 있는 것입니다.
암기해야 하는 표라고 생각했던 것이, 생성 가능한 것이라는 점을 알게 되었습니다.
function buildTable(brackets){
let deduction = 0;
return brackets.map((b, i) => {
...
세율이 개정되어도 brackets를 수정하기만 하면 됩니다. 6개의 상수를 일일이 손으로 고치는 것보다 버그가 발생하지 않습니다.
소박한 의문이 전부를 끌어냈다
주민세 코드에서 문득 걸리는 부분이 있었습니다.
function juminzei(kazeiJumin){
if (kazeiJumin <= 0) return 5000; // 균등할
return Math.round(kazeiJumin * 0.10) + 5000;
...
「소득이 0인 사람이 5,000엔이라면, 소득이 1만 엔인 사람은 1,000엔이 되어 오히려 더 저렴해지는 것 아닌가?」
——결론부터 말하자면, 이것은 저의 오독이었습니다. + 5000이 양쪽 분기(branch) 모두에 있기 때문에 1,000 + 5,000 = 6,000엔이 됩니다. 역전되지는 않습니다.
하지만 「역전이 일어나고 있지 않은가」를 의심한 것 자체가 적중했습니다. 조사해 보니, 현실의 주민세에는 비과세 한도액이 있습니다. 합계 소득이 일정 이하(지자체에 따라 대체로 45만 엔 이하)라면, 균등할(均等割)조차 부과되지 않습니다.
즉, return 5000은 오류이며, 본래 0을 반환해야 했습니다. 내지 않아도 되는 5,000엔을 계산하고 있었던 셈입니다.
그리고, 본론이 등장했다
수정하려면 1차 정보(primary information)를 확인해야 한다고 생각하여 국세청 페이지를 열었습니다.
그곳에서 손이 멈췄습니다.
| 항목 | 레이와 2년~6년분 | 레이와 7년분 이후 |
|---|---|---|
| 최저 보장액 | 550,000엔 | 650,000엔 |
| 경계 | 1,625,000엔 | 1,900,000엔 |
수입 × 40% − 100,000 구분 | 있음 | 소멸 |
코드는 「레이와 2년분~레이와 6년분」의 표 그 자체였습니다.
레이와 7년도 세제 개정(이른바 「연봉의 벽」 대응)을 따르고 있지 않았습니다. 기초 공제(basic deduction)도 일률적으로 48만 엔이었지만, 실제로는 합계 소득에 따른 단계제(95/88/68/63/58만 엔)로 바뀌어 있었습니다.
이 부분이 중요한 지점인데——코드로서의 기능은 아무것도 틀리지 않았습니다. 동작도 하고, 테스트도 통과하며, 에러도 발생하지 않습니다. 로직(logic)은 올바릅니다.
다만, 참조하고 있는 세제가 개정 전의 것이었습니다.
이것은 AI의 결함이 아니라, 성질이라고 생각합니다. AI의 지식은 「어느 시점의 스냅샷(snapshot)」입니다. 그 이후에 국회에서 법률이 바뀌었다는 것을 AI는 알 방법이 없습니다. 인간도 작년의 지식 그대로라면 똑같은 실수를 합니다.
참고로, 40% 구분이 사라진 이유
방금 배운 「연속성」으로 설명이 가능했습니다. 새로운 표의 경계인 190만 엔에서:
최저 보장: 650,000
30% 식: 1,900,000 × 0.3 + 80,000 = 650,000 ← 일치
플랫(flat)한 부분을 「30% 직선이 65만 엔에 도달하는 점(=190만 엔)」까지 늘린 결과, 사이에 있던 40% 구분이 불필요해져 소멸한 것입니다.
표를 통째로 암기하고 있었다면 절대로 깨닫지 못했을 것입니다. 원리를 이해하고 있었기에 개정의 의도까지 읽어낼 수 있었습니다.
수정한 후
1차 정보로 근거를 확인하여 수정하고, 검증되지 않은 부분에는 솔직하게 주석을 남겼습니다.
// ⚠️미검증: 주민세의 기초 공제 43만 엔도 레이와 7년도 개정에서 재검토되었을 가능성 있음 (1차 정보로 미확인)
const kisoJumin = goukeiShotoku<=24000000 ? 430000 : 0;
모르는 것을 「모른다」라고 적혀 있는 코드는, 틀렸다고 단정 짓는 코드보다 안전하다고 생각합니다.
배운 점
1. AI의 지식에는 「시점」이 있다
코드의 로직은 완벽했습니다. 부족했던 것은, 개정 후의 세제를 알지 못했다는 단 한 가지뿐이었습니다. 게다가 그것은 동작만으로는 절대로 알 수 없습니다. 테스트도 통과하고 에러도 나지 않습니다. 그저 조용히 낡았을 뿐입니다.
2. 그래서 「동작했다」 이후에 인간의 확인이 필요하다
특히 돈·세금·법률이 얽힌 것은 더욱 그렇습니다. 이 영역은 AI가 아무리 똑똑해지더라도, 「지금 이 순간의 법률로 올바른가」는 1차 정보를 직접 확인하지 않으면 누구도 보증할 수 없습니다. AI에게 전적으로 맡길 수 없는 영역이 분명히 존재합니다.
3. 가장 효과적이었던 것은 소박한 의문이었다
「소득이 0인 사람보다 소득이 1만 엔인 사람이 더 저렴해지는 것 아닌가?」——이 초보적인 질문이 버그 발견도, 1차 정보 확인도, 개정 사실의 발견도 전부 끌어냈습니다. 역전이나 경계를 의심하는 습관은 세금 코드에서 가장 강력한 무기입니다.
고도의 지식은 필요하지 않았습니다. 「뭔가 이상하지 않아?」라고 생각하고 확인했을 뿐입니다.
4. 읽어보니 의외로 재미있었다
이것은 예상치 못한 수확이었습니다. 속산표(quick calculation table)가 종이 시대의 최적화였다는 점, 40% 구분이 사라진 이유, 상수가 단차를 흡수하고 있다는 점 등. AI에게만 맡겼다면 평생 몰랐을 이야기입니다.
읽는 것은 비용이 아니라 리턴(return)이었습니다.
마치며
최근의 AI는 정말 우수합니다. 하룻밤 사이에 동작하는 도구가 나오고, 로직에도 틀림이 없었습니다. 이것을 「AI는 안 된다」라는 이야기로 만들 생각은 전혀 없습니다.
다만, AI가 알고 있는 것은 「학습한 시점까지」 입니다. 그 이후에 세상이 변했다는 사실은 모릅니다. 그리고 돈이나 법률처럼, 세상이 매년 변하는 영역에서는 그 점이 문제가 됩니다.
그래서, AI에게 작성하게 하고, 인간이 확인한다. 이번에는 그 과정을 통해 버그와 개정 누락을 모두 찾아낼 수 있었습니다. 그리고 확인하는 과정에서 저 자신의 지식도 늘었습니다. 나쁘지 않은 분담이라고 생각합니다.
AI는 앞으로 더욱 똑똑해질 것이라고 생각합니다. 그럼에도 불구하고, 인간의 눈으로 하는 체크는 당분간 사라지지 않을 것이라는 느낌이 듭니다. 잘 협력하며 나아가고 싶습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기