crimson-crab: Claude를 위한 프로덕션급 Rust SDK (그리고 왜 tokio가 wasm32에서 사라지는가)
요약
Anthropic의 Claude API를 위한 프로덕션급 Rust SDK인 crimson-crab이 출시되었습니다. 이 SDK는 Claude의 최신 기능들을 지원하며, 타겟 환경(Native vs WASM)에 따른 의존성 변화와 효율적인 클라이언트 사용법을 다룹니다.
핵심 포인트
- Claude API 전용 Rust SDK인 crimson-crab v0.1.0 출시
- 프롬프트 캐싱, 도구 사용, 구조화된 출력 등 Claude의 핵심 기능 지원
- WASM 타겟 시 reqwest가 fetch 백엔드로 전환되어 tokio 의존성 제거
- Client는 Clone + Send + Sync를 지원하여 상태 공유에 최적화됨
네이티브 타겟(native target)에서 cargo tree --edges normal --invert tokio를 실행하면 tokio v1.52.4가 빌드에 명확히 포함됩니다. 이는 reqwest를 통해 hyper, hyper-rustls, hyper-util, tokio-rustls, tokio-util, 그리고 tower를 거쳐 전달됩니다.
동일한 쿼리를 wasm32-unknown-unknown 타겟으로 다시 실행하면 tokio의 개수는 0이 됩니다.
동일한 크레이트(crate), 동일한 기본 기능(default features), 동일한 명령어를 사용했지만 두 개의 올바른 결과가 나왔습니다. wasm32 환경에서 reqwest는 브라우저의 fetch 백엔드로 전환되고, hyper는 의존성 그래프에서 벗어나며, tokio도 함께 사라집니다.
이것은 무엇인가
crimson-crab 🦀은 Anthropic의 Claude API를 위한 Rust SDK입니다. v0.1.0 버전이 2026년 7월 16일에 crates.io에 게시되었습니다. 이 SDK는 Messages 및 토큰 카운팅(token counting), 누적된 최종 Message를 포함하는 세밀한 SSE 스트리밍(SSE streaming), 커스텀 도구 및 서버-도구 패스스루(server-tool passthrough)를 지원하는 도구 사용(tool use), 확장 및 적응형 사고(extended and adaptive thinking), 5분 및 1시간 TTL을 지원하는 프롬프트 캐싱(prompt caching), JSON Schema를 통한 구조화된 출력(structured output), Message Batches, 그리고 Models 엔드포인트를 다룹니다.
범위는 의도적으로 좁게 설정되었습니다: 오직 Claude만을 대상으로 합니다. 만약 여러 모델 벤더를 대상으로 구축하고 있다면, 멀티 프로바이더 프레임워크가 더 적합할 것이며, rig와 genai가 그 역할을 매우 잘 수행합니다. crimson-crab은 이미 Claude를 선택했으며 Anthropic이 제공하는 기능 전체를 그대로 사용하고자 하는 팀을 위한 것입니다.
cargo add crimson-crab
use crimson_crab::model_ids::CLAUDE_OPUS_4_8;
use crimson_crab::prelude::*;
...
위 코드 스니펫에 있는 #[tokio::main]에 주목하세요. 네이티브 타겟에서는 여전히 런타임(runtime)을 가져오지만, 이 크레이트는 단순히 사용자를 대신해 런타임을 선택하지 않을 뿐입니다. tokio는 crimson-crab의 매니페스트(manifest) 내 [dev-dependencies] 아래에 위치하며, 테스트 스위트와 7개의 예제 코드에서 이를 사용합니다. Client는 Clone + Send + Sync를 지원하며 하나의 커넥션 풀(connection pool)을 공유하므로, 한 번 빌드하여 axum 상태(state)나 일반 구조체 필드에 넣어두기만 하면 됩니다. Arc나 Mutex가 필요 없습니다.
당신의 타겟에 의존하는 의존성
다음은 네이티브 트리(native tree)의 전체 모습입니다. 깔끔하게 한 줄로 요약하는 것은 거짓말이 될 것이기 때문입니다:
$ cargo tree --edges normal --invert tokio
tokio v1.52.4
├── hyper v1.10.1
...
그곳에는 일곱 개의 서로 다른 경로가 reqwest로 수렴합니다. 네이티브 타겟(native target)에서 tokio는 빌드에 포함되어 있으며, 반드시 포함되어야만 합니다. reqwest의 기본 백엔드(backend)는 hyper이고, hyper는 tokio 위에서 실행되기 때문입니다. 네이티브 환경에서 tokio가 없다고 광고하는 reqwest 기반의 크레이트(crate)가 있다면 그것은 틀린 것이며, cargo tree -i tokio 명령어가 약 1초 만에 그 논쟁을 종결시킵니다.
crimson-crab이 실제로 주장하는 바는 더 좁은 범위입니다. tokio는 이 라이브러리의 직접적인 의존성(direct dependency)이 아닙니다. tokio는 매니페스트(manifest)의 [dev-dependencies] 아래에만 나타나며, 네이티브 빌드에서는 reqwest를 통해 전이적(transitively)으로 전달됩니다. 또한 공개 API(public API) 중 그 어떤 것도 런타임(runtime) 타입을 명시하지 않습니다. 스트리밍(Streaming)은 futures_core::Stream을 반환하고, MessageStream은 Send + Unpin이며, crimson_crab::Error는 Send + Sync + std::error::Error입니다. 이 크레이트는 사용자의 실행기(executor)에 대해 어떠한 의견도 갖지 않으며, 실행기를 요구하지도 않습니다.
use crimson_crab::prelude::*;
use futures_util::StreamExt;
...
저 단순한 Stream이 바로 wasm32 결과물을 가능하게 만드는 핵심입니다. wasm32 환경에서 reqwest는 브라우저의 fetch 백엔드로 해결(resolve)되고, hyper는 그래프에서 빠지며, tokio도 함께 빠지게 되어 최종적으로 개수가 0에 도달하게 됩니다.
cargo check --target wasm32-unknown-unknown은 기본 기능(default features) 상태에서 통과합니다. 다만 이것이 무엇을 보장하는지에 대해서는 정확히 짚고 넘어가야 합니다. cargo check는 타입 체크(type-checks)와 빌림 검사(borrow-checks)를 수행할 뿐, 코드 생성(codegen)이나 링크(link)를 수행하지는 않습니다. 따라서 이것은 이 크레이트와 그 의존성 그래프가 별도의 기능 조절(feature juggling)이나 default-features = false라는 주문을 외울 필요 없이 wasm32를 위해 깔끔하게 해결된다는 증거입니다. 이것은 wasm 산출물(artifact)도 아니며, 제가 브라우저 데모를 약속하는 것도 아닙니다. 여러분의 애플리케이션이 브라우저에서 링크되어 실행될지 여부는 통합(integration)의 문제이며, 이 포스트는 그에 대해 어떠한 주장도 하지 않습니다.
따라서 솔직한 헤드라인은 직접적인 의존성 목록(dependency list)과 공개 API 표면(public API surface)에 관한 것이며, 거기서 멈춥니다. 그보다 더 느슨하게 말한다면, 독자가 명령어 하나를 실행하는 순간 당신을 신뢰하지 않게 될 것입니다. 오직 정확한 버전만이 cargo tree와의 접촉에서 살아남습니다.
문서가 부패할 수 없는 이유
cargo test --all-features를 실행하면 191개 통과, 0개 실패가 나옵니다. 흥미로운 부분은 그 구성입니다:
| 종류 | 개수 |
|---|---|
단위 테스트 (Unit tests, src/ 내) | 43 |
| ... |
191개 중 113개입니다. 이 테스트 스위트의 대다수는 실행되는 문서(documentation)입니다.
여기서도 정밀함이 중요합니다. 왜냐하면 "모든 예제가 실행된다"는 말은 사람들이 흔히 느슨하게 내뱉는 말 중 하나이기 때문입니다. 113개의 문서 테스트(doc-tests) 중 21개는 no_run으로 표시되어 있습니다: cargo test는 실제 API를 대상으로 이를 컴파일하고 타입 체크(type-check)를 수행하지만, 실행은 하지 않습니다. 실행하려면 활성화된 API 키가 필요하기 때문입니다. 나머지 92개는 컴파일되고 실행됩니다. ignore로 표시된 것은 없으므로, 컴파일러가 아예 보지 못하는 문서 예제는 없습니다. API와 동기화되지 않고 어긋나는 문서 예제는 18개월 후에 누군가가 이슈(issue)를 제기할 정도로 조용히 방치된 오래된 코드 조각이 되지 않습니다. 대신, 그것을 망가뜨린 커밋에서 즉시 빌드 오류(red build)로 나타납니다.
통합 테스트(integration tests)는 wiremock을 대상으로 실행되므로, 전체 스위트가 오프라인에서 작동합니다: API 키도, 네트워크도, 속도 제한(rate limits)도, 불안정한 테스트(flakes)도 없습니다. 비행기 안에서 리포지토리(repo)를 클론하여 성공적인 실행(green run)을 확인할 수 있습니다.
확인할 수 있는 패닉 프리(Panic-freedom)
수많은 라이브러리가 README의 한 단락에서 자신을 패닉 프리(panic-free)라고 설명합니다. 이 라이브러리는 약 10초 만에 확인이 가능합니다. src/lib.rs는 다음과 같이 시작합니다:
#![forbid(unsafe_code)]
#![cfg_attr(
not(test),
...
그리고 cargo clippy --all-features --all-targets를 실행하면 경고(warning) 0개로 통과합니다. not(test) 범위는 해당 금지 사항들이 라이브러리 빌드에 결합됨을 의미하며, 따라서 이 속성은 기계적인 이유로 유지됩니다: 컴파일러가 이를 위반하는 라이브러리 버전을 생성하는 것을 거부하기 때문입니다. deny(missing_docs)는 문서 테스트(doc-test) 개수의 정직함을 유지해 주는 숨은 공신이기도 합니다. 문서화되지 않은 공개 항목(public item)은 빌드를 즉시 실패시키기 때문입니다.
아직 존재하지 않는 모델로부터의 응답
크레이트(crate) 내의 모든 와이어 열거형(wire enum)은 Unknown이라는 캐치올(catch-all) 항목을 포함하고 있습니다: 콘텐츠 블록(content blocks), 스트림 이벤트(stream events), 델타(deltas), 중단 사유(stop reasons), 도구 정의(tool definitions), 캐시 TTL(cache TTLs), 사고 설정(thinking configs) 등이 이에 해당합니다. 인식되지 않은 변형(variant)은 가공되지 않은 JSON(raw JSON)으로 보존되었다가 변경 없이 다시 직렬화(re-serialized)되므로, 역직렬화(deserialization) 시 오류가 발생하지 않습니다. 이 열거형들은 #[non_exhaustive]로 선언되어 있어, 와일드카드 암(wildcard arm)을 통해 매칭할 수 있으며, 마이너 릴리스에서 새로운 기지 변형(known variant)을 추가하더라도 빌드가 깨지지 않습니다.
이것이 전체 메커니즘이며, 공교롭게도 113개의 문서 테스트(doc-tests) 중 하나이기도 합니다. src/types/content.rs에 나타난 그대로의 모습은 다음과 같습니다:
use crimson_crab::types::{ContentBlock, TextBlock};
let json = serde_json::json!({"type": "text", "text": "Hello"});
...
마지막 두 개의 단언(assertion)이 핵심입니다. 크레이트가 배포될 당시 아무도 발명하지 않았던 블록 타입이 깔끔하게 역직렬화되며, 다시 직렬화했을 때 도착했던 데이터와 바이트 단위로 동일하게(byte-equivalent) 돌아옵니다. 덕분에 프로세스는 계속 실행되고 로그에는 페이로드(payload)가 그대로 유지됩니다.
model 필드는 모든 곳에서 개방형 문자열(open string)로 처리되는데, 이는 한 단계 상위 수준에서 적용된 것과 동일한 원리입니다. 크레이트는 순수하게 편의를 위해 상수(CLAUDE_OPUS_4_8, CLAUDE_FABLE_5, CLAUDE_SONNET_5, CLAUDE_HAIKU_4_5)를 내보내지만(export), 해당 목록에 없는 모델이라도 일반 문자열로 전달하면 여전히 작동합니다. 베타 플래그(Beta flags) 역시 동일한 탈출구(escape hatch)를 가집니다. .beta("some-flag")는 anthropic-beta 플래그를 추가하고, .extra_field(key, value)는 최상위 바디 필드(top-level body field)를 설정하므로, 오늘 아침에 출시된 베타 기능도 SDK 릴리스를 기다릴 필요 없이 바로 사용할 수 있습니다.
재시도(Retries) 및 스트리밍 타임아웃
연결 오류, 타임아웃, 408, 409, 429 및 5xx 오류는 풀 지터(full-jitter) 지수 백오프(exponential backoff)를 적용하여 재시도합니다: 기본 0.5초, 최대 8초입니다. retry-after는 준수되며 최대 60초로 제한되므로, 악의적이거나 고장 난 서버가 재시도 루프를 한 시간 동안 붙잡아 둘 수 없습니다. 스트리밍 요청은 첫 번째 바이트가 전달되기 전까지만 재시도하며, 이는 토큰이 이미 네트워크 선로(wire)에 올라간 이후에는 유일하게 안전한 대응책입니다.
제가 가장 마음에 드는 스트리밍(streaming) 세부 사항은 타임아웃(timeout)입니다. 클라이언트는 전체 요청 마감 시간(total-request deadline) 대신 유휴 읽기(idle read) 타임아웃을 적용하므로, 길더라도 활발하게 흐르는 SSE 응답은 단순히 경과 시간 제한을 초과했다는 이유만으로 끊기지 않습니다. 만약 경과 시간을 계산하고 데이터가 여전히 도착하고 있는지 여부를 무시하는 HTTP 클라이언트로 인해, 긴 생성 과정이 정확히 30초 만에 단절되는 경험을 해본 적이 있다면, 왜 이 차이가 설계에서 중요한 위치를 차지하는지 이미 알고 계실 것입니다.
솔직한 상태 (Status, honestly)
v0.1.0, 2026년 7월 16일 발행. 단일 크레이트(crate): src/에 약 6,076라인, tests/에 1,445라인이 있으며, 7개의 실행 가능한 예제(basic, batches, prompt_caching, streaming, structured_output, thinking, tool_use)가 포함되어 있습니다. MSRV(Minimum Supported Rust Version) 1.75, edition 2021, MIT 또는 Apache-2.0 이중 라이선스입니다. MSRV를 높이는 것은 마이너 버전(minor-version) 변경 사항이 될 것입니다.
이 포스트에 포함되지 않은 내용: 벤치마크(benchmarks), 지연 시간(latency) 수치, 처리량(throughput) 수치. 측정된 것이 없으므로 인용하지 않았습니다. 채택 수치(adoption numbers) 또한 없는데, 이 글이 올라오기 불과 몇 시간 전에 출시되었기 때문이며 아직 존재하지 않습니다. 대신 제가 드릴 수 있는 것은 테스트 스위트(test suite), clippy 실행 결과, 그리고 의존성 트리(dependency tree)이며, 이 모든 것은 여러분의 컴퓨터에서 몇 분 안에 직접 재현할 수 있습니다. v0.1.0 버전으로서는 이것이 정직한 거래라고 생각합니다.
crimson-crab 찾기
- crimson-crab 프로젝트 사이트 — 원페이지 투어
- crates.io의 crimson-crab ·
cargo add crimson-crab - docs.rs/crimson-crab — API 레퍼런스 및 113개의 문서 테스트(doc-tests) 소스
- GitHub의 crimson-crab — README, ARCHITECTURE.md, 그리고 예제들
crimson-crab은 독립적인 오픈 소스 프로젝트이며 Anthropic과 관련이 없습니다. 이 포스트의 모든 수치는 fresh clone 상태에서 실행할 수 있는 명령어를 통해 얻은 것입니다: cargo test --all-features, cargo clippy --all-features --all-targets, 그리고 cargo tree.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기