
새로운 HTTP QUERY 메서드
4분 읽기
2026년 6월, HTTP QUERY 메서드가 RFC 10008로 표준화되었습니다. PATCH(2010) 이후 무려 16년 만의 새로운 표준 HTTP 메서드입니다.
검색 화면을 구현할 때마다 마주치던 의문이 하나 있습니다. 분명 데이터를 가져오는 '조회' 기능인데, 전달받은 API 명세서에는 GET이 아닌 POST 메서드로 정의되어 있는 경우가 많았기 때문입니다. 다중 필터, 정렬, 기간 검색이 얽힌 복잡한 화면일수록 어김없이 POST였습니다.
조회는 GET, 생성은 POST라는 명확한 원칙이 있음에도 왜 실무에서는 이 원칙이 통하지 않는 걸까요?
표준의 이상과 실무의 현실링크를 제목에 복사
RFC 9110(HTTP Semantics) 표준 명세에 따르면, GET을 쓸 때 모든 검색 조건은 URL 뒤의 쿼리 스트링(?param=value)에 담아야 합니다.
그런데 쿼리 스트링에만 의존하는 방식에는 몇 가지 뚜렷한 한계가 있습니다.
- 414 URI Too Long
- 다중 필터, 정렬, 기간 검색처럼 복잡한 조건을 쿼리 스트링에 담다 보면 URL이 길어질 수밖에 없고, 서버는 허용 길이를 넘긴 요청에 414 에러를 반환합니다.
- 데이터 구조의 한계
- 쿼리 스트링은 key=value 형태가 전부라, 배열이나 중첩 객체 같은 복잡한 필터 조건을 URL 인코딩으로 표현하기 어렵습니다.
- 보안 및 로그 노출
- URL에 담긴 검색 조건은 서버 로그, 브라우저 히스토리, 프록시 캐시 등 곳곳에 남습니다. 민감한 정보가 포함되면 그대로 보안 문제가 됩니다.
쿼리 스트링이 문제라면, 가장 먼저 떠오르는 대안은 GET 요청에 body를 담는 것입니다.
그럼 GET body나 POST를 쓰면 되지 않나?링크를 제목에 복사
기술적으로 GET 요청에 body를 포함하는 것 자체가 불가능한 건 아닙니다. 하지만 RFC 9110은 GET 요청은 오직 조회 목적이어야 하며, Request Body를 포함하더라도 서버는 Request Smuggling 공격으로 판단하여 이를 무시하거나 거절할 수 있다고 규정하고 있습니다.
문제는 일관성입니다. 서버, 프록시, HTTP 클라이언트마다 GET body를 처리하는 방식이 제각각이고, 일부는 아예 제거해버립니다. 실제로 Elasticsearch가 GET with body 방식으로 검색 API를 설계했다가 클라이언트 호환성 문제로 논란이 된 적도 있습니다.
결국 GET body는 표준이 보장하지 않는 동작에 의존하는 것이라, 실무에서 신뢰하기 어렵습니다. 그래서 실무가 선택한 것이 POST 메서드를 빌려 쓰는 현실적인 타협안입니다.
하지만 POST는 REST 원칙상 '리소스 생성(Create)'을 위한 메서드라, 조회에 쓰는 건 의미론적으로 어색함이 남습니다. 문제는 어색함만이 아닙니다. HTTP는 POST를 서버 상태를 바꿀 수 있는 메서드로 취급하기 때문에, 내용은 조회일 뿐인 요청이라도 실패했을 때 마음 놓고 재시도할 수 없습니다. 캐싱도 마찬가지입니다. 응답을 저장해뒀다가 같은 요청에 대신 응답해 주는 프록시·CDN 같은 중간 캐시는 HTTP 규칙만 보고 판단하기 때문에, 검색일 뿐인 요청도 POST라는 이유만으로 캐싱하지 않고 매번 원본 서버로 통과시킵니다.
그래서 QUERY는 다 해결해 주나?링크를 제목에 복사
결론부터 말하면, QUERY는 GET의 body 제약과 조회에 POST를 쓸 때 생기는 문제를 동시에 해결합니다.
QUERY /products HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"category": "electronics",
"minPrice": 100,
"maxPrice": 500,
"brand": ["samsung", "lg"],
"sort": "price_asc",
"page": 1,
"size": 20
}- body는 POST처럼
- 검색 조건이 URL이 아니라 body에 담기므로 쿼리 스트링의 세 가지 문제가 한 번에 사라집니다. URL 길이 제한(414)에 걸리지 않고, 예시의
brand배열처럼 배열이나 중첩 객체도 JSON 그대로 표현할 수 있으며, 검색 조건이 브라우저 히스토리나 서버 로그에 남지 않습니다.
- 검색 조건이 URL이 아니라 body에 담기므로 쿼리 스트링의 세 가지 문제가 한 번에 사라집니다. URL 길이 제한(414)에 걸리지 않고, 예시의
- 동작은 GET처럼
- 안전함(safe)과 멱등성(idempotent)을 만족합니다. 요청이 서버 상태를 바꾸지 않고(safe), 같은 요청을 여러 번 보내도 한 번 보낸 것과 결과가 같으므로(idempotent), 실패한 요청을 안심하고 재시도할 수 있습니다.
- HTTP 캐싱도 가능합니다. 단, 캐시 키에 request body를 포함해야 하고 JSON 키 정렬·공백 제거 같은 정규화가 필요해, URL만 보면 되는 GET보다는 구현이 조금 까다롭습니다.
세 메서드를 비교하면 다음과 같습니다.
| 특성 | GET | POST | QUERY |
|---|---|---|---|
| Request Body | 비권장 | 가능 | 가능 |
| Safe | O | X | O |
| Idempotent | O | X | O |
| Cacheable | O | X | O |
| 414 에러 위험 | 있음 | 없음 | 없음 |
다만 표만 보면 단점이 없어 보여도, 공짜는 아닙니다. 알아둘 점이 세 가지 있습니다.
- CORS preflight
- QUERY는 CORS-safelisted 메서드(GET·HEAD·POST)에 속하지 않아서 PUT·DELETE처럼 cross-origin 요청에 preflight(OPTIONS 요청)가 발생하고, 서버가
Access-Control-Allow-Methods헤더에QUERY를 명시하지 않으면 요청이 막힙니다.
- QUERY는 CORS-safelisted 메서드(GET·HEAD·POST)에 속하지 않아서 PUT·DELETE처럼 cross-origin 요청에 preflight(OPTIONS 요청)가 발생하고, 서버가
- 안전함(safe)과 멱등성(idempotent)은 서버가 지켜야 할 약속
- QUERY를 쓴다고 자동으로 안전해지는 게 아닙니다. 클라이언트와 중간 캐시는 안전하다는 전제로 재시도하고 캐싱하므로, 핸들러에 조회 카운트 증가 같은 부수효과를 넣으면 예측 불가능하게 동작합니다.
- 캐시에 남는 민감 정보
- 검색 조건이 URL에서 사라져 로그 노출은 줄지만, 응답이 캐싱되면 쿼리 내용이 캐시에 저장됩니다. 민감한 검색이라면 캐싱 정책을 따로 고민해야 합니다.
정리하면링크를 제목에 복사
HTTP QUERY 메서드는 작은 변화지만, 오랫동안 우회해온 문제를 정면으로 해결합니다.
- 414 에러 없이 복잡한 검색 조건을 body에 담을 수 있습니다.
- GET처럼 안전(safe)하고 멱등(idempotent)하며, 캐싱도 가능합니다.
- POST의 의미론적 어색함에서 벗어날 수 있습니다.
물론 표준이 발표됐다고 곧바로 실무에 쓸 수 있는 것은 아닙니다. fetch는 method에 임의의 문자열을 허용하므로 fetch(url, { method: 'QUERY', body })처럼 지금도 보낼 수는 있지만, RFC 10008은 아직 표준화 트랙의 첫 단계인 제안 표준(Proposed Standard) 상태라 이를 받아줄 서버 프레임워크와 CDN 같은 중간 인프라의 지원이 갖춰지기 전까지 실제 적용은 어렵습니다.
그래도 GET의 오랜 문제를 보완한 메서드가 나왔다는 건 좋은 소식입니다. TanStack Query처럼 queryKey 기준으로 캐싱하는 클라이언트 코드는 크게 달라질 게 없고, 바뀌는 건 코드 바깥이니까요. 지원이 갖춰지면 브라우저·CDN의 HTTP 캐싱과 안전한 재시도를, 코드 구조를 바꾸지 않고 얻게 됩니다.
끝으로, 상세한 기술적 배경과 구현 지침은 IETF의 RFC 10008 문서를 통해 확인하실 수 있습니다.
모든 글