
“왜 추론 모델로 바꾸고 나서 API 요금이 갑자기 뛰었을까요?” 결론부터 말씀드리면, 화면에는 보이지 않는 ‘사고 토큰(reasoning tokens)’이 출력 토큰 단가로 그대로 청구되기 때문입니다. 같은 질문을 던져도 일반 모델보다 추론 모델 쪽 응답에 보이지 않는 계산 과정이 훨씬 길게 붙는 구조라, 프롬프트와 답변 길이는 비슷한데 비용이 눈에 띄게 뛰었을 때 대부분 이 사고 토큰이 원인입니다. 이 글에서는 OpenAI의 o시리즈·GPT-5 계열과 Anthropic Claude의 확장 사고(extended thinking) 기능을 기준으로, 사고 토큰이 왜 과금되는지와 effort(추론 강도)를 어떻게 조절해야 하는지 실제 파라미터 이름과 코드로 정리합니다.
왜 추론 모델로 바꾸고 나면 청구서에 사고 토큰이 붙을까요?
일반적인 챗 모델은 프롬프트를 받으면 바로 답을 이어서 생성합니다. 반면 o시리즈나 GPT-5처럼 거대 언어 모델 중에서도 추론에 특화된 모델은 최종 답을 내놓기 전에 내부적으로 여러 단계의 사고 과정을 먼저 생성합니다. 이 사고 과정은 기본적으로 사용자에게 보이지 않지만, 실제로는 토큰을 소비한 결과물이기 때문에 과금 대상에서 빠지지 않습니다.
OpenAI API를 예로 들면, 응답 객체의 usage.completion_tokens_details.reasoning_tokens 값에 이 숨은 토큰 수가 별도로 기록됩니다. 겉으로 보이는 답변은 짧아도 이 값이 수천 토큰까지 올라갈 수 있고, 이 토큰들은 입력 토큰이 아니라 보통 더 비싼 출력 토큰 단가로 계산됩니다. 관련 동작 방식은 OpenAI가 공개한 추론 토큰 가이드 문서에 정리되어 있는데, 겉보기 응답 길이와 실제 청구 토큰 수가 다를 수 있다는 점이 핵심입니다.
응답에는 안 보이는데 청구서에는 잡히는 사고 토큰의 정체
실제로 사용량을 확인하는 코드는 아래처럼 작성할 수 있습니다. 응답 텍스트만 보면 몇 문장 안 되는데, reasoning_tokens 값을 찍어보면 그보다 몇 배 많은 토큰이 소비된 것을 확인할 수 있습니다.

from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="o3-mini",
reasoning_effort="medium",
messages=[{"role": "user", "content": "이 코드의 버그를 찾아줘: ..."}]
)
usage = response.usage
print("보이는 출력 토큰:", usage.completion_tokens)
print("그중 사고 토큰:", usage.completion_tokens_details.reasoning_tokens)
여기서 중요한 건 사고 토큰이 “낭비”라기보다, 모델이 복잡한 문제를 풀 때 실제로 여러 경로를 시도해보고 검토하는 과정이라는 점입니다. 다만 간단한 분류나 포맷 변환처럼 굳이 깊은 사고가 필요 없는 작업에도 이 과정이 그대로 붙으면, 체감 응답 품질은 크게 달라지지 않으면서 비용만 늘어나는 일이 흔합니다.
reasoning_effort 값에 따라 사고 토큰 양이 크게 달라집니다
이 부분을 조절하는 파라미터가 바로 reasoning_effort입니다. o시리즈 모델은 low · medium · high 세 단계를 지원하고, GPT-5 계열 API는 응답 API(Responses API)에서 reasoning.effort 값으로 minimal까지 추가로 선택할 수 있게 되어 있습니다. 값이 낮을수록 모델이 내부 사고 단계를 적게 거치고 빨리 답을 내놓는 대신, 여러 단계를 거쳐야 풀리는 문제에서는 정확도가 떨어질 수 있습니다.
| effort 값 | 사고 토큰 경향 | 적합한 작업 |
|---|---|---|
| minimal / low | 짧음, 응답 빠름 | 분류, 포맷 변환, 짧은 요약 |
| medium | 중간 | 일반적인 코드 작성, 도구 호출을 포함한 에이전트 작업 |
| high | 길어질 수 있음 | 다단계 수학 증명, 복잡한 리팩터링 계획, 장기 논리 추론 |
Anthropic Claude 쪽은 이름은 다르지만 개념이 비슷한 확장 사고 기능을 제공합니다. thinking 파라미터에 budget_tokens를 지정해서 사고에 쓸 토큰 상한을 직접 정하는 방식입니다.
response = client.messages.create(
model="claude-opus-4",
max_tokens=4096,
thinking={"type": "enabled", "budget_tokens": 2048},
messages=[{"role": "user", "content": "이 계약서 초안의 리스크를 분석해줘"}]
)
budget_tokens는 실제 사용된 만큼만이 아니라 모델이 그 예산 안에서 생성한 사고 토큰 전체가 출력 토큰으로 과금되는 구조라서, 예산을 무작정 크게 잡으면 짧게 끝날 수 있었던 요청도 비용이 함께 늘어날 수 있습니다.

이 기준으로 프로젝트별 effort 단계를 나눠보세요
실무에서는 요청 종류별로 effort를 고정해두는 편이 관리하기 쉽습니다. 예를 들어 사용자 입력을 카테고리로 분류하거나 JSON 형식으로 정리하는 엔드포인트는 low나 minimal로 충분한 경우가 많고, 실제 코드 생성이나 여러 파일을 넘나드는 리팩터링 요청에만 medium 이상을 쓰는 식으로 나누면 전체 평균 비용이 눈에 띄게 줄어듭니다.
다만 여기서 짚어야 할 트레이드오프가 있습니다. effort를 낮췄다고 항상 비용이 비례해서 줄어드는 건 아닙니다. 문제 자체가 정말 여러 단계를 거쳐야 풀리는 성격이면, 모델이 낮은 effort 설정에서도 필요한 만큼 사고 토큰을 쓰려는 경향을 보일 수 있고, 반대로 무리하게 낮추면 중간에 사고를 서둘러 마무리하면서 답의 정확도만 떨어지는 경우도 있습니다. 그래서 effort 값을 바꿀 때는 비용만 보지 말고, 실제 작업에서 정답률이나 결과물 품질이 함께 어떻게 변하는지 간단한 평가셋으로 비교해보는 과정이 필요합니다.
캐싱을 켰는데도 왜 사고 토큰 비용은 그대로인가요?
프롬프트 캐싱을 적용하면 반복되는 입력 토큰 비용은 확실히 줄어듭니다. 하지만 사고 토큰은 매 요청마다 모델이 그 순간 새로 생성하는 값이라서, 같은 프롬프트 앞부분이 캐시에 걸려도 뒤에 이어지는 사고 과정 자체는 캐시되지 않습니다. 즉 입력 쪽 비용은 줄어드는데 사고 토큰이 포함된 출력 쪽 비용은 그대로 남아, “캐싱을 켰는데 총 비용이 왜 안 줄지?”라는 상황이 생깁니다. 이 부분을 줄이려면 캐싱이 아니라 effort 조절이나 애초에 추론 모델로 보낼 요청 자체를 줄이는 방향으로 접근해야 합니다.
비용이 눈에 띄게 뛰었다면 이 순서로 점검해보세요
먼저 최근 요청 로그에서 usage.completion_tokens_details.reasoning_tokens 값을 뽑아, 전체 출력 토큰 중 사고 토큰이 차지하는 비중부터 확인해보시기 바랍니다. 그다음 요청 유형을 나눠 분류·포맷 작업처럼 단순한 요청은 effort를 낮추거나 아예 비추론 모델로 라우팅하고, 정말 다단계 판단이 필요한 요청에만 높은 effort를 남겨두는 방식으로 조정하면 됩니다. 마지막으로 effort를 바꾼 뒤에는 반드시 정확도 변화를 함께 측정해서, 비용은 줄었는데 품질이 감당 안 될 만큼 떨어지지는 않았는지 확인하는 절차까지 챙기시는 걸 권해드립니다.
