새로운 HTTP QUERY 메서드 도입, 왜 필요할까?
HTTP 통신 규약인 RFC에 복잡한 쿼리를 안전하게 처리하기 위한 새로운 'QUERY' 메서드가 도입되었습니다. 기존에 GET 요청의 URL 길이 제한 문제나 POST 요청의 부작용(캐싱 불가, 안전성 문제 등)을 해결하여, 데이터 조회 시 요청 본문(body)을 안전하고 명확하게 전달할 수 있게 되었습니다. RESTful API 설계에 있어 매우 실용적이고 중요한 업데이트입니다.
RESTful API의 세계에서 우리는 오랫동안 엄격한 (스스로 부여한) 규칙에 따라 살아왔습니다. 데이터를 가져올 때 GET을 사용하든, POST로 엔티티를 생성하든, PUT으로 리소스를 업데이트하든 간에, HTTP 메서드는 서버에게 클라이언트의 의도가 무엇인지 알려줍니다. 아주 최근에 HTTP용 새로운 QUERY 메서드를 정의한 RFC 10008이 게시되었습니다. 이미 다른 HTTP 메서드가 있는데 왜 이것이 필요할까요? 알아봅시다.
순전히 기술적 관점에서 볼 때, HTTP 메서드는 단순히 문자열일 뿐입니다. 이론적으로는 GET /api/v1/users를 보내는 대신 FETCH /api/v1/users를 사용할 수도 있습니다. 하지만 실제로는 GET 및 POST와 같이 잘 알려진 HTTP 메서드를 둘러싼 수많은 RFC와 암묵적, 문서화되지 않은 동작들이 존재합니다. 예를 들어, 브라우저는 주소를 입력하거나 북마크를 클릭할 때 GET 요청을 보냅니다. 표준 HTML 폼은 메서드로 GET과 POST만 허용합니다. 또한 대부분의 프록시, 방화벽 및 웹 서버는 '표준' HTTP 메서드만 허용합니다. 그렇다면 수십 년 동안 잘 작동해 온 기존 메서드 세트가 이미 있는데 왜 새로운 HTTP 메서드를 도입해야 할까요?
GET을 사용한 쿼리
전통적으로 리소스를 필터링하려면 GET 요청의 쿼리 매개변수를 사용했습니다 (예: /api/v1/users?role=admin&status=active&sort=desc). 이 방식은 단순한 필터에는 잘 작동합니다. 하지만 복잡한 관계형 쿼리, 깊은 중첩 또는 고급 논리를 수행해야 할 때 URL이 너무 길어져 읽기 어렵고, 때로는 브라우저나 서버의 문자 제한에 걸리기도 합니다.
기타 잠재적인 문제는 다음과 같습니다:
- 비 ASCII 또는 특수 문자를 매개변수로 보내려면 인코딩해야 하므로 요청 크기가 증가합니다.
- 서버 및 기타 미들웨어가 요청 매개변수를 로그에 남기기 십상이며, 이는 특정 상황에서 문제가 될 수 있습니다.
- 배열과 같은 일부 데이터 구조를 표현하는 방식이 명확하게 정의되지 않고 구현마다 다릅니다 (예:
?roles[0]=admin&roles[1]=reportervs?roles=admin&roles=reportervs?roles[]=admin&roles[]=reporter식의 차이). - 깊게 중첩된 구조를 표현할 때도 마찬가지입니다.
이러한 모든 단점이 데이터를 쿼리 매개변수로 보낼 때 발생하는 것이라면, 단순히 JSON 요청 본문을 포함한 GET 요청을 보내면 되지 않을까요? 다시 말하지만, 이론적인 관점에서는 이 방법이 작동해야 합니다. HTTP RFC 중 어느 것도 HTTP GET 요청을 수행할 때 요청 본문을 사용하는 것을 명시적으로 금지하지는 않지만, 이를 수행하지 않아야 한다고 지적하고 있습니다. 결과적으로 다양한 클라이언트, 프록시 및 웹 서버 구현이 본문이 있는 GET 요청을 다르게 처리합니다. 일부는 즉시 거부하고, 일부는 단순히 본문을 버리며 다른 일부는 이를 해석하려고 시도합니다. 따라서 기업 방화벽이나 다른 브라우저 뒤에 있는 사용자가 웹사이트를 사용할 수 없게 될 수 있으므로 요청 본문과 함께 HTTP GET을 사용하는 것은 좋지 않은 생각입니다. 이것이 또한 GET 요청이 이제 요청 본문을 지원해야 한다고 지정하는 새로운 RFC가 없는 이유이기도 합니다. 그렇게 하면 기존의 많은 구현이 손상될 것이기 때문입니다.
해결 방법: POST를 사용한 쿼리
GET을 사용하여 요청 본문을 보내는 것이 문제를 일으킬 수 있으므로, 이를 우회하는 방법으로 POST를 사용하는 것이 일반적입니다. POST는 요청 본문을 허용하지만 상당한 의미론적 문제를 야기합니다. POST는 멱등성이 없는 것(non-idempotent)으로 정의되며 리소스 생성 또는 처리를 위한 것입니다. 이것이 큰 문제처럼 들리지 않을 수도 있지만, 예를 들어 실패 시 자동 재시도를 구현할 때 성가실 수 있습니다. GET 메서드는 안전하고 멱등성이 있는 것으로 정의되어 있어 서버 구현이 올바르기만 하면 부작용에 대한 걱정 없이 실패한 요청을 재시도할 수 있습니다. 또한 POST를 사용하면 프록시나 다른 미들웨어가 해당 작업이 읽기 전용이라는 것을 자동으로 파악하는 것이 불가능해집니다. 예를 들어, 미들웨어는 일정 시간 동안 GET 요청을 자동으로 캐시할 수 있지만 POST 요청에서는 작동하지 않습니다.
QUERY 메서드
위의 모든 이유로 인해 수년간의 논의 끝에 QUERY 메서드가 명세에 포함되었습니다. QUERY 메서드는 특별할 것이 없으며, RFC는 대략적으로 GET 메서드와 비슷하지만 요청 본문이 있다고 설명합니다. 이 메서드는 안전하고 멱등성을 가질 목적으로 만들어졌습니다. QUERY 요청은 캐시될 수 있지만, 이를 구현할 때는 주의를 기울여야 합니다.