30일 챌린지 오픈 알림 받기
← 모든 가이드

업무 자동화

홈택스 안 들어가고 세금계산서 발행하기: 볼타 + Claude 스킬 설치 가이드

Claude
지금 다 못 읽겠다면 · 이 가이드 링크를 메일로 받기

목차 · 12개 섹션

세금계산서는 잘못 발행하면 되돌릴 수 없다. 국세청 수정발행 절차를 밟아야 하고 기록이 남는다.

그런데 홈택스에서 한 장 끊는 데 걸리는 시간이 길다. 인증서를 찾고, 팝업을 세 번 닫고, 그 사이 로그인이 풀린다. 월말에 거래처가 열 곳이면 이걸 열 번 반복한다.

이 글은 그 과정을 Claude에게 넘기는 방법이다. 설치부터 첫 발행까지 따라 하면 된다.

무엇을 만드나

"고객사 주식회사에 9월 이용료 50만원 세금계산서 발행해줘"

이렇게 말하면 Claude가 검증하고, 내용을 보여주고, 확인을 받은 뒤 발행한다. 국세청 전송까지 간다.

중간에 승인 단계가 있다는 게 핵심이다. 확인 없이 바로 나가는 구조였다면 쓰지 않았을 것이다.

준비물

항목설명
볼타(Bolta) 계정전자세금계산서 ASP. 가입 후 API 키 발급
Claude Code 또는 Hermes스킬을 읽고 스크립트를 실행할 도구
Node.js 18+라이브러리 빌드용

볼타는 국세청에 전송해주는 중개 사업자다. 홈택스를 직접 치는 게 아니라 이쪽 API를 쓴다.

1. 설치

sdkfilekorean-tax-invoiceRepository

MIT 라이선스, 공개 레포다.

# Claude Code
git clone https://github.com/sdkfile/korean-tax-invoice \
  ~/.claude/skills/korean-tax-invoice

# Hermes
git clone https://github.com/sdkfile/korean-tax-invoice \
  ~/.hermes/skills/korean-tax-invoice

2. 빌드 — 건너뛰면 동작하지 않는다

cd ~/.claude/skills/korean-tax-invoice
npm install && npm run build

스크립트가 dist/를 참조하기 때문에 이 단계가 필수다. 잊으면 발행과 상태 조회가 실패한다.

3. 키 설정 — test_로 시작하라

export BOLTA_API_KEY=test_xxxxx    # 볼타 개발자센터 > API 키

키 접두사가 환경을 가른다.

접두사동작
test_가상 발행. 국세청에 가지 않는다
live_실제 발행. 되돌릴 수 없다

처음에는 무조건 test_로 시작한다. .env 파일에 넣어도 되고, 리포의 .gitignore가 .env를 제외한다.

4. 점검

node scripts/preflight.mjs

“발행 준비 완료”가 나오면 끝이다. 이제 Claude에게 이렇게 말하면 된다.

볼타로 세금계산서 발행하려는데 준비됐는지 봐줘

5. 첫 발행

사용자: 고객사 주식회사에 9월 이용료 50만원 세금계산서 발행해줘

Claude: (preflight → 요청 파일 작성 → 검증 실행)

        공급받는자  고객사 주식회사 (220-81-62517)
        받는사람    tax@client.co.kr
        공급가액    500,000원
        세액         50,000원
        합계        550,000원
        환경        테스트 (가상)

        이 내용으로 발행할까요?

사용자: 응

Claude: (--confirm 으로 발행 → issuanceKey 확인 → 상태 조회)

터미널에서 직접 하면 이렇다.

node scripts/issue.mjs examples/request.json            # 검증만 (발행 안 함)
node scripts/issue.mjs examples/request.json --confirm  # 실제 발행
node scripts/check-status.mjs <issuanceKey>             # 결과 확인

--confirm이 없으면 아무것도 발행되지 않는다. 실수로 실행됐을 때 아무 일도 일어나지 않아야 하기 때문이다.

실제로 터지는 세 가지

이 라이브러리를 만든 이유다. 일반 API 호출처럼 다루면 아래에서 사고가 난다.

타임아웃 후 재시도 → 중복 발행

ASP가 응답을 안 주면 보통 재시도한다. 그런데 요청은 이미 접수됐을 수 있다. 같은 계산서가 두 장 나간다.

그래서 “거절됨”과 “모름”을 구분한다. 후자는 재요청하지 않고 조회부터 한다.

try {
  const { issuanceKey } = await provider.submit(request, refId)
} catch (e) {
  if (e instanceof ProviderUncertainError) {
    // 접수 여부 불명. 재요청하면 중복 발행이다.
    const status = await provider.getStatusByReferenceId(refId)
    // SUBMITTED → 이미 접수됨 / UNKNOWN → 접수 안 됨
  }
}

작성일자 하루 밀림 → 부가세 신고 어긋남

서버가 UTC면 KST 자정 근처에서 전날이 찍힌다. 작성일자는 과세기간을 가르는 값이라, 월말 발행분이 전월로 넘어가면 신고가 틀어진다.

필수값 누락을 빈 문자열로

대표자명이 없다고 ""를 보내면 ASP가 받아주는 경우가 있다. 그대로 국세청에 간다.

validateIssueRequest()가 발행 전에 막는다. 사유는 한국어 배열로 오니 승인 화면에 그대로 띄울 수 있다.

라이브로 넘어갈 때

⚠️ 라이브는 테스트와 완전히 별도 환경이다. 키만 바꾸면 안 된다. 세 가지를 다시 해야 한다.

  1. 발급자 등록
  2. 공동인증서 등록
  3. 웹훅 URL 등록

references/bolta-setup.md에 절차가 있다.

첫 실전 발행은 자기 회사끼리 소액으로 해보길 권한다. 잘못돼도 남에게 가지 않는다.

코드로 붙일 때

SaaS 월 청구나 정산 자동화에 넣는 경우다. 핵심은 프로바이더를 부르기 전에 잠그는 것이다.

const check = validateIssueRequest(request, { verified: company.verifiedAt !== null })
if (!check.ok) {
  return { blocked: check.issues.map((i) => i.message) }
}

const provider = new BoltaProvider()
const refId = buildClientReferenceId('myapp', invoice.id)

await db.markSubmitted(invoice.id)   // 프로바이더 호출 *전에* 잠근다

const { issuanceKey } = await provider.submit(request, refId)
await db.saveIssuanceKey(invoice.id, issuanceKey)

접수(SUBMITTED)와 발행(ISSUED)은 다른 상태다. 섞으면 아직 안 끝난 건을 완료로 처리하게 된다.

설계 패턴은 references/integration.md에 정리해 뒀다.

다른 프로바이더

프로바이더상태
볼타 (Bolta)구현됨. 실제 발행 검증 완료
팝빌인터페이스만
바로빌인터페이스만

팝빌·바로빌은 인터페이스만 있고 구현이 없다. 있는 척하지 않으려고 표에 그대로 적었다.

새로 붙이려면 src/core/types.ts의 TaxInvoiceProvider를 구현하면 된다. 검증·사업자번호·KST 날짜 같은 핵심 로직은 프로바이더와 무관하게 동작한다.

세무 판단은 대신해주지 않는다

이 도구는 발행 API 연동을 돕는다. 과세 유형, 작성일자, 수정발행 사유 같은 세무 판단은 사용자 책임이다. 확실하지 않으면 세무 전문가에게 확인하라.

자동화가 대체하는 건 클릭과 대기 시간이지 판단이 아니다.

FOR TEAMS

이 가이드, 우리 팀에 적용하려면?

회사명과 업무 한 줄만 남겨주시면 널포인터스튜디오가 직접 검토해 회신드립니다.