RFC 10008은 요청 본문을 허용하면서도 안전하고 멱등적이며 캐시 가능한 새로운 HTTP 메서드 QUERY를 정의한다.
2026년 6월 17일
RFC 10008은 2026년 6월 15일에 발행되었으며 새로운 HTTP 메서드 QUERY를 정의합니다. 이것은 제가 API를 만들어 온 내내 존재해 온 공백을 메웁니다. 서버에 보내서 무엇을 돌려받고 싶은지 설명할 데이터가 있지만, GET에는 본문이 없고 POST는 안전하지도 멱등적이지도 않습니다. QUERY는 요청 본문을 받아들이면서도 안전하고, 멱등적이며, 캐시 가능한 메서드를 제공합니다.
JSON-RPC API와 통신하는 SDK를 만들어 본 적이 있다면 이 고통을 느껴 보셨을 것입니다. JSON-RPC는 설계상 메서드와 매개변수를 설명하는 JSON 페이로드를 보냅니다. 그 페이로드는 본문에 들어가야 하고, 이는 곧 POST를 의미하며, 다시 말해 캐시와 중간 매개체는 모든 요청을 상태를 변경하는 작업으로 취급합니다. 재시도 로직은 복잡해집니다. CDN 캐싱은 고려 대상에서 제외됩니다. 결국 HTTP의 내장 메커니즘이 도움을 줄 수 없기 때문에 애플리케이션 수준의 캐싱을 직접 구축하게 됩니다.
QUERY는 이것을 바꿉니다. 의미 체계는 단순합니다. 본문을 보내고, 응답을 받으며, 전체 교환은 캐싱과 안전성의 관점에서 GET처럼 취급됩니다.
Go의 net/http는 이미 http.NewRequest와 함께 임의의 메서드 문자열을 사용할 수 있게 해 주므로, QUERY를 사용하는 SDK 코드는 예상하는 모습과 거의 비슷합니다:
body, _ := json.Marshal(map[string]any{
"jsonrpc": "2.0",
"method": "getScore",
"params": []any{"0xABC123", "latest"},
"id": 1,
})
req, _ := http.NewRequestWithContext(ctx, "QUERY", "https://rpc.example.com", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
새로운 의존성은 필요하지 않습니다. HTTP 메서드는 그저 문자열이기 때문에 표준 라이브러리가 이를 처리합니다.
reqwest에서는 reqwest::Method를 사용해 사용자 정의 메서드를 정의할 수 있습니다:
use reqwest::{Client, Method};
let client = Client::new();
let query_method = Method::from_bytes(b"QUERY").unwrap();
let resp = client
.request(query_method, "https://rpc.example.com")
.header("Content-Type", "application/json")
.body(r#"{"jsonrpc":"2.0","method":"getScore","params":["0xABC123","latest"],"id":1}"#)
.send()
.await?;
제가 기다려 온 부분은 2.7절에 있습니다. QUERY에 대한 응답은 캐시 가능하며, 캐시 키는 요청 본문과 그 메타데이터를 반드시 포함해야 합니다. 이는 리버스 프록시나 CDN이 Content-Type과 본문 바이트를 함께 보고 동일한 질의에 대해 캐시된 응답을 제공할 수 있음을 뜻합니다. 캐시는 적중률을 높이기 위해 본문을 정규화할 수도 있습니다. 예를 들어 JSON 키 순서를 재정렬하거나, 의미 없는 공백을 제거할 수 있습니다.
모든 요청이 의미적으로는 읽기 작업이지만 구조적으로는 POST인 RPC 스타일 API에서는 이것이 의미 있는 개선입니다. 위에 별도의 맞춤형 계층을 구축하지 않고도 HTTP 고유의 캐싱을 얻을 수 있습니다.
이 RFC는 짧고 읽기 쉽습니다. HTTP API를 만들거나 사용하는 분이라면 20분 투자할 가치가 있습니다.