Cursor에서 140만 토큰의 두 요청 비용이 왜 이렇게 달랐을까
요약
Cursor 사용 중 동일한 토큰 수임에도 비용 차이가 발생하는 이유를 분석합니다. 비용은 단순 총 토큰 수가 아니라 입력, 출력, 캐시 읽기/쓰기 등 카테고리별 가중치에 의해 결정됨을 설명합니다.
핵심 포인트
- 비용은 총 토큰 수가 아닌 카테고리별(Input, Output, Cache Read/Write) 합계로 결정됨
- 출력(Output) 토큰은 캐시 읽기(Cache Read)보다 약 50배 더 비쌈
- 프리픽스 캐싱(Prefix Caching)을 통해 컨텍스트 재사용 시 비용 절감 가능
- 세션 첫 호출은 캐시 구축 비용(Cache Write) 때문에 가장 비싸게 발생함
며칠 전, 내 토큰이 어디로 가고 있는지 이해하기 위해 Cursor의 사용량 대시보드를 분석하던 중, 방금 막 실행된 두 개의 요청에서 시선이 멈췄습니다. 보통은 토큰 수를 보고 숫자가 더 큰 쪽이 더 비쌀 것이라고 짐작하기 마련입니다. 하지만 두 요청을 비교했을 때, 제가 발견한 결과는 그렇지 않았습니다.
두 요청의 크기는 거의 동일했습니다. 하나는 134만 토큰이었고, 다른 하나는 140만 토큰이었습니다. 볼륨이 거의 똑같았습니다. 첫 번째 요청은 1.13달러였지만, 두 번째 요청은 거의 세 배에 달하는 2.96달러였습니다.
만약 토큰 수가 비용을 결정하는 요소라면, 크기가 같은 두 요청은 거의 비슷한 비용이 들어야 합니다. 하지만 실제로는 그렇지 않았습니다. 더 많은 토큰이 더 많은 비용을 의미한다는 직관은 기껏해야 불완전한 것이며, 비용을 실제로 결정하는 요인이 무엇인지 이해하는 것은 LLM (Large Language Model) 청구서를 읽는 방식을 완전히 바꿔 놓습니다.
왜 총 토큰 수가 우리를 속이는가
본능적으로는 요청을 요약하는 숫자인 '총계(Total)'를 보게 됩니다. 하지만 총계는 컨텍스트 윈도우 (Context Window)를 통과한 텍스트의 양을 측정하는 것이지, 비용을 측정하는 것이 아닙니다. 이 두 가지는 함께 움직이지 않습니다.
비용은 총 토큰 수의 단순한 함수가 아닙니다. 비용은 카테고리별로 가중치가 부여된 합계입니다: 새로운 입력 (Input), 캐시 쓰기 (Cache Write), 캐시 읽기 (Cache Read), 그리고 출력 (Output).
네 가지 카테고리, 네 가지 가격
요청의 세부 내역은 사용량을 네 가지 카테고리로 나누며, 각 카테고리마다 고유한 가격이 책정됩니다. Opus의 값을 기준으로 백만 토큰당 가격을 살펴보면 다음과 같습니다:
| 카테고리 | 백만 토큰당 가격 | 설명 |
|---|---|---|
| Cache Read | ~US$ 0.50 | 이미 캐시되어 재사용되는 컨텍스트 |
| ... |
양 끝단의 차이가 핵심입니다. 출력 (Output)은 캐시 읽기 (Cache Read)보다 50배 더 비쌉니다. 동일한 토큰이라도 어떤 카테고리에 속하느냐에 따라 비용이 근본적으로 달라집니다. 요청 비용을 예측하려면 총계가 아니라 이러한 카테고리 간의 분포를 알아야 합니다.
프리픽스 캐싱 (Prefix Caching)은 어떻게 작동하는가
모델은 stateless (상태 비저장)입니다. 한 호출과 다음 호출 사이에 아무것도 유지하지 않습니다. 새로운 메시지가 올 때마다 애플리케이션은 시스템 지침 (system instructions), 규칙 (rules), 도구 정의 (tool definitions), 대화 기록 (conversation history), 이미 읽은 파일 등 처음부터 시작되는 전체 컨텍스트 (context)를 다시 보냅니다. 매 호출마다 모든 것을 보냅니다.
매 상호작용마다 이 모든 콘텐츠를 처음부터 다시 처리하는 것은 비용이 너무 많이 듭니다. 해결책은 프리픽스 캐싱 (Prefix Caching)이며, '프리픽스 (prefix, 접두사)'라는 단어는 문자 그대로의 의미를 갖습니다. 캐시는 이전 호출의 콘텐츠와 동일한 동안 컨텍스트의 시작 부분부터 앞으로 작동합니다.
세션의 첫 번째 호출에서는 캐싱된 것이 아무것도 없습니다. 모든 컨텍스트가 처음으로 기록되며, 기록하는 것은 비용이 많이 듭니다 (이는 Cache Write입니다). 이것이 모든 세션의 첫 번째 요청이 토큰당 가장 비싼 이유입니다. 캐시를 구축하기 위한 비용을 지불하는 것입니다. 두 번째 호출부터는 동일한 시작 부분이 Cache Read로 재사용되며, 이는 약 10배 더 저렴합니다. 긴 연속 세션이 저렴해지는 경향이 있는 이유가 바로 이것입니다. 캐시를 구축하는 비용은 한 번만 지불하면 되며, 이후의 모든 호출에 걸쳐 분산되기 때문입니다.
중간에 무언가 변경되면 어떤 일이 발생하는가
실무에서 가장 주의해야 할 세부 사항은 다음과 같습니다. 캐시는 컨텍스트가 변경된 첫 번째 지점까지만 유효합니다. 캐시는 시작부터 앞으로 매칭되기 때문에, 초기 부분의 어떤 변경이라도 그 이후의 모든 것을 무효화합니다.
구체적인 예를 들어보겠습니다. 귀하의 규칙 (rules)은 시스템 지침 바로 뒤인 컨텍스트의 앞부분에 위치합니다. 세션 중간에 규칙 하나를 수정하면, 그 이후에 오는 모든 것(도구, 전체 기록, 파일들)은 이전 캐시와 일치하지 않게 되어 다시 Cache Write 가격으로 다시 기록되어야 합니다. 단 몇 줄의 변경이라도 앞부분에 위치하면 수십만 개의 토큰을 다시 처리하도록 강제합니다. 실질적인 결과는 명확합니다. 안정적인 것은 컨텍스트의 앞부분에 두어야 하고, 자주 변경되는 것은 뒷부분에 두어야 합니다.
Cursor가 컨텍스트 창(context window)의 한계에 도달하여 컨텍스트를 요약(summarize)할 때도 동일한 현상이 발생합니다. 요약은 히스토리를 다시 쓰는 과정이며, 다시 쓴다는 것은 변화를 의미합니다. 다시 쓰여진 부분부터는 캐시(cache)가 손실됩니다.
캐시는 시간이 지나면 만료됩니다
사용자가 아무것도 변경하지 않더라도 캐시는 영원히 지속되지 않습니다. Anthropic의 경우, 기본 캐시는 약 5분간 활동이 없으면 만료되며, 이 카운터는 새로운 액세스가 발생할 때마다 재설정됩니다. 실제로 5분 미만의 간격으로 연속해서 작업을 수행하며 호출한다면, 캐시는 무기한으로 '웜(warm)' 상태를 유지할 수 있습니다. 하지만 그보다 오래 멈추거나, 회의에 참석하거나, 점심을 먹고 다음 날 돌아온다면 캐시는 '콜드(cold)' 상태가 됩니다. 그러면 다음 호출은 새로운 콜드 스타트(cold start)처럼 처음부터 다시 캐시 쓰기(Cache Write)를 수행하게 됩니다. 더 높은 기록 비용을 지불하면 1시간 동안 유지되는 더 긴 창(window) 옵션도 있습니다.
메커니즘을 요약하자면 다음과 같습니다: 안정적이고 연속적인 앞부분은 저렴합니다. 컨텍스트의 앞부분을 건드리거나 세션이 식게 만드는 것이 비용을 발생시킵니다.
네 가지 필드가 드러내는 것
메커니즘이 명확해졌으므로, 이제 'Total' 대신 네 가지 필드를 통해 제 요청들을 읽어볼 수 있습니다.
| 요청 | Total | Cache Read | Cache Write | Input | Output | 비용 | 100만 토큰당 비용 |
|---|---|---|---|---|---|---|---|
| A | 1,343,927 | 1,334,715 | 5,613 | 10 | 3,589 | US$ 1.13 | ~US$ 0.84 |
| B | 1,399,381 | 912,025 | 302,010 | 169,871 | 15,475 | US$ 2.96 | ~US$ 2.12 |
요청 A는 이상적인 시나리오입니다. 사용량의 99%가 캐시 읽기(Cache Read)였습니다. 이는 웜(warm) 세션 중간에 작동하며 이전 요청들이 구축한 캐시를 재사용했음을 의미합니다. 새로운 입력(Input)은 거의 없었고, 출력(Output)도 적었습니다. 100만 토큰당 약 US$ 0.84가 발생했으며, 이것이 바로 효율적인 요청의 모습입니다.
요청 B는 정반대입니다. A와 비슷한 볼륨을 가졌지만, 비용은 거의 세 배에 달했습니다. 그 이유는 두 가지 항목에 있습니다: 단일 요청 내에서 302,000개의 Cache Write (캐시 쓰기)와 170,000개의 Input fresco (신규 입력)가 발생했습니다. 이것이 바로 churn (변동/이탈)입니다. 세션 도중 규칙(rule)이 편집되었거나 Cursor에 의해 요약(summarization)이 실행되는 등 컨텍스트의 시작 부분에 변화가 생겼고, 그 지점부터 캐시가 무효화(invalidated)된 것입니다. 모델은 컨텍스트의 상당 부분을 Write (쓰기) 가격으로 다시 기록했으며, 새로운 파일들을 Input (입력)으로 처리했습니다. 단 하나의 요청에 세 가지의 비싼 카테고리가 집중된 것입니다.
A와 B는 토큰 볼륨이 각각 134만 대 140만으로 거의 동일함에도 불구하고, 비용은 거의 세 배 차이가 납니다. 변수는 크기가 아니었습니다. 바로 구성(composition)이었습니다.
결론: 네 가지 항목으로 측정하세요
Total (합계)은 비용을 예측하거나 진단하는 데 유용한 지표가 아닙니다. 네 가지 항목이 유용합니다.
요청 비용이 높게 나온다면, 상세 내역을 열어 책임이 있는 카테고리를 식별해야 합니다:
- **Cache Write (캐시 쓰기)**가 높으면 cold start (콜드 스타트) 또는 컨텍스트 시작 부분의 변경을 의미합니다.
- **Output (출력)**이 높으면 과도한 생성, 특히 오류 발생 후 코드가 다시 생성되는 경우가 빈번함을 의미합니다.
- **Input fresco (신규 입력)**가 높으면 많은 파일을 처음부터 탐색하고 있음을 의미합니다.
각 카테고리에는 별도의 원인과 완화(mitigation) 방법이 있습니다. Total은 모든 것을 하나의 숫자로 합쳐버려, 정작 조치를 취하는 데 필요한 정보를 가려버립니다.
다음 포스트에서는 완화 방법, 즉 비용을 실제로 줄여주는 두 가지 레버인 Cache Read (캐시 읽기) 극대화와 낭비되는 Output (출력) 감소에 대해 다루겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기