소스 정보
- 저장소
- puk0806/gugbab-claude
- 최근 소스 활동
- 2026년 8월 12일 00:35
- 감지된 SKILL.md 언어
- 한국어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/puk0806/gugbab-claude --skill reqwest명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
DDD(Domain-Driven Design) 아키텍처 핵심 패턴 - 유비쿼터스 언어, 서브도메인, 바운디드 컨텍스트, Aggregate, Entity/VO, 도메인 서비스/이벤트, 레이어드 아키텍처
대규모 React/Next.js 프로젝트를 layer-first(types/·utils/·hooks/·api/·components/ 밑에 도메인이 반복되는 구조)에서 domain-first(feature/도메인 우선) 구조로 전환하는 설계 기준과 절차. Feature-Sliced Design 2.1 정본(layers 6종·slices·segments·import 규칙·@x 크로스임포트·public API), FSD를 쓰지 않는 경량 대안(features + shared 2~3계층 + ESLint import/no-restricted-paths), Next.js App Router 공존 전략(route group `()`·private folder `_`·colocation), Turborepo/Nx 모노레포에서 폴더↔패키지 승격 기준, colocation과 배럴 파일 성능 트레이드오프, 도메인 경계 역추출(import 그래프·change coupling·용어 클러스터), 전환 실패 패턴(shared 비대화·entities 남용·순환 의존·도메인=라우트 착각·조기 추상화). 도메인 개념 자체(바운디드 컨텍스트·유비쿼터스 언어)는 `architecture/ddd` 스킬을 참조한다.
소스 파일 수천 개 규모 프론트엔드 코드베이스를 멈추지 않고 점진 재구조화하는 실행 전략 - Strangler Fig / Branch by Abstraction / Parallel Change, ts-morph·jscodeshift codemod, PR 분할·검증 게이트·되돌리기, 테스트 없는 코드의 안전망, 작업 순서 설계와 위반 수 기반 진행 추적
| name | reqwest |
| description | Rust reqwest HTTP 클라이언트 핵심 패턴 — GET/POST, JSON, 헤더, 스트리밍, 에러 처리, Client 재사용 |
소스: https://docs.rs/reqwest/latest/reqwest/ | https://github.com/seanmonstar/reqwest 검증일: 2026-08-12
주의: 이 문서는 reqwest 0.12.x 기준으로 작성되었습니다. 0.13.x (최신 0.13.11, 2026-05-28 릴리즈)가 출시되어 Breaking Change가 있으므로 신규 프로젝트는 마이그레이션 노트를 참조하세요.
reqwest 0.13으로의 마이그레이션 시 주요 Breaking Change (0.12 → 0.13):
- rustls가 기본 TLS로 변경(aws-lc 기반),
rustls-tlsfeature가rustls로 rename- MSRV이 1.85로 상향
ClientBuilder::dns_resolver가dns_resolver2로 교체reqwest::Url의 serde Deserialize 지원이 별도 feature 필요- 0.11.x 이하(hyper 0.14 기반) 대비 API 변경 있음
# reqwest 0.12.x (현재 문서 기준) — 0.13이 2026-05-28 릴리즈됨, 마이그레이션 주의사항 참조
[dependencies]
reqwest = { version = "0.12", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
json feature: .json() 메서드 활성화 (serde 연동)stream feature: bytes_stream() 메서드 활성화 (스트리밍 응답)Client는 내부에 커넥션 풀을 유지한다. 요청마다 새로 만들지 않고 재사용해야 한다.
use reqwest::Client;
// 기본 Client — 대부분의 경우 충분
let client = Client::new();
// ClientBuilder — 타임아웃, 기본 헤더, 프록시 등 설정 시
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
use std::time::Duration;
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, HeaderValue::from_str("Bearer sk-xxx")?);
let client = Client::builder()
.default_headers(headers) // 모든 요청에 적용
.timeout(Duration::from_secs(30)) // 요청별 타임아웃
.connect_timeout(Duration::from_secs(10))
.pool_max_idle_per_host(10) // 호스트별 유휴 커넥션 수
.build()?;
Client::new vs ClientBuilder:
Client::new(): 기본 설정, 빠른 프로토타이핑Client::builder(): 타임아웃, 기본 헤더, TLS 설정 등 커스터마이징 필요 시// 단순 텍스트 응답
let body = client.get("https://httpbin.org/get")
.send()
.await?
.text()
.await?;
// JSON 역직렬화
#[derive(serde::Deserialize)]
struct ApiResponse {
origin: String,
url: String,
}
let resp: ApiResponse = client.get("https://httpbin.org/get")
.send()
.await?
.json()
.await?;
#[derive(serde::Serialize)]
struct CreateRequest {
model: String,
max_tokens: u32,
messages: Vec<Message>,
}
#[derive(serde::Serialize)]
struct Message {
role: String,
content: String,
}
let request_body = CreateRequest {
model: "claude-sonnet-5".into(),
max_tokens: 1024,
messages: vec![Message {
role: "user".into(),
content: "Hello".into(),
}],
};
let response = client.post("https://api.anthropic.com/v1/messages")
.header("x-api-key", api_key)
.header("anthropic-version", "2023-06-01")
.json(&request_body) // Content-Type: application/json 자동 설정
.send()
.await?;
.json(&body) 호출 시 Content-Type: application/json 헤더가 자동으로 설정된다.
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
// 방법 1: 요청별 개별 헤더
let resp = client.post(url)
.header(AUTHORIZATION, format!("Bearer {}", token))
.header(CONTENT_TYPE, "application/json")
.header("x-api-key", api_key) // 커스텀 헤더는 문자열 사용
.send()
.await?;
// 방법 2: HeaderMap 일괄 설정
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", token))?);
headers.insert("x-api-key", HeaderValue::from_str(api_key)?);
let resp = client.post(url)
.headers(headers)
.send()
.await?;
Claude API의 Server-Sent Events 스트리밍 응답 처리 패턴.
use futures_util::StreamExt;
let response = client.post("https://api.anthropic.com/v1/messages")
.header("x-api-key", api_key)
.header("anthropic-version", "2023-06-01")
.json(&serde_json::json!({
"model": "claude-sonnet-5",
"max_tokens": 1024,
"stream": true,
"messages": [{"role": "user", "content": "Hello"}]
}))
.send()
.await?;
// stream feature 필요
let mut stream = response.bytes_stream();
while let Some(chunk) = stream.next().await {
let chunk = chunk?;
let text = String::from_utf8_lossy(&chunk);
// SSE 파싱: "data: " 접두사 처리
for line in text.lines() {
if let Some(data) = line.strip_prefix() {
data == {
;
}
: serde_json::Value = serde_json::(data)?;
}
}
}
주의: SSE 청크가 이벤트 경계와 정확히 일치하지 않을 수 있다. 프로덕션에서는 버퍼링 로직 또는
eventsource-stream같은 SSE 파서 크레이트 사용을 권장한다.
futures-util 의존성 필요:
futures-util = "0.3"
use reqwest::StatusCode;
#[derive(Debug)]
enum ApiError {
Network(reqwest::Error),
Status { code: StatusCode, body: String },
Parse(serde_json::Error),
}
impl From<reqwest::Error> for ApiError {
fn from(e: reqwest::Error) -> Self {
ApiError::Network(e)
}
}
async fn call_api(client: &Client, url: &str) -> Result<String, ApiError> {
let response = client.get(url).send().await?;
// 상태 코드 확인
let status = response.status();
if !status.is_success() {
let body = response.text().await.unwrap_or_default();
return Err(ApiError::Status { code: status, body });
}
Ok(response.text().await?)
}
// error_for_status() — 4xx/5xx를 reqwest::Error로 변환
let = client.(url)
.()
.?
.()?;
reqwest::Error 주요 판별 메서드:
is_timeout() — 타임아웃 발생 여부is_connect() — 연결 실패 여부is_status() — HTTP 상태 코드 에러 여부status() — Option<StatusCode> 반환use reqwest::{Client, header::{HeaderMap, HeaderValue}};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Serialize)]
struct MessagesRequest {
model: String,
max_tokens: u32,
messages: Vec<Message>,
}
#[derive(Serialize, Deserialize)]
struct Message {
role: String,
content: String,
}
#[derive(Deserialize)]
struct MessagesResponse {
id: String,
content: Vec<ContentBlock>,
model: String,
stop_reason: Option<String>,
}
#[derive(Deserialize)]
struct ContentBlock {
#[serde(rename = "type")]
block_type: String,
text: Option<String>,
}
fn build_client(api_key: &str) -> reqwest::Result<Client> {
let mut headers = HeaderMap::new();
headers.insert("x-api-key", HeaderValue::from_str(api_key).unwrap());
headers.insert("anthropic-version", HeaderValue::from_static("2023-06-01"));
Client::()
.(headers)
.(Duration::())
.()
}
(
client: &Client,
model: &,
user_message: &,
) <, < std::error::Error>> {
= MessagesRequest {
model: model.(),
max_tokens: ,
messages: [Message {
role: .(),
content: user_message.(),
}],
};
: MessagesResponse = client
.()
.(&body)
.()
.?
.()?
.()
.?;
(resp.content.()
.(|b| b.text)
.collect::<<_>>()
.())
}
reqwest 자체에는 재시도 기능이 없다. 직접 구현하거나 reqwest-middleware + reqwest-retry 크레이트 사용.
주의: reqwest 0.12.23(2025-08-08 릴리즈)부터
reqwest::retry모듈과ClientBuilder::retries(policy)메서드가 내장됨. 단, 기본 내장 retry는 HTTP/2 REFUSED_STREAM 등 프로토콜 레벨 NACK 재시도 용도이며, 커스텀 정책이 필요한 경우 reqwest-middleware + reqwest-retry 조합이 더 유연함.
// 수동 재시도 (지수 백오프)
use std::time::Duration;
use tokio::time::sleep;
async fn retry_request(
client: &Client,
url: &str,
max_retries: u32,
) -> reqwest::Result<reqwest::Response> {
let mut last_err = None;
for attempt in 0..max_retries {
match client.get(url).send().await {
Ok(resp) if resp.status().is_server_error() => {
last_err = Some(resp.error_for_status().unwrap_err());
}
Ok(resp) => return Ok(resp),
Err(e) if e.is_timeout() || e.is_connect() => {
last_err = Some(e);
}
Err(e) => return Err(e), // 재시도 불가한 에러
}
let delay = Duration::from_millis(100 * .(attempt));
(delay).;
}
(last_err.())
}
주의:
reqwest-middleware0.4.x /reqwest-retry0.7.x 기준. 버전 호환성 확인 필요.