HTTP 메서드

Aiden·2022년 2월 20일
1

HTTP

목록 보기
8/13
post-thumbnail

저번 포스팅에서는 HTTP 의 기본적인 개념과 역사, 다양한 특징들에 대해서 알아보았다.
그 중 HTTP Message 의 Start-line 에서 GET 이라는 메서드를 잠깐 확인할 수 있었는데, 이번 포스팅에서 다루게 될 HTTP Method 중 하나로 HTTP 에서 아주 핵심적인 개념이다.

이는 이후에 학습하게될 REST API 에서도 필수적인 개념이기 때문에 각 메서드들이 가지는 차이점이 무엇인지, 특정 상황에서 이 메서드가 과연 최선의 선택이었는지를 항상 고민하면서 공부해나가는 것이 중요하다고 볼 수 있겠다.

HTTP API 설계


본격적으로 각 메서드들에 대해서 알아보기 전에, HTTP API 를 설계하는 예시 두 가지를 확인하고 어떤 방식으로 설계하는 것이 바람직한 설계 방법인지를 고민해보도록 하자.

요구사항은 다음과 같다.

📌 Requirements

  • 회원 목록 조회
  • 회원 조회
  • 회원 등록
  • 회원 수정
  • 회원 삭제

💡 설계 1

A 는 위와 같은 요구 사항들에 대해 HTTP API 를 설계하라는 지시를 받았고, API URI 를 다음과 같이 작성하였다.

  • 회원 목록 조회 /read-member-list
  • 회원 조회 /read-member-by-id
  • 회원 등록 /create-member
  • 회원 수정 /update-member
  • 회원 삭제 /delete-member

겉보기에는 아주 그럴듯하다. URI 를 보자마자 어떤 작업을 수행하고자 하는지를 직관적으로 확인할 수 있고, 외우기도 아주 쉬워보인다.

바로 다음 설계를 확인해보자.

💡 설계 2

B 는 위와 같은 요구 사항들에 대해 HTTP API 를 설계하라는 지시를 받았고, API URI 를 다음과 같이 작성하였다.

  • 회원 목록 조회 /members
  • 회원 조회 /members/{id}
  • 회원 등록 /members/{id}
  • 회원 수정 /members/{id}
  • 회원 삭제 /members/{id}

A 의 설계와 비교하여 아주 간단해보인다. 하지만 어떤 작업을 수행하는지를 직관적으로 알아보기 힘들고, 각각의 URI 를 구분할 수 없을 것 같다.

그렇다면, 과연 A 의 설계와 B 의 설계 중 어떤 방법이 더 바람직한 설계 방법일까?

이를 위해서 우리는 이전에 살펴봤던 URI 의 개념과 본질에 대해서 다시 생각해볼 필요가 있다.

📌 URI

  • Uniform Resource Identifier 의 약어로 인터넷에 있는 자원의 위치를 나타내는 유일한 주소

URI 는 인터넷에 있는 자원, 즉 리소스의 위치를 나타내는 유일한 주소이다.
따라서, URI 의 본질은 리소스를 식별하는 것이 되어야 한다.

  • URI 에 대한 자세한 내용은 아래 링크를 참고바란다.
  • HTTP 기초 - URI

즉, 회원 목록 조회와 회원 조회, 등록, 수정, 삭제와 같은 다양한 기능들 중에서 리소스를 식별해내어야 하고, 이를 가장 잘 반영한 URI 가 그 본질에 따라 바람직하게 설계되었다고 볼 수 있는 것이다.

그렇다면 A의 설계와 B의 설계를 비교해보자.
둘 중 누구의 설계가 리소스를 가장 잘 식별해냈을까?

✅ 정답은 B 가 될 것이다.

A 는 회원 목록 조회나 회원 조회, 등록 등 회원이라는 리소스에 대해 수행하고자 하는 작업을 URI 에 반영하여 설계했다. 즉, 리소스 그 자체와 리소스를 대상으로 하는 행위를 분리하지 못하고, /read-member-list, /read-member-by-id 와 같이 명사와 동사를 함께 사용하여 설계한 것이다.
따라서, 이는 리소스를 정확하게 식별해낸 URI 라 보기 어려울 것이다.

반면, B 의 설계는 회원 목록 조회, 회원 조회, 회원 등록, 회원 수정 등 다양한 기능에서 회원이라는 리소스를 정확하게 식별하여 설계하였다. 회원 목록 조회는 /members, 각 회원에 대한 조회, 등록, 삭제, 변경에 대해서는 /members 뒤에 /{id} 를 활용하여 정확하게 행위의 대상이 되는 특정 리소스를 식별해낼 수 있는 URI 를 설계한 것이다.

💡 정리

즉, 우리는 HTTP API 설계 과정에서 URI 를 설계할 때, 그 목적과 본질에 부합하도록 리소스행위를 분리해내어 URI리소스만을 식별할 수 있도록 설계하여야 한다.
앞선 요구사항들을 예로 들자면, 다음과 같다.

  • 회원 목록 조회
  • 회원 조회
  • 회원 등록
  • 회원 수정
  • 회원 삭제

✅ 리소스 : 회원
✅ 행위 : 조회, 등록, 수정, 삭제

여기서 리소스는 회원이 되므로, URI 는 회원만을 식별할 수 있도록 설계되는 것이 바람직하다.

회원 목록을 조회하는 것, 회원을 조회하는 것, 회원을 등록하는 것, 회원을 수정하는 것, 회원을 삭제하는 것은 리소스 자체가 아니기 때문이다.

하지만 B 의 설계를 들여다보면, 의문점이 생길 수 있다.
리소스를 대상으로 수행되어야 하는 행위는 모두 다른데, URI 가 모두 같다면 이를 어떻게 구분할 수 있을까?

이에 대한 해답이 바로 이제부터 살펴볼 HTTP Method 가 되겠다.
지금부터는 본격적으로 HTTP Method 에 대해서 살펴보도록 하자.



HTTP Method


📌 HTTP Method

  • HTTP는 요청 메서드를 정의하여, 주어진 리소스에 수행하길 원하는 행동을 나타냅니다. 간혹 요청 메서드를 "HTTP 동사"라고 부르기도 합니다.

    (https://developer.mozilla.org/ko/docs/Web/HTTP/Methods)

위는 HTTP Method 에 대한 정의를 MDN 에서 발췌한 내용이다.
즉, HTTP 메서드는 동사에 해당하며, 클라이언트가 대상 리소스에 수행하고자하는 행위를 서버에 요청하기 위해 사용한다.

BURI 설계를 다시 확인해보면,

  • 회원 목록 조회 /members
  • 회원 조회 /members/{id}
  • 회원 등록 /members/{id}
  • 회원 수정 /members/{id}
  • 회원 삭제 /members/{id}

회원 목록 조회를 제외한 모든 기능의 URI 가 동일하여 조회, 등록, 수정, 삭제와 같은 행위에 대해 구분하지 못하고 있음을 알 수 있다.

이 때, 각각의 URI 와 HTTP 메서드를 함께 사용하게 되면,

  • 회원 목록 조회 GET + /members
  • 회원 조회 GET + /members/{id}
  • 회원 등록 POST + /members/{id}
  • 회원 수정 PATCH + members/{id}
  • 회원 삭제 DELETE + /members/{id}

URI 가 공통적으로 가리키는 리소스에 대해 수행하고자하는 행위를 서로 다른 HTTP 메서드를 활용하여 구분하고 요청할 수 있는 것이다. 여기서 HTTP 메서드에 해당하는 GET POST PATCH DELETE 는 조회, 등록, 수정, 삭제의 리소스에 대한 행위를 정의할 것이다.

그럼 주로 쓰이는 HTTP 메서드들에 대해서 알아보도록 하자.

💡 GET


GET 은 자주 사용되는 HTTP 메서드 중 하나로, 리소스 조회를 위해 사용된다.
즉, 앞선 예시에서 회원 목록 조회 및 회원 조회에 GET 이 사용된 모습을 확인할 수 있는데, 클라이언트가 회원 목록 혹은 회원에 대한 정보를 서버에게 요청하는 작업이기 때문이다.

위 그림은 GET 메서드를 통해 특정 리소스를 요청하는 HTTP 메시지의 예시이다.
이 때, 요청과 함께 서버에 전달하고 싶은 데이터가 있다면 쿼리 파라미터 혹은 쿼리 스트링을 활용해 전달해줄 수 있다. 위 그림에서는 /search 뒤의 쿼리를 통해 필터와 같은 검색 조건에 대한 데이터를 함께 전달해주고 있다. (?q=hello&hl=ko)
물론, HTTP 요청 메시지의 Body 에 전달하고자 하는 데이터를 함께 담아보내도 되지만, 일반적으로 GET 메시지의 경우에는 Body 를 사용하지 않기 때문에 이를 지원하는 서버가 많지 않아 권장되지 않는 방법이다. 즉, 서버가 GET 메시지의 Body 를 확인하지 않을 가능성이 높다.
따라서, GET 메시지로 리소스를 요청할 때 필터와 같이 전달하고자 하는 데이터가 있을 경우에는 쿼리 파라미터로 작성해 전달하는 방법이 일반적이다.

이제 B 의 설계에서 GET 을 활용한 회원 조회 기능의 흐름을 간략하게 살펴보자.

클라이언트는 GET 메서드와 /members/100 을 활용해 100 번 회원의 정보를 서버에 요청하고 있다.

서버는 메시지를 받아 해석한 후, /members/100 에 해당하는 리소스를 찾아낸다.

이후, 찾아낸 /members/100 리소스의 정보를 Body 에 담아 응답 메시지를 생성, 클라이언트에게로 전송한다.
이 때, 서버가 전송한 응답 메시지의 Start-lineHeader 를 보면 HTTP 의 버전과 상태 코드, Body 에 담긴 데이터의 타입과 길이 정보가 담겨있는 것을 확인할 수 있다.

물론 예시에서는 JSON 타입을 사용했지만, 이는 html 이 될 수도 있고, jpeg 나 XML 이 될 수도 있다.

이제 메시지를 전달받은 클라이언트는 리소스의 타입에 따라 데이터를 읽어 화면에 그려주거나 웹브라우저로 랜더링하는 등의 처리를 수행하게 되는 것이다.

💡 POST


다음은 POST 메서드이다.
POST 는 클라이언트가 서버에게 데이터를 보내면서, 해당 데이터에 대한 처리를 요청할 때 사용한다.
즉, POST 메시지의 Body 에 데이터를 담아 서버로 보내면, 서버는 Body 를 통해 들어온 데이터를 처리하는 모든 기능을 수행하는 것이다.

이 때의 처리는 클라이언트와 서버가 사전에 약속한 작업을 수행하는 것을 의미하며, 주로 신규 리소스의 등록이나, 프로세스의 처리를 의미한다.

위 그림에서 POST 메서드와 /members 를 명시하고 BodyJSON 데이터를 담은 모습을 확인할 수 있는데 이는 사전에 클라이언트와 서버가 POST 에 대한 처리를 회원 등록으로 약속해놓았다고 가정하였을 때, 해당 JSON 데이터를 신규 회원으로 등록해달라는 요청이 될 것이다.

B 의 설계에 POST 를 적용한 흐름을 살펴보면, 쉽게 이해가 될 것이다.
아래는 클라이언트와 서버가 사전에 POST회원 등록으로 정의해놓았을 때의 예시이다.

클라이언트는 POST 메시지에 /members URL 을 명시하고, Header 에는 Body 에 담긴 데이터의 타입을 명시한다. Body 에는 신규 리소스가 담겨 있다.

이는 즉, Body 에 담긴 JSON 데이터를 /members 라는 회원목록에 신규 회원으로 등록해달라는 요청이다.

요청을 받은 서버는 Body 에 담긴 데이터를 데이터베이스에 신규 리소스로 저장하면서 신규 id 를 부여하게 되는데, 그림에서는 회원 목록의 100 번째 회원이라는 의미로 100 을 부여하였다고 가정했다. 따라서 신규 리소스가 생성된 경로는 /members/100 이 될 것이다.


서버는 신규 리소스를 등록한 후, 응답 메시지를 생성하여 클라이언트에게 전송한다.
이 때, 응답 메시지에는 HTTP 의 버전과 상태 코드가 동일하게 입력되며, Body 의 데이터 타입 및 길이와 함께 신규 리소스가 생성된 경로를 전송한다.

POST 메시지로 회원 등록이 수행되면, 서버는 생성한 데이터를 다시 Body 에 담아 전송하기 때문에 응답 메시지의 Body 에도 요청 메시지와 동일한 데이터가 담긴다.

📌 POST요청 데이터 처리

  • POST 의 데이터 처리는 정해진 것이 없어 각 리소스마다 지정해주어야 한다.

앞서 잠깐 언급했었는데, POST 는 클라이언트와 서버가 리소스에 대해 사전에 약속한 작업을 수행한다.

즉, POST 는 리소스를 등록하는 작업만 수행하는 것이 아니라, html 의 폼태그의 입력값을 활용하여 회원 가입 및 주문 등에 사용하기도 하고, 게시판 및 블로그, 뉴스와 같은 커뮤니티의 포스팅 및 댓글 등의 기능에 활용될 수도 있다. 이외에도 신규 주문 생성이나 기존 리소스에 데이터를 추가하는 등 다양한 작업을 지정하여 POST 메서드의 기능으로 활용할 수 있다.
말그대로 만능 메서드라고 볼 수 있다.

물론, 그렇다고 해서 POST 를 무분별하게 사용하는 것은 바람직한 HTTP API 의 설계 방향은 아니다. GET 이나 PUT 과 같은 다른 메서드들로 충분히 처리할 수 있는 경우에는 해당 메서드들로 처리를 하는 것이 좋고, 다른 메서드들로 처리하기 어려운 경우에는 불가피하게 POST 를 사용할 수 있을 것이다.

따라서, POST 를 주로 사용하는 경우에 대해 간단히 정리해보자면 다음과 같다.

  • 새 리소스 생성(등록)
  • 내부적으로 프로세스가 변경되는 경우
  • 다른 메서드로 처리하기 어려운 경우
📌 내부적으로 프로세스가 변경되는 경우
   [결제완료 ➡️ 배달시작 ➡️ 배달완료]

새로운 리소스가 생성되는 것이 아니라, 내부적인 프로세스의 상태를 변경시켜야 할 경우를 의미한다.

💡 PUT


다음은 PUT 메서드이다.
PUT리소스를 덮어쓰는 기능을 수행한다.
즉, 지정된 URI 에 리소스가 이미 존재한다면 Body 의 데이터로 덮어쓰고, 리소스가 존재하지 않는다면 Body 의 데이터로 새로운 리소스를 생성한다.

이 때, 주의할 점은 리소스의 부분 변경이 불가능하다는 점이다.
PUT 은 리소스를 덮어쓰고 완전히 대체하는 메서드이기 때문에 기존 리소스의 특정 부분만을 변경하기 위해 PUT 메서드를 사용하게 되면, Body 에 담아 전송한 부분 이외의 모든 데이터가 삭제된 후 대체된다.
따라서, PUT 을 활용해 기존 리소스의 특정 부분만을 변경하고 싶다면 리소스 전체를 모두 작성한 뒤 전송해야 한다.

또한, PUT 은 기존 리소스가 존재하지 않을 경우에는 전송한 리소스를 새롭게 생성한다. 즉, POST 와 같은 신규 리소스 생성의 기능으로 활용할 수도 있는 것이다.
단, POST 와는 다르게 클라이언트가 리소스의 정확한 위치를 알고 URI 를 지정해주어야 한다는 점에서 차이가 있다.
앞선 예시에서도 알 수 있듯이 POST 에서는 /members 로 경로를 지정하고, 생성되는 신규 리소스의 정확한 위치는 서버에서 지정했었다. ex) /members
반면, PUT 에서는 클라이언트 단의 요청 메시지에서 정확한 리소스의 위치를 지정한 뒤, 서버로 전송하기 때문에 클라이언트가 리소스의 정확한 URI 를 알고 있다. ex) /members/100

아래의 그림을 통해 각각의 상황에서 PUT 의 흐름을 살펴보자.

먼저, 기존의 리소스가 존재하는 경우이다.

클라이언트에서 PUT 메서드와 함께 /members/100 의 경로를 지정한 뒤, JSON 데이터를 Body 에 담아 전송했다.
이 때, 서버의 /members/100 에는 이미 리소스가 존재하고 있다.

이 경우, 기존의 리소스는 새롭게 전송된 리소스로 완전히 대체된다.
만약, 기존의 리소스에 "username" 과 "age" 이외에 다른 컬럼이 존재한다고 하더라도 해당 데이터는 삭제되고, 새로운 리소스로 대체된다.

다음으로 지정한 경로에 리소스가 존재하지 않을 경우이다.

이 때는 /members/100 에 리소스가 존재하지 않으므로,

위와 같이 신규 리소스로 생성된다.

주의할 점은 POST 와는 다르게, 클라이언트가 리소스의 정확한 URL 을 메시지에 명시하여 주었다는 점이다.

정리하면, PUT 메서드는 컴퓨터의 파일 붙여넣기와 유사하다.
기존의 리소스가 존재할 경우 리소스를 덮어쓰고, 기존의 리소스가 존재하지 않을 경우에는 신규 리소스로 생성된다.

💡 PATCH

앞서 PUT 메서드는 리소스를 완전히 대체하기 때문에 부분 변경이 불가능했다.
그렇다면 리소스의 부분만을 변경하고 싶을 때는 어떤 메서드를 사용하여야 할까?
이는 수정 기능에 특화된 PATCH 메서드를 사용하여 처리할 수 있다.

PATCH 는 리소스의 부분을 변경하는 수정 기능을 위한 메서드이다.
따라서, PUT 과 다르게 변경하고 싶은 부분의 데이터를 Body 에 담아 전송하면 해당 부분의 데이터만 변경되고 다른 데이터는 유지할 수 있다.
B 의 설계에서도 회원 수정 기능을 PATCH 메서드로 구현하였는데, 이는 회원 정보를 변경하고자 할 때 회원 정보 데이터를 처음부터 모두 전송하지 않더라도 부분 데이터만을 변경할 수 있도록 하기 위함이라고 볼 수 있다.

아래에서 PATCH 의 흐름을 그림으로 살펴보자.

클라이언트에서 PATCH 메서드와 함께 /members/100 의 리소스 URI 를 지정해주었고 Body 에는 'age' 필드에 대해 50 이라는 값을 담아 전송하고 있다.

또한, /members/100 에는 기존의 리소스가 존재하고, 'username' 과 'age' 필드를 가지고 있다.


이 때, 서버에서는 Body 를 통해 들어온 'age' 필드값으로 기존 리소스를 수정하게 되는데, PUT 과 다르게 'username' 필드가 여전히 유지되어 있는 모습을 확인할 수 있다.

따라서, PATCH 메서드는 변경하고자 하는 부분의 데이터만 작성하여 전송하면, 기존 리소스를 쉽게 수정할 수 있기 때문에 PUT 메서드보다는 '수정' 의 의미에 조금 더 가까운 메서드라고 볼 수 있다.

또한, PATCH 는 명시한 URI 에 기존 리소스가 존재하지 않을 경우에는 오류가 발생한다.
즉, PUT 메서드에서 기존 리소스가 존재하지 않을 경우 신규 리소스로 생성했던 것과는 달리, PATCH 에서는 기존 리소스가 존재하지 않을 경우 메서드 자체를 사용할 수 없는 것이다.
이 또한 PATCH 가 수정 기능에 더 적합한 메서드인 이유 중 하나라고 볼 수 있으며, PUT 과의 차이점이라고 볼 수 있겠다.

💡 DELETE


마지막으로 살펴볼 메서드는 DELETE 이다.
DELETE 는 말 그대로 단순히 리소스를 제거하는 역할을 수행한다.

바로 그림을 통해 그 흐름을 살펴보자.

클라이언트에서 DELETE 메서드와 함께 /members/100 URI 를 명시해주었다.
이는 /members/100 리소스를 삭제한다는 의미를 가진다.

따라서, 서버에서는 /members/100 에 위치한 리소스를 위와 같이 제거하게 될 것이다.


DELETE 를 마지막으로, 주요 메서드인 GET POST PUT PATCH DELETE 에 대해 알아보았다.

HTTP Method 에 대해서 학습할 때는 각 메서드들이 수행하는 기능적인 역할에 대해서 이해하는 것도 중요하지만, 메서드 간 차이점이나 장, 단점에 대해 파악하면서 이를 기반으로 특정 상황에서 최선의 메서드를 선택해 구현해낼 수 있는 능력의 기반을 다지는 일도 그에 못지않게 중요하다고 생각한다.

따라서 이번 포스팅에서는 HTTP APIURI 를 구현하는 AB 의 예시를 들어 HTTP Method 의 필요성 혹은 역할에 대해 먼저 살펴보았고, 그 이후에 각 메서드 간 차이점이나 특징들을 설명하는데 집중하였다.

다음 포스팅에서는 HTTP Method 가 가지는 속성인 Safe, Idempotent, Cacheable 에 대해서 살펴보고, 설계한 HTTP Method 를 실제로 활용하는 방법에 대해서 작성할 예정이다.



출처


0개의 댓글