LLM 비용은 호출 횟수가 아니라 주고받은 토큰의 양으로 결정됩니다. 같은 요청 한 건이라도 프롬프트가 길거나 답변이 길면 비용이 몇 배로 뜁니다. 그래서 LLM 서비스를 운영하면 “이번 달 청구서가 왜 이렇게 나왔지?”라는 질문을 피할 수 없는데, 제공사 대시보드는 총액만 보여 줄 뿐 어떤 기능이, 어떤 사용자가, 어떤 요청이 비용을 발생시켰는지 알려 주지 않습니다. 직접 토큰 사용량을 로깅하고 집계하는 대시보드를 만들어야 하는 이유입니다. 핵심은 네 가지입니다. 입력·출력·캐시 토큰을 구분해 기록하는 로깅 스키마, 기능·사용자별 비용 배분, 이상 급증 알림, 그리고 그 데이터에서 최적화 인사이트를 뽑아내는 집계입니다.
토큰이 곧 비용이다
LLM API의 과금은 토큰 단위입니다. 토큰은 모델이 텍스트를 처리하는 최소 단위로, 대략 단어 조각 정도라고 보면 됩니다. 비용은 보통 입력 토큰과 출력 토큰에 각각 다른 단가가 매겨지며, 일반적으로 출력 토큰이 입력보다 비쌉니다. 따라서 같은 호출이라도 긴 답변을 생성하면 비용이 크게 늘어납니다.
여기에 더해 최근에는 캐시 토큰이라는 구분이 중요해졌습니다. 프롬프트 캐싱을 쓰면 반복되는 앞부분 컨텍스트가 캐시에서 재사용되는데, 이 캐시 적중 토큰은 일반 입력보다 훨씬 낮은 단가로 청구됩니다. 반대로 캐시를 처음 만드는 쓰기 토큰에는 약간의 추가 비용이 붙는 모델도 있습니다. 결국 정확한 비용을 알려면 입력, 출력, 캐시 읽기, 캐시 쓰기를 각각 따로 집계해야 합니다.
중요한 원칙 하나는 토큰 수를 추정하지 말고 실측하라는 것입니다. 대부분의 API는 응답에 사용량(usage) 정보를 담아, 그 요청이 실제로 소비한 토큰 수를 정확히 돌려줍니다. 클라이언트에서 토크나이저로 추정한 값과 실제 청구 값은 차이가 날 수 있으므로, 비용 집계의 기준은 항상 응답의 실측 usage여야 합니다.
입력·출력·캐시 토큰을 구분 집계
제공사 응답의 usage 객체에는 보통 입력·출력 토큰이 분리돼 있고, 캐싱을 쓰면 캐시 관련 토큰이 추가로 들어 있습니다. 필드명은 제공사마다 다르지만 구조는 비슷합니다. 비용을 정확히 계산하려면 이 항목들을 각자의 단가와 곱해 합산합니다.
| 토큰 종류 | 의미 | 단가 경향(일반) |
|---|---|---|
| 입력 토큰 | 프롬프트로 보낸 양 | 기준 단가 |
| 출력 토큰 | 모델이 생성한 양 | 입력보다 높음 |
| 캐시 읽기 토큰 | 캐시에서 재사용한 입력 | 입력보다 낮음 |
| 캐시 쓰기 토큰 | 캐시에 새로 올린 입력 | 모델별 상이(추가 비용 가능) |
// 응답 usage에서 비용 계산 (개념 예시 — 단가는 설정에서 주입)
function costOf(usage, price) {
const input = (usage.input_tokens ?? 0) * price.input;
const output = (usage.output_tokens ?? 0) * price.output;
const cRead = (usage.cache_read_tokens ?? 0) * price.cacheRead;
const cWrite = (usage.cache_write_tokens ?? 0) * price.cacheWrite;
return input + output + cRead + cWrite; // 단위는 가격표 기준에 맞춰 환산
}단가는 코드에 하드코딩하지 말고 모델별 설정값으로 분리해 두는 것이 좋습니다. 단가는 시점에 따라 바뀌고 모델마다 다르므로, 가격표를 한곳에서 관리하면 변경에 강해집니다. 또한 과거 데이터를 다시 계산할 일이 있으므로, 비용을 미리 계산해 저장하기보다 토큰 수를 원본으로 저장하고 비용은 단가표를 곱해 도출하는 구조가 유연합니다.
요청별 로깅 스키마
대시보드의 토대는 요청 한 건마다 남기는 로그입니다. 무엇을 기록하느냐가 나중에 어떤 질문에 답할 수 있는지를 결정합니다. 비용을 다차원으로 분석하려면 토큰 수뿐 아니라 그 요청의 맥락(누가, 어떤 기능에서, 어떤 모델로)을 함께 남겨야 합니다.
-- 요청별 사용량 로그 (개념 예시)
CREATE TABLE llm_usage_log (
id BIGSERIAL PRIMARY KEY,
ts TIMESTAMPTZ NOT NULL DEFAULT now(),
request_id TEXT, -- 추적·중복 제거용
model TEXT NOT NULL, -- 모델/버전
feature TEXT NOT NULL, -- 기능 단위(요약/검색/채팅 등)
user_id TEXT, -- 사용자/조직 배분용
input_tokens INT NOT NULL,
output_tokens INT NOT NULL,
cache_read_tokens INT NOT NULL DEFAULT 0,
cache_write_tokens INT NOT NULL DEFAULT 0,
latency_ms INT,
status TEXT -- success / error / timeout
);
CREATE INDEX ON llm_usage_log (ts);
CREATE INDEX ON llm_usage_log (feature, ts);
CREATE INDEX ON llm_usage_log (user_id, ts);로깅에서 지켜야 할 두 가지가 있습니다. 첫째, 토큰 수와 메타데이터만 남기고 프롬프트·응답 본문은 신중히 다뤄야 합니다. 본문에는 개인정보나 민감정보가 섞일 수 있어, 비용 분석 목적이라면 본문 대신 토큰 수와 분류 태그만으로 충분한 경우가 많습니다. 둘째, 시계열 질의가 잦으므로 타임스탬프와 분석 축(기능·사용자)에 인덱스를 걸어 집계 성능을 확보합니다. 데이터가 커지면 일자별 롤업 테이블로 미리 합산해 두면 대시보드가 가벼워집니다.
기능·사용자별 비용 배분
총비용만 보면 “비싸다”는 사실만 알 뿐 손쓸 곳을 모릅니다. 비용을 기능과 사용자라는 축으로 쪼개야 비로소 행동으로 이어집니다. feature 태그로 집계하면 어떤 기능이 비용의 대부분을 먹는지 보이고, user_id로 집계하면 소수의 헤비 유저가 비용을 끌어올리는지, 무료 사용자에게 과도한 비용이 나가는지 드러납니다.
-- 기능별 일일 비용 집계 (개념 예시, 단가는 조인/뷰로 적용)
SELECT
date_trunc('day', ts) AS day,
feature,
count(*) AS calls,
sum(input_tokens) AS in_tok,
sum(output_tokens) AS out_tok,
sum(cache_read_tokens) AS cache_tok
FROM llm_usage_log
WHERE ts >= now() - interval '30 days'
GROUP BY 1, 2
ORDER BY 1 DESC, out_tok DESC;이 집계는 단가 정책 결정에도 쓰입니다. 예를 들어 특정 기능이 비용의 대부분을 차지하는데 사용 빈도는 낮다면, 그 기능에만 더 저렴한 모델을 붙이거나 응답 길이를 제한하는 식의 조준된 최적화가 가능합니다. 사용자별 배분은 요금제 설계의 근거가 됩니다. 실제 토큰 소비와 요금이 어긋나는 구간이 보이면 가격 정책을 조정할 수 있습니다.
| 분석 축 | 답할 수 있는 질문 | 활용 |
|---|---|---|
| 기능별 | 어느 기능이 비용을 먹나 | 모델 다운그레이드·출력 제한 |
| 사용자/조직별 | 누가 많이 쓰나 | 요금제·쿼터 설계 |
| 모델별 | 어떤 모델이 효율적인가 | 모델 전환 판단 |
| 시간대별 | 언제 몰리나 | 배치 이전·캐시 전략 |
이상 급증 알림
비용 사고는 보통 갑자기 터집니다. 무한 루프가 같은 요청을 반복하거나, 프롬프트에 거대한 문서가 통째로 들어가거나, 잘못된 배포로 호출량이 폭증하는 식입니다. 월말 청구서로 알게 되면 이미 큰 금액이 나간 뒤입니다. 그래서 실시간에 가까운 이상 급증 알림이 필요합니다.
알림 기준은 여러 층위로 둡니다. 단순한 절대 임계치(시간당 비용이 일정 금액을 넘으면 알림)는 구현이 쉽고 즉효성이 있습니다. 여기에 상대 비교(직전 같은 요일·시간대 대비 몇 배 이상 증가)를 더하면 평소 패턴에서 벗어나는 변화를 잡습니다. 단일 요청의 토큰 수가 비정상적으로 큰 경우(예: 입력이 평소의 수십 배)도 개별 알림 대상입니다. 한 건이 통째로 비용 폭탄일 수 있기 때문입니다.
총비용만 보면 폭주를 늦게 발견합니다.
일·주 단위 총액만 모니터링하면, 새벽에 시작된 루프성 호출이 아침까지 수 시간 동안 비용을 쌓는 것을 놓칩니다. 시간당·기능당 비용에 임계치와 상대 증가율 알림을 함께 걸고, 단일 요청의 토큰 상한도 점검해야 사고를 초기에 끊을 수 있습니다. 가능하면 제공사 측 예산 한도(budget/quota) 설정도 병행하세요.
집계 도구와 구성
대시보드는 거창한 새 인프라 없이도 만들 수 있습니다. 데이터 규모와 기존 스택에 맞춰 단계적으로 키우는 것이 합리적입니다. 작은 규모라면 관계형 DB에 로그를 쌓고 SQL 집계 뷰를 BI 도구나 간단한 차트 페이지에 연결하는 것으로 충분합니다. 규모가 커지면 시계열 DB나 컬럼형 분석 DB로 옮기고, 메트릭은 관측성 스택(메트릭·로그·트레이스)에 통합하는 방향이 자연스럽습니다.
| 규모 | 저장 | 집계·시각화 |
|---|---|---|
| 소규모 | 관계형 DB(로그 테이블) | SQL 뷰 + BI/차트 |
| 중규모 | + 일자별 롤업 테이블 | 대시보드 도구 |
| 대규모 | 시계열/컬럼형 분석 DB | 관측성 스택 통합 |
어느 구성이든 핵심은 동일합니다. 원본 토큰 수를 정확히 남기고, 단가표를 분리해 비용을 도출하며, 분석 축(기능·사용자·모델·시간)으로 쪼개 볼 수 있게 하는 것입니다. 도구는 이 데이터 모델을 따라가는 수단일 뿐입니다.
최적화 인사이트 도출
대시보드의 목적은 보는 것이 아니라 비용을 줄이는 것입니다. 잘 쌓인 데이터는 구체적인 절감 행동으로 이어집니다. 출력 토큰 비중이 큰 기능은 응답 길이를 제한하거나 더 간결한 출력을 유도하는 프롬프트로 비용을 줄일 수 있습니다. 입력 토큰이 큰데 반복 호출이 잦다면 프롬프트 캐싱으로 캐시 읽기 비중을 높여 단가를 낮춥니다. 캐시 읽기 토큰 비율이 곧 캐싱 효율의 지표가 됩니다.
모델 선택도 데이터로 판단합니다. 단순 분류·추출처럼 가벼운 작업에 비싼 모델을 쓰고 있다면, 더 저렴한 모델로 내려도 품질이 유지되는지 실험하고 비용 차이를 측정합니다. 실시간성이 필요 없는 대량 작업은 배치 처리로 옮겨 할인 혜택을 받을 수 있는지도 검토합니다. 이 모든 판단은 “어디서 얼마가 나가는지”를 정확히 아는 데서 출발합니다.
비용을 절대값이 아니라 단위 지표로 환산해 보면 더 선명한 인사이트가 나옵니다. 요청당 평균 비용, 사용자당 월 비용, 기능 한 번 실행당 비용처럼 분모를 정해 나누면, 트래픽이 늘어 총비용이 오른 것인지 아니면 요청 하나하나가 비싸진 것인지 구분할 수 있습니다. 매출이나 활성 사용자 수와 함께 보면, LLM 비용이 서비스 성장에 비례해 건강하게 늘고 있는지 아니면 효율이 나빠지고 있는지가 드러납니다. 또한 입력 토큰 대비 출력 토큰 비율, 캐시 적중 비율 같은 파생 지표를 시계열로 추적하면, 프롬프트가 슬그머니 길어지거나 캐싱 효율이 떨어지는 변화를 배포 시점과 연결해 짚어낼 수 있습니다. 결국 잘 만든 대시보드는 사고가 터진 뒤 들여다보는 청구서가 아니라, 비용 구조의 변화를 미리 읽어 내는 운영 도구가 됩니다.
대시보드 구축 체크리스트
- 비용 기준을 추정값이 아닌 응답의 실측 usage로 삼는가
- 입력·출력·캐시 읽기·캐시 쓰기 토큰을 각각 분리해 저장하는가
- 요청 로그에 기능·사용자·모델·시각·지연·상태를 함께 남기는가
- 단가는 코드에 박지 않고 모델별 설정으로 분리했는가
- 기능·사용자·모델·시간 축으로 집계해 비용을 배분하는가
- 시간당·요청당 임계치와 상대 증가율 알림을 걸었는가
- 프롬프트·응답 본문의 민감정보 저장을 최소화했는가
자주 묻는 질문
- 토큰 수를 직접 세서 비용을 계산하면 안 되나요?
- 클라이언트 토크나이저 추정값은 실제 청구 토큰과 차이가 날 수 있습니다. 거의 모든 API가 응답에 실측 사용량(usage)을 담아 주므로, 비용 집계의 기준은 항상 이 실측값으로 삼아야 정확합니다. 추정은 사전 예상용으로만 쓰세요.
- 캐시 토큰은 왜 따로 집계하나요?
- 캐시에서 재사용된 입력은 일반 입력보다 훨씬 낮은 단가로 청구되고, 캐시 쓰기에는 추가 비용이 붙는 모델도 있습니다. 분리해 집계해야 비용이 정확해지고, 캐시 읽기 비율로 캐싱이 실제로 효과를 내는지도 측정할 수 있습니다.
- 비용을 미리 계산해 저장하는 게 좋나요?
- 토큰 수를 원본으로 저장하고 비용은 단가표를 곱해 도출하는 편이 유연합니다. 단가는 바뀌고 모델마다 다르므로, 단가를 분리해 두면 정책 변경 시 과거 데이터도 다시 정확히 계산할 수 있습니다.
- 이상 급증은 어떻게 잡나요?
- 총액만 보지 말고 시간당·기능당 비용에 절대 임계치와 평소 대비 상대 증가율 알림을 함께 거세요. 단일 요청의 토큰 수가 비정상적으로 큰 경우도 별도로 점검해야 한 건짜리 비용 폭탄을 초기에 끊을 수 있습니다.
- 대시보드에 어떤 도구가 필요한가요?
- 작은 규모는 관계형 DB에 로그를 쌓고 SQL 집계를 차트에 연결하는 것으로 충분합니다. 규모가 커지면 일자별 롤업, 시계열·분석 DB, 관측성 스택 통합으로 확장하면 됩니다. 도구보다 토큰 원본·단가 분리·분석 축이라는 데이터 모델이 더 중요합니다.
입력·출력·캐시 토큰 단가와 usage 필드 구조는 2026년 기준이며 수시로 바뀌므로, 비용 집계 전 OpenAI 공식 문서의 가격·사용량(usage) 항목을 확인하세요.
이 글은 OpenAI·Anthropic·Google 등의 공개 API 문서에 기술된 토큰·사용량(usage) 구조와 일반적인 관측성 설계 원칙을 정리한 것입니다. usage 필드명, 토큰 종류 구분, 단가는 제공사와 모델, 시점에 따라 다르므로 실제 비용 계산 전 각 제공사의 공식 문서와 가격 페이지를 확인하시기 바랍니다. 스키마·코드는 원리 설명용 개념 예시입니다.