1. 왜 Bedrock 비용 추적이 어려운가

Claude 같은 Foundation Model을 프로덕션에서 쓰면 비용이 얼마나 나올지 궁금해집니다. 특히 Prompt Caching을 켜면 캐시 히트율에 따라 비용 구조가 복잡해지는데, AWS Cost Explorer는 이 정도 세분화를 지원하지 않습니다.

AWS Cost Explorer의 한계

Cost Explorer는 서비스 레벨만 보여줍니다. “Amazon Bedrock"으로 필터링하면 전체 합계는 나오지만, 어떤 모델에 얼마를 썼는지, 일별 트렌드가 어떤지, 캐시 히트율은 몇 퍼센트인지 알 수 없습니다.

Cost Explorer 조회 결과:
- Amazon Bedrock: $5,724.02 (지난 7일)

... 그래서 어떤 모델에 얼마 썼나요?

Bedrock은 토큰 단위로 과금합니다. Opus 4.6과 Sonnet 4.6의 Input 토큰 가격은 5배 차이($15 vs $3)입니다. 모델별로 얼마나 썼는지 모르면 최적화할 수 없습니다.

Prompt Caching 도입 후 복잡도 증가

Prompt Caching을 켜면 비용 구조가 4개로 나뉩니다.

  • Input Tokens: 일반 입력 ($15/1M for Opus)
  • Output Tokens: 생성된 응답 ($75/1M for Opus)
  • Cache Read: 캐시 히트 ($1.50/1M for Opus, 90% 절약)
  • Cache Write: 캐시 생성 ($18.75/1M for Opus, 25% 추가)

캐시 히트율이 99%면 비용이 1/10로 줄어들지만, 캐시가 자주 미스나면 오히려 비싸집니다. 이 수치들을 실시간으로 모니터링해야 하는데, Cost Explorer는 이 레벨까지 들어가지 못합니다.


2. CloudWatch에 숨어있는 Bedrock 메트릭

사실 AWS는 Bedrock 사용량 메트릭을 CloudWatch에 자동으로 보내고 있습니다. 단지 콘솔에서 눈에 잘 띄지 않을 뿐입니다.

AWS/Bedrock 네임스페이스

CloudWatch → Metrics → AWS/Bedrock으로 가면 다음 메트릭들이 보입니다.

CloudWatch 메트릭 조회 플로우 CloudWatch에서 Bedrock 메트릭을 조회하는 3단계 플로우

InputTokenCount: 프롬프트 입력 토큰 수 (Cache Write 제외)

OutputTokenCount: 응답 생성 토큰 수

CacheReadInputTokenCount: 캐시에서 읽은 토큰 수 (캐시 히트)

CacheWriteInputTokenCount: 캐시에 쓴 토큰 수 (캐시 생성)

Invocations: API 호출 횟수

각 메트릭은 ModelId 디멘션으로 필터링 가능합니다. global.anthropic.claude-opus-4-6-v1 같은 정확한 모델 ID로 조회하면 모델별 토큰 사용량이 나옵니다.

boto3가 아닌 AWS CLI 직접 호출

Python boto3 CloudWatch 클라이언트를 쓸 수도 있지만, 의존성을 줄이기 위해 AWS CLI를 subprocess로 호출하는 방법을 택했습니다.

cmd = [
    "aws", "cloudwatch", "get-metric-statistics",
    "--namespace", "AWS/Bedrock",
    "--metric-name", "InputTokenCount",
    "--start-time", "2026-02-27T00:00:00Z",
    "--end-time", "2026-03-06T23:59:59Z",
    "--period", "86400",  # 1일 단위
    "--statistics", "Sum",
    "--dimensions", "Name=ModelId,Value=global.anthropic.claude-opus-4-6-v1",
]
result = subprocess.run(cmd, capture_output=True, text=True)
data = json.loads(result.stdout)

반환 형식은 다음과 같습니다.

{
  "Datapoints": [
    {
      "Timestamp": "2026-03-03T00:00:00Z",
      "Sum": 1234567.0,
      "Unit": "None"
    }
  ]
}

Sum 값이 토큰 수입니다. 이걸 모델별 가격표에 곱하면 비용이 나옵니다.


3. Python CLI 구현 — claude-cost

설계 철학은 간단합니다. Python 표준 라이브러리 + boto3만 쓰고, Docker나 인프라는 필요 없습니다. 터미널에서 바로 실행 가능해야 합니다.

핵심 로직 3단계

비용 계산 로직 플로우 토큰 집계 → 가격표 적용 → 비용 산출 3단계 플로우

1단계: 모델별 토큰 집계

def cw_list_bedrock_models(profile=None, region=None):
    """CloudWatch에서 사용 중인 Bedrock 모델 목록 조회"""
    cmd = [
        "aws", "cloudwatch", "list-metrics",
        "--namespace", "AWS/Bedrock",
        "--metric-name", "Invocations",
    ]
    if profile:
        cmd.extend(["--profile", profile])
    if region:
        cmd.extend(["--region", region])

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
    data = json.loads(result.stdout)

    models = set()
    for m in data.get("Metrics", []):
        for d in m.get("Dimensions", []):
            if d["Name"] == "ModelId":
                models.add(d["Value"])
    return sorted(models)

먼저 list-metrics로 사용 중인 모델 목록을 가져옵니다. 그 다음 각 모델에 대해 5개 메트릭(Input, Output, CacheRead, CacheWrite, Invocations)을 조회합니다.

token_metrics = [
    "InputTokenCount",
    "OutputTokenCount",
    "CacheReadInputTokenCount",
    "CacheWriteInputTokenCount"
]

for model_id in anthropic_models:
    totals = {"input": 0, "output": 0, "cache_read": 0, "cache_write": 0}

    for metric in token_metrics:
        datapoints = cw_get_metric(model_id, metric, start_str, end_str)
        for dp in datapoints:
            val = int(dp.get("Sum", 0))
            if metric == "InputTokenCount":
                totals["input"] += val
            elif metric == "OutputTokenCount":
                totals["output"] += val
            # ... (나머지 메트릭)

2단계: 가격표 적용

Anthropic 공식 가격을 하드코딩합니다. (2026년 3월 기준)

PRICING = {
    "claude-opus-4-6": {
        "input": 15.0, "output": 75.0,
        "cache_read": 1.5, "cache_creation": 18.75,
    },
    "claude-sonnet-4-6": {
        "input": 3.0, "output": 15.0,
        "cache_read": 0.30, "cache_creation": 3.75,
    },
    "claude-haiku-4-5": {
        "input": 0.80, "output": 4.0,
        "cache_read": 0.08, "cache_creation": 1.0,
    },
}

Bedrock ModelId(global.anthropic.claude-opus-4-6-v1)를 짧은 이름으로 매핑하여 가격을 조회합니다.

3단계: 비용 계산

def calc_cost(model, totals):
    p = get_pricing(model)
    cost = (
        totals["input"] * p["input"] / 1_000_000
        + totals["output"] * p["output"] / 1_000_000
        + totals["cache_read"] * p["cache_read"] / 1_000_000
        + totals["cache_write"] * p["cache_creation"] / 1_000_000
    )
    return cost

실행 예시

claude-cost aws 7

출력:

======================================================================
  AWS Bedrock Usage - All Environments (last 7 days)
  Region: us-west-2
======================================================================
  Total Estimated Cost:  $   5724.02 USD
======================================================================

  claude-opus-4-6  (global.anthropic.claude-opus-4-6-v1)
  ──────────────────────────────────────────────────────────────────
    Cost:           $   5679.68  (99%)
    Invocations:        10,462
    Input:           6,885,109 tokens
    Output:          2,962,258 tokens
    Cache Read:     1,229,148,119 tokens  (99% hit)
    Cache Write:    187,227,315 tokens

  claude-sonnet-4-6  (global.anthropic.claude-sonnet-4-6-v1)
  ──────────────────────────────────────────────────────────────────
    Cost:           $     44.34  (1%)
    Invocations:          234
    Input:             421,045 tokens
    Output:            186,712 tokens
    Cache Read:      8,012,334 tokens  (95% hit)
    Cache Write:     1,234,567 tokens

  Daily Trend (all models)
  ──────────────────────────────────────────────────────────────────
  02-27  $  891.74   1882 calls  ████████████████████
  03-03  $ 1292.76   2253 calls  ██████████████████████████████
  03-06  $  383.78    766 calls  ████████

4. 실전 활용 — 비용 인사이트

일별 트렌드로 비용 급증 감지

Daily Trend 섹션은 비용이 갑자기 튀는 날을 찾는 데 유용합니다. 03-03에 $1,292로 치솟았다면, 그날 무슨 작업을 했는지 역추적할 수 있습니다.

daily_cost = defaultdict(float)
for model_id in model_daily:
    p = get_pricing(model_id)
    for day, d in model_daily[model_id].items():
        cost = (
            d["input"] * p["input"] / 1_000_000
            + d["output"] * p["output"] / 1_000_000
            + d["cache_read"] * p["cache_read"] / 1_000_000
            + d["cache_write"] * p["cache_creation"] / 1_000_000
        )
        daily_cost[day] += cost

모델별 비용 비중: Opus vs Sonnet

위 예시에서 Opus가 99%, Sonnet이 1%를 차지합니다. Opus Input 토큰 가격은 $15/1M이고, Sonnet은 $3/1M입니다. 5배 차이입니다.

만약 워크로드의 50%를 Sonnet으로 옮기면:

Before: Opus 100% → $5,679
After:  Opus 50% + Sonnet 50% → ~$1,140 (Opus) + ~$228 (Sonnet) = $1,368
절약액: $4,311 (76% 감소)

물론 Sonnet이 Opus만큼 성능을 낼 수 있는 태스크에 한정됩니다. 단순 요약이나 데이터 추출은 Sonnet으로 충분하지만, 복잡한 추론은 Opus가 필요합니다.

Cache Hit Rate 분석

Cache Read가 1,229M이고, Input이 6.9M이면:

Cache Hit Rate = 1,229 / (1,229 + 6.9) = 99.4%

99% 히트율은 Prompt Caching이 제대로 작동한다는 뜻입니다. 캐시 Read 가격($1.50)이 일반 Input($15)보다 10배 싸기 때문에, 캐시를 쓸수록 비용이 줄어듭니다.

만약 히트율이 50% 미만으로 떨어지면, 프롬프트가 매번 바뀌어서 캐시가 무용지물이거나, TTL이 너무 짧아서 자주 만료되는 것입니다. 이 경우 Prompt Caching을 끄는 게 나을 수 있습니다. Cache Write 비용($18.75)이 일반 Input($15)보다 비싸기 때문입니다.

Profile별 조회로 계정/환경 분리

AWS CLI는 --profile 옵션으로 여러 계정을 관리합니다.

claude-cost aws 30 --profile dev
claude-cost aws 30 --profile prod

dev 환경에서 실험하고, prod 환경에서 실제 비용을 추적할 수 있습니다. 리전도 --region으로 지정 가능합니다.


5. 확장 가능성

cron + Slack 알림으로 일일 비용 리포트 자동화

cron에 등록하여 매일 아침 비용을 Slack으로 보낼 수 있습니다.

# crontab -e
0 9 * * * /usr/local/bin/claude-cost aws 1 | /usr/local/bin/send-to-slack

Slack Incoming Webhook으로 보내면 팀원들이 실시간으로 비용을 확인할 수 있습니다.

import requests
import subprocess

result = subprocess.run(
    ["claude-cost", "aws", "1"],
    capture_output=True,
    text=True
)
requests.post(SLACK_WEBHOOK_URL, json={"text": result.stdout})

예산 임계치 초과 시 경고

일일 비용이 $1,000를 넘으면 경고를 보내는 로직을 추가할 수 있습니다.

daily_cost = parse_output(result.stdout)
if daily_cost > 1000:
    send_alert("Bedrock 비용 초과: $" + str(daily_cost))

팀 단위 비용 분배 (태그 기반)

현재 CloudWatch 메트릭에는 사용자 정보가 없어서 계정 전체 합산만 가능합니다. 팀별로 나누려면 Bedrock API 호출 시 태그를 붙이고, CloudTrail 로그를 파싱해야 합니다.

이 경우 AWS 공식 샘플의 ADOT + Athena 파이프라인이 필요합니다. claude-cost는 개인용 경량 도구이므로, 팀 단위 분석이 필요하면 더 큰 인프라를 고려해야 합니다.


References