청구서는 커졌는데, 무엇을 만든 비용인지는 보이지 않습니다

AI 코딩 도구 비용이 커지면 모델별 사용액이나 사용자별 순위부터 열어보게 됩니다. 하지만 그 표만으로는 결제 개편에 얼마를 썼는지, 장애 대응과 취소된 실험에 비용이 얼마나 들어갔는지 설명하기 어렵습니다.

제목의 ‘월 15만 달러’는 검증된 Navigara 고객 청구액이나 절감 실적이 아닙니다. Navigara 공동창업자가 CTO 시절 CFO에게 “Claude가 매달 거의 15만 달러의 실질 가치를 만드는가”라는 질문을 받았다고 소개한 일화입니다. 회사·기간·사용자 수·청구 증빙은 공개되지 않았으므로 문제의 크기를 보여주는 질문으로만 봐야 합니다.

그래도 질문의 방향은 유효합니다. 어떤 모델에 돈을 썼는지가 아니라, 그 돈이 어느 제품 목표로 흘러가 어떤 결과를 만들었는지 보여줘야 예산을 늘릴지, 줄일지, 작업 방식을 고칠지 결정할 수 있으니까요.

먼저 한 달치 사용원가를 빠짐없이 회수하세요

첫 결과물은 거대한 대시보드가 아니라 Claude Platform 한 달치 비용을 끝까지 가져온 원장입니다. 아래 절차는 조직의 Admin API 키와 x-api-key 헤더를 사용하는 경로로 한정합니다. OAuth 토큰을 쓰는 조직은 인증 헤더가 달라지므로 이 명령을 그대로 사용하면 안 됩니다. 개인 계정·Claude Enterprise·Claude Platform on AWS에도 같은 API 경로를 적용할 수 없습니다.

  1. 공식 문서에서 대상 계정인지 확인합니다. Usage and Cost API 문서를 열어 현재 조직과 자격 증명이 지원되는지 먼저 확인하세요.
  2. curl, jq, Python 3를 준비합니다. 터미널에서 아래 확인 명령을 실행하세요. 빠진 도구가 있다면 관리형 기기의 승인된 소프트웨어 경로나 운영체제에 맞는 패키지 관리자를 사용해 설치한 뒤 세 명령이 모두 버전을 출력하는지 다시 확인합니다.
curl --version
jq --version
python3 --version
  1. 조직의 Admin API 키를 현재 셸에 불러옵니다. Claude Console에서 조직 관리자가 발급한 Admin API 키를 준비하고, 조직이 승인한 비밀 관리 도구를 이용해 ANTHROPIC_ADMIN_KEY 환경변수에 주입하세요. 키 값을 스크립트·원장·공유 문서에 직접 적지 않습니다.
  2. UTC 기간과 저장 폴더를 실제 대사 대상에 맞춥니다. 아래 값은 2026년 8월을 조회하는 설명용 입력입니다. 이 글은 인증된 조직에서 명령을 실행해 응답을 재현하지 않았습니다.
  3. 스크립트를 실행해 모든 페이지를 고유 파일로 보존합니다. Cost API의 기본 한도는 7개 버킷이고 최대값은 31개입니다. 스크립트는 limit=31을 요청하되, has_more=true이면 next_page를 자동으로 전달합니다.
set -eu
: "${ANTHROPIC_ADMIN_KEY:?Load an Anthropic Admin API key first}"

STARTING_AT='2026-08-01T00:00:00Z'
ENDING_AT='2026-09-01T00:00:00Z'
REPORT_DIR='./claude-cost-2026-08'
mkdir -p "$REPORT_DIR"

page=''
page_number=1
while :; do
  output_file=$(printf '%s/page-%03d.json' "$REPORT_DIR" "$page_number")
  if [ -n "$page" ]; then
    curl --fail-with-body --silent --show-error --get \
      'https://api.anthropic.com/v1/organizations/cost_report' \
      -H "x-api-key: ${ANTHROPIC_ADMIN_KEY}" \
      -H 'anthropic-version: 2023-06-01' \
      --data-urlencode "starting_at=${STARTING_AT}" \
      --data-urlencode "ending_at=${ENDING_AT}" \
      --data-urlencode 'limit=31' \
      --data-urlencode 'group_by[]=workspace_id' \
      --data-urlencode 'group_by[]=description' \
      --data-urlencode "page=${page}" > "$output_file"
  else
    curl --fail-with-body --silent --show-error --get \
      'https://api.anthropic.com/v1/organizations/cost_report' \
      -H "x-api-key: ${ANTHROPIC_ADMIN_KEY}" \
      -H 'anthropic-version: 2023-06-01' \
      --data-urlencode "starting_at=${STARTING_AT}" \
      --data-urlencode "ending_at=${ENDING_AT}" \
      --data-urlencode 'limit=31' \
      --data-urlencode 'group_by[]=workspace_id' \
      --data-urlencode 'group_by[]=description' > "$output_file"
  fi

  jq -e '.data and (.has_more | type == "boolean")' "$output_file" >/dev/null
  has_more=$(jq -r '.has_more' "$output_file")
  [ "$has_more" = 'true' ] || break
  page=$(jq -r '.next_page // empty' "$output_file")
  [ -n "$page" ] || { echo 'has_more=true but next_page is missing' >&2; exit 1; }
  page_number=$((page_number + 1))
done

python3 - "$REPORT_DIR" <<'PY'
from decimal import Decimal
from pathlib import Path
import json, sys

files = sorted(Path(sys.argv[1]).glob('page-*.json'))
amount_cents = Decimal('0')
buckets = []
for path in files:
    payload = json.loads(path.read_text())
    for bucket in payload['data']:
        buckets.append((bucket['starting_at'], bucket['ending_at']))
        for result in bucket['results']:
            if result['currency'] != 'USD':
                raise SystemExit(f"Unexpected currency: {result['currency']}")
            amount_cents += Decimal(result['amount'])
print(f'pages={len(files)}')
print(f'buckets={len(buckets)}')
if buckets:
    print(f'range={min(x[0] for x in buckets)}..{max(x[1] for x in buckets)}')
print(f'amount_cents={amount_cents}')
print(f'amount_usd={amount_cents / Decimal("100")}')
PY

성공 기준은 요청이 오류 없이 끝나고, 마지막 응답의 has_more가 false이며, 출력된 버킷 범위가 의도한 UTC 기간을 덮는 것입니다. 각 페이지가 page-001.json처럼 따로 남고 amount_cents와 amount_usd도 출력돼야 합니다. 인증 오류, 잘못된 JSON, 빠진 next_page, 예상하지 않은 통화가 나오면 원가 배분으로 넘어가지 마세요.

Cost API의 amount는 달러가 아니라 USD 센트 단위의 decimal string이며 소수 센트가 생길 수 있습니다. 그래서 스크립트는 이진 부동소수점 대신 Python의 Decimal로 합산한 뒤 100으로 나눕니다. 센트 합계가 18420이면 사용원가는 USD 184.20입니다. Priority Tier 비용은 이 API에 포함되지 않으므로 별도 비용 행으로 남겨야 합니다.

카드 결제액과 사용원가는 다른 장부입니다

선불 Claude 계정에서는 그달 Cost API 합계를 그달 카드 결제액과 직접 맞추면 안 됩니다. 대부분의 Claude Console 조직은 usage credits를 먼저 구매하며, 잔액이 설정한 기준 아래로 내려가면 자동 충전할 수 있습니다. 8월 카드 결제는 8월에 사용한 비용이 아니라 앞으로 쓸 크레딧 구매일 수 있습니다.

계정 방식Cost API와 먼저 비교할 값결제 기록 처리
선불 크레딧같은 UTC 기간의 Console Usage/Cost 기록수동·자동 충전을 크레딧 구매로 별도 기록
월 후불 계약같은 기간의 사용료 명세·인보이스세금·할인·크레딧·조정액을 별도 행으로 분리

선불 계정은 기초 크레딧 잔액 + 기간 중 구매액 ± 조정액 − 기간 중 사용액 = 기말 크레딧 잔액으로 흐름을 확인하세요. Cost API 사용액은 같은 기간의 Console Usage/Cost 기록과 먼저 비교하고, 카드 결제·수동 충전·자동 충전은 구매 행으로 둡니다. 차이가 남으면 UTC 기간 경계, Priority Tier 같은 API 제외 범위, 크레딧 만료·할인·세금·기타 조정을 확인해야 합니다.

비용 회수가 끝났다는 신호는 카드 결제액과 API 합계가 우연히 같아지는 것이 아닙니다. 전체 페이지가 보존됐고, 센트가 정확히 변환됐으며, 사용원가가 같은 기간의 Console 기록과 설명 가능하게 맞고, 크레딧 구매와 사용이 서로 다른 행으로 보이면 됩니다.

원본 비용과 PR별 배분액을 분리하세요

공급자 비용을 대사했다면 다음 열로 원장을 만드세요.

원본 비용 행 ID · 기간 · 공급자 · 제품 · 원본 금액 · 배분액 · 워크스페이스 · 저장소 · PR · 이슈 · 이니셔티브 · 연결 근거 · 귀속 상태 · 출시 상태 · 품질 결과

Claude Platform Cost API는 비용을 일 단위로 조회하고 workspace_id와 description으로 그룹화할 수 있지만 PR·이슈·로드맵 식별자는 제공하지 않습니다. 공급자 API는 청구 대사의 출발점이지 완성된 로드맵 원가표가 아닙니다.

출발 데이터확인할 수 있는 것추가로 연결할 기록
공급자 비용제품·워크스페이스별 사용원가코딩 도구 세션 또는 저장소
저장소·PR실제 코드 변경브랜치·PR 본문의 이슈 키
이슈·에픽작업 목적과 담당 범위이니셔티브·로드맵
로드맵 항목목표별 배분 원가출시·재작업·장애 결과

특정 워크스페이스가 결제 개편에만 쓰였다는 승인 기록이 있다면 그 비용은 이니셔티브 수준에서 직접 귀속할 수 있습니다. 그렇다고 월간 워크스페이스 비용 전부를 특정 PR 한 건의 직접비로 적을 수 있는 것은 아닙니다. PR별 비용을 만들려면 세션 비용과 세션 ID가 함께 기록돼 있거나, 사전에 합의한 배분 기준이 추가로 필요합니다.

Navigara도 AI 지출을 작업 유형과 로드맵 정렬 여부로 분류하고 이니셔티브·에픽·티켓·코드 변경에 연결하는 제품 개념을 제시합니다. 다만 공개 페이지에는 개별 공급자 비용 이벤트를 세션·PR에 조인하는 규칙이나 여러 이슈가 섞인 세션의 충돌 처리 방식이 충분히 설명돼 있지 않습니다. 화면의 지출액과 절감률도 독립적으로 검증된 고객 성과로 해석하면 안 됩니다.

확인한 비용과 추정한 비용을 섞지 마세요

쓸 만한 원가표는 정교해 보이는 표가 아니라 어디까지 확인했고 어디부터 추정했는지 드러나는 표입니다.

  • 직접 귀속: 특정 이니셔티브 전용 워크스페이스나 비용이 함께 기록된 세션 ID처럼 명시적인 근거가 있는 비용
  • 추정 귀속: 사용자·저장소·시간대·변경 파일 등을 기준으로 나눴지만 직접 비용 식별자가 없는 비용
  • 공통비: 공유 좌석과 공용 인프라처럼 한 이니셔티브에 직접 넣기 어려운 비용
  • 미분류: 연결 근거도 합의된 배분 기준도 없는 비용

다음 숫자는 모두 계산 방식을 보여주는 설명용 입력 예시이며 실제 고객 비용이나 성과가 아닙니다. 원본 비용이 USD 184.20이고 전용 워크스페이스 기록으로 결제 개편에 연결됐다면, PR은 비운 채 ‘이니셔티브 수준 직접 귀속’으로 기록할 수 있습니다.

이 금액을 합의된 변경 파일 비율에 따라 PR #842에 USD 110.52, #857에 USD 73.68로 나눴다면 두 행은 ‘추정 배분’입니다. 같은 원본 비용 행 ID를 보존하고 두 배분액의 합계가 USD 184.20을 넘지 않는지 검사하세요. 근거가 없다면 PR 열을 비운 채 이니셔티브 수준 비용으로 유지하는 편이 정확합니다.

로드맵 미연결 비용을 곧바로 낭비로 판정하지 마세요. 보안 패치, 장애 대응, 리팩터링과 탐색 실험은 필요하지만 계획된 기능 목록에는 없을 수 있습니다. Navigara도 로드맵 정렬 작업과 정당한 비로드맵 작업을 구분하는 개념을 제시합니다. 미연결 항목은 삭제 대상이 아니라 목적을 확인할 검토 대기열입니다.

비용의 분모에는 출시와 품질을 넣으세요

PR 수나 커밋 수만으로 비용 대비 생산성을 계산하면 활동량 증가를 가치 증가로 오해할 수 있습니다. Navigara 역시 PR이 곧 가치 단위는 아니라는 문제를 제기하지만, 제품이 제시하는 자체 점수를 독립적으로 검증된 표준 지표로 볼 수는 없습니다.

DORA는 측정 목적에 맞는 프레임워크를 고르고 시스템 로그와 자기보고 데이터가 보여주는 범위와 한계를 함께 해석해야 한다고 설명합니다. 따라서 로드맵 원가표에는 금액뿐 아니라 결과·품질·귀속 신뢰도를 나란히 두는 편이 좋습니다.

원가
직접비·배분비·미분류액
결과
출시 여부·계획 대비 시점
품질
재작업·장애·리뷰 시간
신뢰도
직접·추정·공통비·미분류

GitHub Copilot 데이터를 더할 때도 범위를 기록하세요. GitHub는 조직·엔터프라이즈·저장소·사용자 수준의 사용 지표와 PR 활동을 제공하지만 좌석 정보는 별도 API에서 확인합니다. 조직 수치는 실제 작업 위치가 아니라 멤버십을 기준으로 귀속될 수 있어 한 사용자의 활동이 여러 조직에 나타날 수 있으며, 서로 다른 Copilot API 자원을 직접 비교해서도 안 됩니다.

“결제 개편에 2만 달러”보다 “이니셔티브 수준 직접 귀속 1만4천 달러, 공통비 배분 4천 달러, PR별 추정 2천 달러, 계획보다 2주 빠르게 배포, 재작업 3건”이라는 보고가 더 유용합니다. 이 숫자 역시 설명용 예시입니다. 중요한 것은 가장 비싼 모델부터 끄는 게 아니라 미분류율과 재작업이 함께 높은 작업 흐름을 찾는 것입니다.

  1. 공급자 사용원가를 대사합니다. 전체 페이지와 기간 범위, 센트 변환을 확인하고 같은 UTC 기간의 Console Usage/Cost 기록과 비교합니다.
  2. 구매와 사용을 분리합니다. 선불 크레딧 구매를 사용원가와 별도 행에 두고 기초·기말 잔액과 구매·조정·사용 흐름을 연결합니다.
  3. 모든 비용에 귀속 상태를 붙입니다. 직접 귀속·추정·공통비·미분류 중 하나를 표시하고 연결 근거를 남깁니다.
  4. 배분액 초과를 검사합니다. 같은 원본 비용 행 ID를 가진 하위 행의 합계가 원본 금액을 넘지 않는지 확인합니다.
  5. 출시와 품질을 함께 봅니다. 비용 변화와 출시 시점·재작업·장애가 함께 움직였다는 이유만으로 AI의 인과 효과라고 단정하지 않습니다.

모든 비용을 억지로 PR까지 내려보낼 필요는 없습니다. 확인 가능한 범위까지만 연결하고 나머지는 추정과 미분류로 정직하게 남기세요. 그래야 월 총액이 공포스러운 청구서에서 개선할 작업 흐름을 알려주는 원가 데이터로 바뀝니다.