개발 일기

HTTP 메서드 POST VS GET 사용에 대한 고찰

팡펑퐁 2023. 10. 11. 02:57
728x90
💡
회사에서 진행 중인 프로젝트의 api를 만드는 도중에 문득 생각이 들었다. 어..? 이거 생각해 보니 죄다 POST네 이게 맞나?

지금까지 한 번도 생각해보지 않았던 내용인데 그동안 내가 만든 api의 HTTP 메서드는 POST인지 GET인지가 명확했기 때문이다.

 

 내게 고민을 안겨준 요청 메서드의 내용은 쉽게 말해 검증이다. 클라이언트에서 검증할 데이터를 보내면 서버는 검증을 거치고 나서 검증 결과에 따른 데이터를 리턴한다. 이렇게만 보면 서버에 어떠한 변화도 주지 않고 결과만 받으니 GET이 아닌가 싶지만 검증할 데이터를 url에 실기에는 너무 큰 것이 고민의 시작이었다.

 

예를 들어 member 엔티티의 특정 필드 값 몇 개를 검증한다고 가정해 보자. 서버에서는 해당하는 데이터를 데이터베이스에서 찾아 객체에 담고 검증 과정을 거치고 결과를 리턴할 것이다. 여기서 요청 api의 url에 검증할 member의 id 값 등을 실어 보낼 것이고, 이때는 GET 메서드를 사용하는 것이 합리적으로 보인다. 

 

 그러나, 나의 경우에는 검증 데이터 전체를 클라이언트가 들고 있고 이는 json 형태로 되어있다.(파일의 크기가 굉장히 크다.) 또한, 서버는 클라이언트로부터 받은 검증 데이터(json 데이터)를 파싱 하여 value 값을 꺼내고 이를 검증한다. 이때 검증할 비교 데이터는 데이터 베이스에 꺼낸다. 참으로 애매하다. 일단 확실한 것은 url에 검증 데이터를 실을 수 없었다는 것이다.

 

🤔 그럼 GET의 요청 메시지 본문에 json Body를 실어서 보낸다면?

😨 잠깐, GET 메시지의 본문에 json body를 실어서 보낼 수가 있나? 

 

 일단, Mozilla에서 설명하는 GET 메서드를 살펴보자. 참고로 Mozilla는 오픈 소스 기반의 비영리 조직으로 웹 기술과 오픈 웹을 발전시키기 위한 다양한 프로젝트를 진행하고 있는 단체이다. 대표적으로는 웹 브라우저인 파이어폭스(Firefox)가 있다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET

  • HTTP GET 메서드는 구체적인(지정된) 리소스의 표현을 요청한다. GET을 사용하는 요청은 데이터를 요청하는 데에만 사용해야 한다.(데이터를 포함해서는 안 됨)
  • Note: GET 요청에 body/payload를 포함시키면 일부 구현체에서 요청을 거절할 수 있다.
  • 사양(specification)에서는 이를 금지하고 있지 않지만, GET 요청에 body/payload를 포함시킨 sementics가 정의 되어 있지 않으므로 요청시 body/payload를 전송하는 것을 피하는 게 좋다.

 

흥미로운 내용이다. 보다 정확한 확인을 위해 뤼튼에게 물어보았다.

 

🤖 뤼튼의 답변

  • HTTP GET 요청에 본문(body) 또는 페이로드(payload)를 싣는 것은 기술적으로 가능하지만, 대부분의 경우 권장되지 않습니다. 이에 대한 몇 가지 주요 이유는 다음과 같습니다
  • 표준 준수: HTTP/1.1 명세인 RFC 7231에 따르면, GET 요청에 본문을 포함하는 것은 정의되지 않은 의미입니다. 즉, GET 요청에 본문이 포함되어 있으면 서버가 어떻게 반응할지 예측하기 어렵습니다.
  • 호환성 문제: 많은 클라이언트, 서버 및 프록시 도구들이 GET 요청의 본문을 지원하지 않거나 무시합니다. 따라서 이러한 방식을 사용하면 예상치 못한 결과나 호환성 문제가 발생할 수 있습니다.
  • 캐싱 및 재사용 문제: HTTP GET 요청은 멱등(idempotent)하고 안전(safe) 해야 하며, 캐싱 가능해야 합니다. 그러나 본문을 포함하는 경우 이러한 특성들이 깨질 수 있으며, 재사용과 캐싱이 어려워집니다.
  • API 설계 원칙: 일반적으로 RESTful API에서는 리소스를 생성하거나 변경할 때 POST 또는 PUT 등의 메서드를 사용하고, 리소스를 조회할 때는 GET 메서드를 사용합니다. 이런 원칙을 따르면 API가 보다 일관적이고 예측 가능해집니다.
  • 따라서 대부분의 경우에서는 HTTP POST 나 PUT 같은 다른 메서드를 사용하여 데이터를 전송하는 것이 좋습니다.

 

⌨️ RFC 7231에 대해 좀 더 물어보았다.

HTTP GET 요청은 기술적으로 본문을 포함할 수 있지만, 이는 권장되지 않습니다. RFC 7231 (HTTP/1.1: Semantics and Content)에 따르면:

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

  • 위에 나온 대로 GET 요청에 페이로드(본문)를 포함시키는 것은 정의된 semantics가 없으며, 일부 구현체에서는 이런 요청을 거부할 수 있다는 같은 내용이다.

 

 나는 지금까지 HTTP 요청 메시지에서 POST와 GET을 나누는 기준이 서버의 변경 가능성 즉 멱등성이 가장 크다고 생각했었다. 멱등성이란 같은 연산을 여러 번 하더라도 그 결과가 달라지지 않는 성질을 말한다. POST의 경우 데이터의 생성과 같이 서버의 무언가가 변한다면, GET의 경우에는 데이터를 조회하여 가져오는 등의 검색, 조회 작업만 하기 때문에 같은 동작을 수십번 해도 항상 같은 결과를 얻는 멱등성을 가진다. 그런데 이 기준만 놓고 판단하기에는 무리가 있었다. 그래서 POST와 GET 요청의 표준 명세를 찾아보기로 했다. HTTP/1.1 명세는 RFC 7231이라는 문서에 자세히 기술되어 있다. 이 문서에서 HTTP GET과 POST 메서드에 대해 다음과 같이 설명하고 있다.

 

📖 HTTP GET (RFC 7231, Section 4.3.1)

The GET method requests transfer of a current selected representation for the target resource. GET is the primary mechanism of information retrieval and the focus of almost all performance optimizations. Hence, when people speak of retrieving some identifiable information via HTTP, they are generally referring to making a GET request.

  • GET 메서드는 정보 검색의 주요 메커니즘이며 거의 모든 성능 최적화의 중심이다. 즉, POST보다 가벼운 메서드임을 드러낸다. 말 그대로 서버에서 데이터를 조회하는 것에 집중되어 있기 때문인듯하다.

 

📖 HTTP POST (RFC 7231, Section 4.3.3)

The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics.

  • POST 메서드는 대상 리소스가 요청에 포함된 표현(representation)을 리소스 자체의 특정 의미 체계에 따라 처리하도록 요청한다.

 

📖 Safe Methods (RFC 7231, Section 4.2.1)

Request methods are considered "safe" if their defined semantics are essentially read operations

  • 요청 메서드는 그 정의된 의미가 본질적으로 읽기 연산인 경우 '안전'으로 간주된다. 여기에서는 GET이 안전한 메서드로 분류된다.

 

📖 Idempotent Methods (RFC 7231, Section 4.2.2)

A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request

  • 여러 개의 동일한 요청이 해당 방법으로 서버에 미치려고 하는 효과가 단일 요청의 효과와 동일한 경우 요청 방법은 '멱등'으로 간주된다. 여기에서 GET은 멱등한 메서드로 분류되지만 POST는 그렇지 않다.

 

 핵심은 POST와 GET의 차이는 안전성과 멱등에 있는 듯하다. 즉, GET의 경우 어떤 데이터를 단순 조회하는 기능이므로 서버의 보안적인 내용이나 핵심 비즈니스 연산과는 관련이 없어야 하며 단순 읽기 요청에 집중되어 있기 때문에 동일 요청이 반복되더라도 항상 같은 결과를 리턴해야 한다는 것이다. 이 내용에 두 메서드 간의 다른 차이를 추가하여 정리해 보았다.

 

🧹 정리

📌 HTTP GET

  • GET은 정보를 조회하기 위한 메서드이다. 서버의 어떤 리소스에 대한 정보를 요청할 때 사용한다.
  • GET 요청은 안전(safe)하고 멱등(idempotent) 해야 한다.
    • 서버의 상태를 변경하지 않아야 하며, 같은 요청을 여러 번 보내도 결과가 동일해야 한다.
  • GET 요청의 응답은 캐시 될 수 있다.
    • HTTP GET 요청의 응답이 캐시 될 수 있다는 것은 GET으로 요청한 리소스가 사용자의 브라우저나 중간에 위치한 캐싱 서버에 저장될 수 있다는 것을 의미한다.
    • 가벼운 메서드임으로 가능한 것이다.
    • 여기서 가볍다는 것은 데이터의 보안에 크게 신경 쓸 필요가 없으며 작은 크기임을 뜻하는 것 같다.
  • GET 요청에는 중요한 데이터나 크기가 큰 데이터를 포함하지 않아야 한다.
    • 위에 언급한 것처럼 가볍기 때문에 중요 데이터는 포함되어서는 안 된다.
    • 크기가 큰 데이터의 경우 URL 길이 제한 등으로 인해 잘리거나 누락될 수 있다.

 

📌 HTTP POST

  • POST는 서버의 상태를 변경하거나 새로운 리소스를 생성하기 위한 메서드이다.
  • POST 요청은 비멱등(non-idempotent)이다. 같은 요청을 여러 번 보내면 서버의 상태가 다르게 변할 수 있다.
    • 단순히 데이터를 생성하고 변경하는 것 외에도 어떤 프로세스를 처리하여 상태가 변경되는 경우에도 사용한다.
      • 이때의 POST의 결과로 새로운 리소스가 생성되지 않을 수 있다.
      • 예를 들면 결제완료가 된 후 배달을 시작하고 완료하는 것과 같은 프로세스가 진행되는 경우
  • POST 요청에는 본문(body)이 포함되어 있으며 이 본문에는 생성하거나 변경할 리소스의 정보가 들어있다.
  • POST 요청은 보통 폼 제출, 파일 업로드 등에서 사용되지만, 복잡한 구조의 입력 데이터가 필요한 API 호출에서도 사용될 수 있다.

 

💡 이와 관련된 블로깅을 찾아보던 중 내 궁금증을 완벽하게 해결한 글을 보았다.

해당 글에 따르면 HTTP/1.1의 기본 스펙이었던 RFC2616에는 GET 요청 시에 메시지 바디가 포함되면 안 된다는 문구가 있었지만 이후 사라지게 되었고 위의 내용처럼 포함될 수 있으나 권장하지 않는 것으로 변경되었다고 한다. 

또한, GET 메서드는 요청 데이터를 url에 포함시키기 때문에 보안적인 측면에 문제가 있을 수 있어 body를 포함시키자는 논의가 계속 있었던 것으로 보인다. 그래서 지금은 넣을 수는 있나 보다.

 

📌 결론

  • GET 요청 메시지에 body를 포함시킬 수 있지만 이는 GET 요청 메시지의 표준 명세인 안전성과 멱등성을 해치지 않는 선에서만 사용해야 한다.
  • 데이터 조회 이외의 로직이 포함되어서는 안 된다.
    • GET 요청에 본문이 포함되어 있으면 서버가 어떻게 반응할지 예측하기 어렵기 때문이다.
  • 캐시 될 수 있을 정도로 가벼운 메서드이기 때문에 중요 데이터나 큰 크기의 데이터는 포함해서는 안 된다.
  • JSON으로 조회 데이터를 넘겨야 하는 경우 GET 메서드보다 POST 메서드를 사용한다.

 

 이를 고려했을 때 나의 검증 메서드를 생각해 보면 검증에 필요한 데이터를 조회하고, 검증하여(조회 이외의 로직이 들어감), 검증 결과를 리턴하기 때문에 서버에 어떠한 변경 사항도 없으며 항상 같은 데이터를 리턴한다. 하지만, 해당 검증 데이터가 url에 실을 수 없을 정도로 큰 데이터이며 암호화되어 있는 중요 데이터임으로 안전하지 않을 가능성이 있다. 마지막으로 검증에 대한 로그용 테이블에 검증 기록을 저장할 로직이 추가될 예정이기 때문에 데이터베이스에 데이터를 INSERT하는 변경 사항이 생기게 된다. 따라서 GET보다는 POST 요청으로 처리하는 것이 RFC 7231 사양을 따르는 올바른 방법이라고 결론지었다. 앞으로도 이 포인트를 생각하여 사용해야겠다. 끗.

 

 

 

참고

김영한 - 모든 개발자를 위한 HTTP 웹 기본 지식

뤼튼

https://cl8d.tistory.com/63

728x90