Claude Messages API Message Threads 해부: 5분 서버 스레드·프롬프트 캐싱·404 리플레이 분리해 설계하기
11월 2일 공개된 Claude Messages API의 Message Threads(message-threads-2026-08-12) 동작 원리와 5분 만료·리플레이 설계를 정리한다. 프롬프트 캐싱과의 과금 차이, 고정 필드 무결성, 브레이크포인트 4개 상한 검증을 살펴본다.
클라이언트의 전체 대화 이력 중 신규 턴(Delta)만 전송해 서버의 5분 유효 메모리 스레드와 최대 4개 프롬프트 캐시 슬롯을 거쳐 추론으로 이어지는 흐름을 표현한 개념 일러스트다. 실제 콘솔 화면이 아니다.
2026년 11월 2일 Anthropic이 Messages API의 장기 대화와 에이전트 루프에서 매 턴 전체 대화 이력을 다시 업로드하지 않고 신규 메시지만 전송해 첫 토큰 도달 시간(TTFT)을 줄이는 Message Threads(message-threads-2026-08-12 베타)를 공개했다. 첫 요청에서 thread: {"type": "create"}로 전체 대화를 보내면 서버가 응답 ID에 대화를 보관하고, 이후 턴에는 thread: {"type": "continue", "previous_message_id": "..."}와 새 메시지만 보내는 구조다.
결론부터 말하면 Message Threads는 ‘네트워크 업로드 및 토큰화·검증 지연’을 줄이는 전송 계층 최적화일 뿐, 프롬프트 캐싱(cache_control)이나 클라이언트의 원본 대화 보관을 대체하지 않는다. 스레드를 써도 모델은 매 턴 전체 대화를 다시 읽으므로 cache_control을 별도로 설정하지 않으면 입력 토큰 비용은 그대로 청구된다. 또한 서버 스레드는 마지막 요청 후 5분이 지나면 만료되고 최대 1시간 이내에 삭제되는 최선형(Best-effort) 메모리 상태이므로, 클라이언트가 항상 전체 대화를 직접 들고 있다가 404 thread_not_found 발생 시 create 요청으로 즉시 리플레이(Replay)해야 한다.
이 글은 Claude Platform 공식 릴리스 노트와 Message Threads 공식 문서, Prompt Caching 문서를 바탕으로 필드 제약, 캐시 브레이크포인트 한도, 리플레이 복구 설계를 정리한 가이드다. 실제 프로덕션 트래픽의 TTFT 벤치마크를 직접 측정한 결과는 포함하지 않으며, 로컬 Python 코드는 요청 무결성 및 리플레이 변환 규칙만 검증한다.
1. Message Threads, 프롬프트 캐싱, Managed Agents 세션은 역할이 다르다
이름이 비슷해 혼동하기 쉽지만, Claude 플랫폼에서 대화를 다루는 세 가지 기능은 해결하는 병목과 수명 주기가 완전히 다르다.
| 구분 항목 | Message Threads ( message-threads-2026-08-12) | Prompt Caching ( cache_control) | Managed Agents Session Threads |
| 핵심 해결 과제 | 매 턴 전체 대화를 재업로드·재검증·재토큰화하는 전송 및 전처리 비용 절감 (TTFT 단축) | 동일한 프롬프트 프리픽스의 KV 캐시를 재사용해 모델 연산 시간과 입력 토큰 비용 절감 (읽기 0.1배, Fable 5.1·Mythos 5.1은 0.025배) | 관리형 샌드박스·도구 실행 루프·메모리 스토어를 포함하는 에이전트 실행 세션 상태 관리 |
| 토큰 과금 영향 | 과금 할인 없음. 전송 바이트만 줄어들며 모델이 읽는 전체 대화 토큰이 그대로 과금됨 | 직접적인 비용 절감. 5분 TTL 쓰기 1.25배, 1시간 TTL 쓰기 2배, 캐시 읽기는 기본 입력 단가의 10%(일부 모델 2.5~5%) | 세션 내 매 모델 요청마다 사용한 토큰 및 샌드박스 실행 비용 과금 (세션 예산 상한 설정 가능) |
| 수명 및 저장 방식 | 서버 메모리에만 임시 보관. 마지막 요청 후 5분 뒤 만료, 최대 1시간 이내 영구 삭제 (조회·삭제 API 없음) | 기본 5분(또는 1시간) TTL. 요청 시작 시점 기준으로 수명이 계산되며 히트 시 갱신됨 | 영속적인 세션 리소스로 저장되며 GET /v1/sessions/...로 이력 및 스트림 조회 가능 |
| 원본 상태 책임 (Source of Truth) | 클라이언트 애플리케이션. 서버가 저장하지 못했거나 만료되면 클라이언트가 전체 이력을 다시 보내야 함 | 요청 페이로드 자체. 캐시 미스 시에도 요청에 전체 프롬프트가 포함되어 있으므로 정상 실행됨 | Anthropic 관리형 에이전트 서버가 세션 이벤트 로그를 보관함 |
즉, 50만~100만 토큰에 달하는 긴 에이전트 루프에서 네트워크 업로드 크기와 TTFT를 줄이려면 Message Threads를 켜고, 입력 토큰 요금까지 줄이려면 반드시 최상위 cache_control: {"type": "ephemeral"}(또는 블록 단위 브레이크포인트)을 함께 지정해야 한다.
2. Create-only·Fixed·Free 필드 구분과 thread_fingerprint_mismatch 방지
Message Threads를 직접 cURL이나 기본 messages.create로 호출할 때 가장 많이 겪는 400 오류는 continue 요청에 넣어야 할 필드와 빼야 할 필드를 혼동하는 데서 나온다. 공식 문서는 요청 필드를 세 그룹으로 엄격히 나눈다.
| 필드 분류 | 대상 파라미터 | create 요청 (첫 턴 / 리플레이) | continue 요청 (후속 턴) | 위반 시 발생하는 오류 및 주의사항 |
| Create-only (생성 전용) | system, tools | 전체 값과 내부 cache_control 브레이크포인트를 포함해 전송 | 반드시 생략(Omit). null이나 빈 배열로 보내도 안 됨 | continue에 system이나 tools 키가 존재하면 즉시 400 invalid_request_error 반환 |
| Fixed (고정 필드) | model, tool_choice, thinking, mcp_servers(인증 토큰 제외), context_management, output_config, container 내 skills, 최상위 cache_control, anthropic-beta 목록, fallbacks 유무 | 초기 설정값으로 전송 | 매 턴 동일한 값으로 반복 전송 필수. 서버가 mcp_servers URL이나 container ID를 보관하지 않으므로 매번 보내야 함 | 값이 하나라도 다르면 400 thread_fingerprint_mismatch 반환 (오류 메시지에 불일치 필드명 명시). 값을 바꾸려면 새 create로 스레드를 다시 시작해야 함 |
| Free (자유 변경 필드) | max_tokens, stop_sequences, stream, metadata, service_tier 등 | 원하는 값으로 전송 | 턴마다 자유롭게 변경 가능 | 단, 응답이 max_tokens 부족으로 도구 호출 도중 잘리면 서버가 해당 턴을 스레드에 저장하지 않아 다음 턴에 404가 발생함 |
여기서 두 가지 실무 함정을 주의해야 한다.
- 프롬프트 캐시 브레이크포인트 4개 상한과 누적 계산:
thread를 사용하는 요청은 최대 4개의cache_control브레이크포인트만 가질 수 있다. 이때 최상위cache_control이 1개로 계산될 뿐 아니라, 첫create요청의system과tools에 달아둔 브레이크포인트가 모든 후속continue요청에서도 계속 개수에 합산된다. 반면 이전 턴의 메시지 블록에 달았던 브레이크포인트는continue시 합산되지 않지만, 만료 후 전체 대화를create로 리플레이할 때는 과거 메시지 블록의 브레이크포인트가 한꺼번에 합산되어 4개를 초과하면400 thread_unsupported_request(unsupported: ["cache_control"])가 발생한다.
- 사고 블록(Thinking Blocks)과 시스템·도구 변경의 충돌: Claude Opus 5.5, Sonnet 5.5, Haiku 5.5, Fable 5.1 등에서
system이나tools를 바꾸기 위해 새create요청을 보낼 때, 이전 대화 이력에 있던thinking및redacted_thinking블록을 그대로 포함하면 프리픽스 불일치로400오류가 난다. 시스템 프롬프트나 도구를 수정해 새 스레드를 열 때는 이전 턴의 사고 블록을 이력에서 제거하고 보내야 한다.
3. 스레드 만료·거절·폴백 모델 응답 시 복구(Replay) 규칙
Message Threads는 어펜드 전용(Append-only)이며, 각 응답 ID에서는 딱 한 번만 continue할 수 있다. 운영 환경에서는 다음 다섯 가지 상황에서 반드시 복구 경로가 동작해야 한다.
| 상황 및 오류 코드 | 발생 원인 | 필수 복구 동작 (Client Replay Protocol) |
404 thread_not_found | 5분 유휴 만료, 1시간 최대 수명 경과, 이전 턴이 max_tokens로 잘려 미저장됨, 서로 다른 API 키·워크스페이스·IAM 역할로 호출, 텍스트 전용 스레드에 첫 이미지 추가 등 | 클라이언트가 보관 중인 전체 대화 이력에서 이전 메시지들의 cache_control을 정리한 뒤 system·tools와 함께 새 thread: {"type": "create"} 요청으로 리플레이한다. 리플레이 직후 첫 continue에서도 또 404가 나면 해당 대화를 스레드로 저장할 수 없는 경우이므로 thread 파라미터 없이 무상태 모드로 전환한다 |
400 thread_already_continued / thread_continue_in_flight | 스트림 끊김 등으로 같은 previous_message_id에 대해 continue를 중복 호출했거나 이전 요청이 아직 생성 중임 | 진행 중인 요청이 끝날 때까지 기다려 새 응답 ID로 이어가거나, 전체 대화를 새 create 요청으로 리플레이한다 (스트리밍 시에는 message_start가 아니라 반드시 message_stop 이벤트 수신 후에 다음 continue를 보내야 함) |
stop_reason: "refusal" (안전 거절) | 모델 안전 분류기가 요청을 거절함. 서버 스레드에 거절된 턴이 이미 남아 있을 수 있으며 continue로는 이를 지울 수 없음 | 절대 해당 응답 ID에서 continue하지 않는다. 거절을 유발한 턴을 제외하거나 표현을 수정한 뒤, 거절 이전까지의 대화 이력으로 새 create 요청을 보낸다 |
usage.iterations 내 fallback_message 존재 | 서버 사이드 폴백( server-side-fallback-2026-07-01)이 동작해 예비 모델이 답변을 작성함. 스레드는 단일 모델만 유지하므로 폴백 모델이 쓴 턴은 저장되지 않음 | 404를 기다리지 말고 다음 턴에 즉시 전체 대화(폴백 응답 포함)를 새 create 요청으로 보낸다. 원래 모델로 재시도하거나, 폴백 모델로 아예 model을 고정해 새 스레드를 시작한다 |
400 thread_unsupported_request | Message Batches, Token Counting, 온디맨드 Compaction( compact-2026-09-04), HIPAA 준비 조직, Inference Hooks 활성화 조직, 브레이크포인트 4개 초과 등 미지원 기능 결합 | error.details.unsupported 배열에서 원인 항목(cache_control, context_management, hipaa, inference_hooks 등)을 확인해 제거하거나 thread 없이 호출한다 |
4. 로컬 Python으로 구현·검증하는 스레드 요청 빌더와 리플레이 정리기
SDK의 client.beta.messages.thread(...) 헬퍼를 쓰면 기본적인 리플레이와 system·tools 생략을 자동으로 처리해 주지만, 자체 게이트웨이나 오케스트레이터에서 REST API를 직접 호출한다면 아래와 같은 상태 관리기가 필요하다.
다음 Python 코드는 ① create 스냅샷 기준 고정 필드 무결성 검사, ② continue 시 생성 전용 필드 제거, ③ 404 리플레이 시 과거 메시지의 cache_control 브레이크포인트를 자동으로 벗겨내 4개 상한 오류를 예방하는 로직을 구현한 것이다.
직접 실행해 확인한 범위
로컬 Python 환경에서 위 검증기와 브레이크포인트 정리 함수를 실행해 다음 네 가지 시나리오를 확인했다.
- 정상
continue요청: 최상위cache_control(1개) + 초기system(1개) + 초기tools(1개) + 신규 메시지 블록(1개)으로 총 4개 브레이크포인트를 유지하고 자유 필드인max_tokens만 1024에서 2048로 변경했을 때 오류 0건으로 통과했다.
- 생성 전용 필드 혼입 탐지:
continue요청에"system": None을 넣자400_create_only_present:system으로 즉시 거부되었다.
- 고정 필드 변경 탐지:
continue도중model을claude-opus-5-5에서claude-sonnet-5-5로 바꾸자400_thread_fingerprint_mismatch:model을 정확히 잡아냈다.
- 리플레이 시 브레이크포인트 누적 해소: 3턴 동안 매 사용자 메시지에 브레이크포인트를 추가한 이력을 그대로 새
create요청에 넣으면 총 6개(최상위 1 + system 1 + tools 1 + 메시지 3)가 되어 4개 상한을 넘기지만,strip_earlier_message_breakpoints()적용 후에는 정확히 4개로 줄어들어 검증을 통과했다.
5. 11월 30일 예정된 Claude API 변경과 함께 점검할 체크리스트
- 프롬프트 캐싱 병행 설정: Message Threads만 켜서 업로드 크기만 줄이지 말고, 최상위
cache_control: {"type": "ephemeral"}을 고정 필드로 함께 보내 입력 토큰 비용까지 절감한다.
- 무상태 폴백(
continue_unthreaded) 준비: 리플레이 직후 첫continue에서도404 thread_not_found가 반복되면 대화가 서버 스레드 저장 한도를 넘은 것이므로 즉시thread파라미터를 빼고 일반 Messages 요청으로 전환한다(SDK 헬퍼 사용 시unsupported_request_behavior="continue_unthreaded"설정).
- 온디맨드 Compaction 및 토큰 카운팅 분리: 스레드 요청에는
compaction파라미터를 함께 보낼 수 없으므로, 대화를 요약(Compaction)할 때는 요약을 일반 메시지로 넣은 새create요청으로 스레드를 새로 시작한다. 토큰 수 계산(/v1/messages/count_tokens) 역시thread필드 없이 전체 대화로 호출해야 한다.
- 2026년 11월 30일 모델·옵션 종료 일정 확인: 같은 릴리스 노트에서 예고된 대로 2026년 11월 30일에
claude-opus-4-8의 빠른 모드(speed: "fast")와claude-sonnet-4-5-20250929모델이 제거된다. 제거 이후에는 표준 속도로 자동 폴백되지 않고 즉시 오류를 반환하므로, 에이전트 루프를claude-opus-5-5또는claude-sonnet-5-5로 미리 마이그레이션하는 편이 안전하다.
참고 자료
확인 기준일: November 4, 2026
English version: Claude Messages API Message Threads: Separate 5-Minute Server State, Prompt Caching, and 404 Replays