LLM 비용 모니터링과 알림: 실전 구축 가이드
시리즈: toksum.dev 가이드 · 공식 공급자 가격 페이지 기준 검증
눈에 보이지 않는 청구서
프로바이더 인보이스가 알려주는 건 딱 하나, 이번 달에 얼마를 내야 하는지뿐이다. 어떤 기능이 그 돈을 썼는지, 어떤 고객이 비용을 끌어올렸는지, 어제 찍힌 숫자가 정상 범위인지는 전혀 알 수 없다. 인보이스가 도착할 즈음이면 돈은 이미 나갔고, 그 비용을 일으킨 스파이크는 2주 전 일이라 다시 추적하는 것조차 불가능하다. 해법은 사후에 멋들어진 대시보드를 얹는 게 아니다. 모든 단일 요청에 대해, 다른 무엇보다 먼저 올바른 필드를 로깅해서 모든 1달러를 트래픽의 한 줄까지 되짚을 수 있게 만드는 것이다.
이 가이드의 모든 내용은 하나의 결정 위에 세워진다. 요청 단위로 계측하고, (달러가 아니라) 토큰 수를 저장하며, 나중에 비용을 잘게 쪼개 볼 수 있을 만큼 충분한 맥락을 각 요청에 태그로 붙인다는 것이다. 이것만 제대로 해두면 예산, 알림, 이상 탐지, 고객별 수익성이 모두 같은 데이터에서 자연스럽게 따라 나온다. 반대로 이걸 놓치면 영원히 월간 PDF를 역설계하는 신세가 된다.
이 글은 의도적으로 벤더 중립적이다. OpenAI를 호출하든, Anthropic, Gemini, 혹은 이들을 섞어 쓰든 동일한 스키마가 그대로 작동한다. 그리고 두 번째 프로바이더를 추가하는 날 — 임시방편식 비용 추적이 조용히 무너지는 바로 그날 — 에도 이 스키마는 살아남는다.
모든 요청에서 무엇을 로깅할 것인가
LLM 클라이언트를 한 번 래핑하고, 매 호출 후 구조화된 이벤트를 내보내라. 이벤트 자체는 저렴하다 — 수백 바이트 수준 — 그리고 이것이 다운스트림 모든 분석의 원재료가 된다. 타협 불가능한 필드는 다음과 같다:
- request_id — 직접 생성하는 UUID. 이 이벤트를 애플리케이션 로그 및 트레이스와 조인할 수 있게 해준다.
- model — 친근한 별칭이 아니라 정확한 모델 문자열(
claude-sonnet-4-6,gpt-5-4). 별칭은 바뀌기 마련이고, 가격을 매긴 대상은 실제 청구된 모델이다. - input_tokens, output_tokens — API 응답의 usage 객체에서 읽어와라. 절대 사후에 추정하지 마라.
- cache_read_tokens, cache_write_tokens — 프로바이더가 이 둘을 따로 보고하고 가격도 따로 매긴다. 이를
input_tokens에 합쳐 넣으면 캐시 ROI를 볼 수 있는 능력이 사라진다. - feature —
summarize,chat,rerank같은 안정적인 태그. 비용 귀속에서 단연 가장 레버리지가 큰 필드다. - tenant_id / 고객 id — 이 요청이 누구를 위한 것이었는지.
- latency_ms — 실제 경과 시간(wall-clock). 여기서 발생하는 스파이크는 종종 비용 스파이크(재시도, 긴 생성)의 전조다.
- retry_count — 하나의 논리적 요청이 API를 몇 번 때렸는지. 폭주하는 재시도는 인보이스를 놀라게 하는 고전적인 원인이다.
- finish_reason —
stop이냐length냐.length로 인한 절단이 쏟아진다면 최대 출력 한도에 돈을 내고 있다는 뜻이다.
토큰 필드가 달러 총액보다 나은 이유
본능적으로는 cost_usd를 로깅하고 끝내고 싶을 것이다. 그 유혹을 참아라. 달러 값은 호출 시점에 참이라고 믿었던 가격을 박아 넣은 파생값이다. 프로바이더가 단가를 바꾸는 순간, 저장해 둔 모든 과거 달러 값은 이제 영원히 바로잡을 수 없는 허구가 된다. 그 값을 만들어낸 원본 토큰 수를 이미 버렸기 때문이다.
토큰을 저장하라. 달러는 쿼리 시점에 가격 설정(아래에서 다룬다)을 기준으로 계산하라. 그러면 단가 변경은 한 줄짜리 설정 수정으로 끝나고, 전체 히스토리가 올바르게 재계산된다. 토큰 수는 또한 엔지니어링 질문에 답할 수 있게 해주는 유일한 필드다. 출력 토큰 스파이크는 장황함이나 프롬프트 회귀를 가리키고, 입력 토큰 스파이크는 컨텍스트 비대화나 폭주하는 대화 이력을 가리키며, 캐시 읽기 토큰의 붕괴는 캐싱 버그를 가리킨다. 달러 총액은 이 중 무엇도 알려주지 않는다 — 그냥 올라갈 뿐이다.
구체적으로 보자. 입력 8,000 토큰, 출력 1,200 토큰의 Claude Sonnet 4.6 요청은 8000 × $3.00/1M + 1200 × $15.00/1M = $0.024 + $0.018 = $0.042 가 든다. 저장할 것은 8000과 1200이다. $0.042 는 영속화하는 값이 아니라 다시 계산해내는 값이다.
비용 귀속: 기능 태그와 고객 태그
전체 합산 비용은 허영 지표다. 실제로 행동을 이끌어내는 뷰는 feature별, tenant_id별로 묶은 비용이다. LLM 지출은 거의 항상 극단적으로 한쪽으로 쏠리기 때문이다. 트래픽의 5%가 비용의 50%를 일으키는 일은 흔하다 — 200K 토큰 컨텍스트로 채팅 기능을 돌리는 단 한 명의 파워유저 테넌트, 혹은 같은 문서를 매시간 다시 요약하는 백그라운드 작업 하나 같은 식이다. 이름 붙일 수 없는 것은 고칠 수도 없다.
기능 태그는 "돈이 어디로 가는가?"에 답한다. chat이 지출의 70%를 차지한다면, 최적화 노력은 거기에 들어가야지 아무리 머리를 굴려도 한 달에 $11밖에 안 드는 title-suggest 기능에 들어가서는 안 된다. 테넌트 태그는 더 날카로운 질문에 답한다. 누가 적자를 내고 있는가?
고객별 단위 경제(unit economics)를 따져보라. 시트 하나가 월 $20에 팔린다고 하자. 테넌트 A는 GPT-5.4에서 요청당 입력 약 2,500 + 출력 600 토큰으로 월 400회 요청한다: 400 × (2500 × $2.50/1M + 600 × $15.00/1M) = 400 × ($0.00625 + $0.009) = 400 × $0.01525 = $6.10. 건강하다 — 매출의 30%다. 테넌트 B는 같은 기능을 12,000 토큰 컨텍스트로 월 9,000회 두들긴다: 9000 × (12000 × $2.50/1M + 600 × $15.00/1M) = 9000 × ($0.030 + $0.009) = 9000 × $0.039 = $351. 이 테넌트는 $20을 내고 $351의 비용을 일으킨다. 테넌트별 태그가 없으면 테넌트 B는 보이지 않는다 — 멀쩡해 보이는 혼합 COGS에 평균값으로 녹아들면서, 흑자 고객 40명의 마진을 조용히 갉아먹는다.
이것이 눈에 보이기 시작하면 선택지가 생긴다. 사용량 상한, 종량제 초과 과금, 헤비 테넌트를 더 저렴한 모델로 라우팅, 공격적인 캐싱 등이다. 하지만 전제 조건은 태그다. 필요해지기 전에 미리 붙여라. 이미 보내버린 요청에 비용 귀속 정보를 소급해서 채워 넣을 수는 없다.
토큰을 돈으로 환산하기: 가격 설정 계층
단가를 애플리케이션 코드에 하드코딩하지 마라. 상수로 박힌 가격은 시한폭탄이다. 프로바이더의 단가 개정이 조용히 모든 추정치와 알림을 틀리게 만든다 — 에러도, 배포도, 신호도 없이, 그저 인보이스가 바로잡아 줄 때까지 숫자가 서서히 어긋날 뿐이다. 단가는 코드를 배포하지 않고도 수정할 수 있는 설정 계층에 두어라. 데이터베이스 테이블, 시작 시 가져오는 JSON 파일, 혹은 작은 가격 서비스 같은 것이다.
구조는 간단하다 — 모델과 단가 유형을 키로 한다: { "claude-sonnet-4-6": { "input": 3.00, "output": 15.00, "cache_write": 3.75, "cache_read": 0.30 } }, 모두 1M 토큰당 단가다. 그러면 비용 함수는 cost = (input_tokens × rate.input + output_tokens × rate.output + cache_read_tokens × rate.cache_read + cache_write_tokens × rate.cache_write) / 1e6 가 된다. 함수 하나로 모든 모델, 모든 과거 row를 처리한다.
세 가지 규칙이 이 체계를 정직하게 유지해준다. 첫째, 들어오는 모든 단가를 1M 토큰당 기준으로 정규화하라. 그래야 단위가 섞이지 않는다 — 1K당과 1M당을 헷갈리는 실수는 1,000배짜리 과금 오류다. 둘째, 설정을 버전 관리하고 각 비용 계산에 설정 버전을 찍어두어라. 그래야 특정 숫자가 어떤 단가로 산출됐는지 감사할 수 있다. 셋째, 네 가지 단가 유형을 일급 시민으로 다뤄라. 표준 입력/출력, 캐시 읽기, 캐시 쓰기는 진짜로 서로 다른 가격이다. Anthropic의 캐시 쓰기 프리미엄(Sonnet 4.6 기준 쓰기 $3.75/1M, 이후 읽기 $0.30/1M) 대 OpenAI/Gemini의 쓰기 수수료 없는 모델 차이를 생각하면, 혼합 단가는 당신에게 거짓말을 하게 된다.
프로바이더 가격 페이지의 각 단가가 실제로 무엇을 의미하는지 — 입력/출력 비대칭, 배치 컬럼, 캐시 수명 등 — 에 대한 전체 분석은 LLM 가격표 읽는 법을 참고하라. Claude Sonnet 4.6 vs GPT-5.4 같은 비교 페이지에는 설정의 시드로 삼을 수 있는 최신 단가 카드가 유지되고 있다.
예산, 상한, 그리고 킬 스위치
알림은 단순 총액이 아니라 변화율을 기준으로 해야 한다. 월간 임계값은 28일째에야 울리는데, 그건 너무 늦다. 번레이트(burn rate)를 추적하라. 시간당 또는 일당 지출을, 남아 있는 예산과 비교하는 것이다. 쓸모 있는 알림은 "현재 일일 소진 속도로는 이번 달 예산을 19일에 초과한다"이며, 어제의 태그된 지출로부터 매일 아침 계산된다. 사후 부검 대신 대응할 11일을 벌어준다.
소프트부터 하드까지 세 단계 통제를 겹쳐 쌓아라.
킬 스위치는 거의 실시간에 가까운 데이터가 필요하다. 야간 배치보다 수 초 안에 빠른 저장소에 도착하는 요청별 이벤트가 더 중요한 이유가 바로 이것이다. 이 지연 수준에서 정확한 회계가 필요한 건 아니다. 대략적인 단가로 분당 토큰 수를 세는 것만으로도 4급 화재를 감지하기엔 충분하다.
- 번레이트 알림(소프트). 일간·주간 단위로, 오늘 지출이 기간 예산을 넘어설 것으로 예측되면 온콜 채널에 알린다. 기능별 임계값은 전체 숫자는 별로 움직이지 않는데 특정 기능 하나만 회귀하는 경우를 잡아낸다.
- 테넌트별 상한(미디엄). 테넌트당 월간 토큰 또는 달러 한도. 80%에서 계정 담당자에게 알리고, 100%에서는 우아하게 성능을 낮춘다 — 큐잉, 스로틀, 더 저렴한 모델로 강등 — 무한정 청구서를 키우며 계속 서비스하는 대신 말이다.
- 하드 킬 스위치(최후의 수단). 절대 지출 속도에 대한 서킷 브레이커. 정상 트래픽으로는 결코 도달할 수 없는 분당 비용 상한을 넘으면, 호출을 멈추고 사람을 호출(page)한다. 이것은 폭주 루프를 위한 것이다 — 영원히 재시도하는 에이전트, 무한정 팬아웃하는 작업 — 버그 하나가 오후 반나절 만에 한 달치 예산을 태워버릴 수 있는 상황 말이다.
머신러닝 없이 하는 이상 탐지
비용 이상치를 잡는 데 모델은 필요 없다. 기능별 후행(trailing) 중앙값이 실제 사고의 대부분을 잡아내면서도 오탐은 거의 일으키지 않는다. 각 기능에 대해 최근 14일 또는 28일의 일일 지출(또는 요청당 중앙값 토큰)의 중앙값을 구한 뒤, 오늘 값이 정해둔 배수 — 가령 2배 — 이상 벗어나면 알린다. 평균이 아니라 중앙값을 써라. 그래야 과거의 스파이크 하나가 기준선을 오염시키지 않는다.
특히 세 가지 신호를 주시하라. 실제 세계의 비용 폭발 대부분이 이들로 설명되기 때문이다:
이것을 이벤트 저장소에 대한 스케줄 작업으로 돌려라. SQL 쿼리 몇 개와 비교 한 번이면 된다 — 이미 로깅을 위해 갖춰둔 것 외에 추가 인프라는 필요 없다.
- 출력 토큰 스파이크. 가장 흔한 범인이다. 출력은 어느 모델에서나 입력의 5~6배 비싸다(GPT-5.4는 입력 $2.50 / 출력 $15.00, 6배 비율이고, Sonnet 4.6은 $3.00 / $15.00, 5배다). 그래서 장황함 회귀 — 모델을 횡설수설하게 만드는 프롬프트 변경이나, 제거된
max_tokens— 는 청구서를 강하게 때린다. 기능별 요청당 중앙값 출력 토큰에 알림을 걸어라. - 캐시 적중률 하락. 입력 토큰 중 캐시 읽기가 차지하는 비중이 절벽처럼 떨어진다면, 십중팔구 배포가 프롬프트 프리픽스를 깨뜨린 것이다(시스템 프롬프트에 주입된 타임스탬프, 순서가 바뀐 컨텍스트 블록 등). 당신은 조용히 캐시 읽기 단가에서 전체 입력 단가로 되돌아간다 — Sonnet 4.6에서는 $0.30/1M 대신 $3.00/1M을 내는 것이니, 캐시된 부분에 대해 10배 점프다.
- 재시도 횟수 스파이크. 재시도가 늘었다는 건 같은 논리적 요청에 대해 두세 번 돈을 내고 있다는 뜻이다. 프로바이더 장애나 잘못된 요청 버그의 첫 신호인 경우가 많다.
의사결정을 이끄는 대시보드
대시보드는 그 위의 어떤 숫자가 이번 주에 누군가의 행동을 바꿀 때에만 존재할 자격이 있다. 허영뿐인 총지출 게이지는 건너뛰고, 레버에 매핑되는 뷰를 만들어라:
기능별 비용 뷰를 AI 기능 비용 추정하기의 작업과 짝지어, 모니터링된 현실을 당신이 계획할 때 썼던 모델과 대조할 수 있게 하라. 대시보드가 어떤 기능이 추정치의 3배라고 말한다면, 헤드라인은 절대 달러 값이 아니라 바로 그 격차다.
- 기능별 비용(시간에 따른 누적 차트). 돈이 어디 있는지, 그리고 어떤 기능의 최적화가 실제로 청구서를 움직일지를 보여준다.
- 1,000 요청당 비용, 기능별. 트래픽이 늘어도 안정적인 단위 경제 지표라서, 원시 총액이라면 트래픽 증가에 가려질 효율 회귀를 잡아낼 수 있다.
- 캐시 적중률(cache_read_tokens ÷ cacheable_input_tokens). 당신의 단연 최고의 조기 경보 지표다. 여기서의 하락은 공짜 돈이 문 밖으로 걸어 나가는 것이다.
- 출력/입력 토큰 비율 추세. 이 값이 슬금슬금 올라간다는 건 응답이 프롬프트 대비 점점 길어진다는 뜻이다 — 그리고 출력이 비싼 쪽이므로, 이는 어떤 알림이 울리기도 전에 당신의 마진이 서서히 침식되고 있다는 신호다.
직접 만들기 vs 사서 쓰기
직접 만드는 구성은 사실 작다. 이벤트를 내보내는 클라이언트 래퍼, 이벤트를 적재할 테이블(또는 컬럼형 저장소), 가격 설정, 야간 이상 탐지 쿼리, 그리고 이미 쓰고 있는 BI 도구에 만든 대시보드면 된다. 집중하는 엔지니어 한 명이 며칠이면 핵심을 출시할 수 있고, 데이터와 스키마, 프라이버시 태세를 직접 소유한다 — 프롬프트나 고객에 관한 그 무엇도 당신의 인프라 밖으로 나가지 않는다. 대가는 유지보수, 그리고 실시간 알림과 트레이스별 디버깅을 직접 추가로 만들어야 한다는 점이다.
호스팅형 LLM 관측가능성 도구는 트레이스, 토큰 회계, 대시보드, 평가(eval), 알림을 곧바로 제공하며, 잘 만들기 번거로운 요청별 드릴다운까지 갖춰준다. 트레이드오프는 실재한다. 요청 메타데이터(때로는 전체 프롬프트와 응답까지)를 제3자를 통해 흘려보내고, 가격이 이벤트 볼륨에 따라 늘어나는 경우가 많아 당신이 통제하려던 LLM 지출에 맞먹을 수도 있으며, 그들의 스키마와 새 모델이 나왔을 때의 모델 커버리지 지연을 그대로 떠안게 된다.
합리적인 규칙은 이렇다. 필요한 것이 구체적으로 비용 귀속과 예산 통제일 때는 직접 만들어라 — 그건 작고 잘 정의된 문제이고 데이터는 민감하다. 복잡한 에이전트 전반에 걸친 깊은 프롬프트 수준 디버깅, 평가 파이프라인, 트레이스 시각화까지 원하고, 그것을 복제할 엔지니어링 시간이 구독료를 넘어선다면 호스팅 도구로 손을 뻗어라. 많은 팀이 둘 다 한다. 재무용으로 신뢰하는 얇은 내부 비용 원장, 그리고 디버깅용 호스팅 트레이서를 병행하는 식이다.
어느 쪽을 택하든, 이 가이드의 요청별 필드가 토대다 — 당신의 기능 태그와 테넌트 태그를 수집하지 못하는 호스팅 도구는 비용 귀속도 할 수 없다. 분석 결과가 더 저렴한 모델 쪽을 가리킬 때, 마이그레이션 ROI 시뮬레이터는 당신의 실제 토큰 구성을 전환 비용 추정치로 바꿔준다.
자주 묻는 질문
요청마다 달러 비용을 저장해야 하나요, 아니면 토큰 수만 저장하면 되나요?
토큰 수(입력, 출력, 캐시 읽기, 캐시 쓰기)를 저장하고 달러는 쿼리 시점에 가격 설정을 기준으로 계산하세요. 달러 값을 영속화하면 프로바이더의 단가 변경이 히스토리를 영구히 망가뜨립니다. 그 값을 만들어낸 원본 토큰 수를 이미 버렸기 때문이죠. 토큰을 보존해 두면 단가 변경은 한 줄짜리 설정 수정으로 끝나고, 전체 히스토리가 올바르게 재계산됩니다.
어떤 고객이 적자를 내고 있는지 어떻게 찾나요?
모든 요청에 테넌트 id를 태그한 뒤, 쿼리에서 각 테넌트의 월간 토큰 수에 1M당 단가를 곱하고 그 테넌트가 내는 금액과 비교하세요. 지출은 보통 심하게 한쪽으로 쏠리기 때문에, 고컨텍스트 기능을 쓰는 소수의 테넌트가 구독료의 몇 배에 달하는 비용을 낼 수 있습니다. GPT-5.4에서 12,000 토큰 컨텍스트로 월 9,000회 요청하는 테넌트는 토큰 비용만 약 $351인데, 이는 테넌트별 비용 귀속이 있어야만 보이는 문제입니다.
실제로 작동하는 가장 단순한 이상 탐지는 무엇인가요?
기능별로 최근 14일 또는 28일의 일일 지출(또는 요청당 토큰)의 후행 중앙값을 구하고, 오늘 값이 2배 같은 배수를 넘으면 알리세요. 평균이 아니라 중앙값을 써야 과거 스파이크 하나가 기준선을 오염시키지 않습니다. 특히 출력 토큰 스파이크, 캐시 적중률 하락, 재시도 스파이크를 주시하세요 — 이 셋이 실제 사고 대부분을 커버하며, 스케줄된 SQL 쿼리 몇 개만 있으면 됩니다.
호스팅형 관측가능성 도구가 꼭 필요한가요, 아니면 직접 만들 수 있나요?
비용 귀속과 예산 통제만 놓고 보면 직접 만드는 데 며칠이면 됩니다. 요청별 이벤트를 내보내는 클라이언트 래퍼, 이를 저장할 테이블, 가격 설정, 야간 이상 탐지 쿼리면 되죠. 데이터가 민감하고 필요한 것이 재무 수준의 비용 추적이라면 직접 만드세요. 복잡한 에이전트 전반에 걸친 깊은 프롬프트 수준 트레이스 디버깅과 평가 파이프라인까지 원하고, 그것을 복제하는 데 구독료보다 더 많은 엔지니어링 시간이 든다면 호스팅 도구를 사세요.