저번 포스팅에서는 HTTP
의 기본적인 개념과 역사, 다양한 특징들에 대해서 알아보았다.
그 중 HTTP Message
의 Start-line 에서 GET
이라는 메서드를 잠깐 확인할 수 있었는데, 이번 포스팅에서 다루게 될 HTTP Method
중 하나로 HTTP
에서 아주 핵심적인 개념이다.
이는 이후에 학습하게될 REST API
에서도 필수적인 개념이기 때문에 각 메서드들이 가지는 차이점이 무엇인지, 특정 상황에서 이 메서드가 과연 최선의 선택이었는지를 항상 고민하면서 공부해나가는 것이 중요하다고 볼 수 있겠다.
본격적으로 각 메서드들에 대해서 알아보기 전에, HTTP API
를 설계하는 예시 두 가지를 확인하고 어떤 방식으로 설계하는 것이 바람직한 설계 방법인지를 고민해보도록 하자.
요구사항은 다음과 같다.
📌 Requirements
- 회원 목록 조회
- 회원 조회
- 회원 등록
- 회원 수정
- 회원 삭제
A 는 위와 같은 요구 사항들에 대해 HTTP API
를 설계하라는 지시를 받았고, API URI
를 다음과 같이 작성하였다.
겉보기에는 아주 그럴듯하다. URI
를 보자마자 어떤 작업을 수행하고자 하는지를 직관적으로 확인할 수 있고, 외우기도 아주 쉬워보인다.
바로 다음 설계를 확인해보자.
B 는 위와 같은 요구 사항들에 대해 HTTP API
를 설계하라는 지시를 받았고, API URI
를 다음과 같이 작성하였다.
A 의 설계와 비교하여 아주 간단해보인다. 하지만 어떤 작업을 수행하는지를 직관적으로 알아보기 힘들고, 각각의 URI
를 구분할 수 없을 것 같다.
그렇다면, 과연 A 의 설계와 B 의 설계 중 어떤 방법이 더 바람직한 설계 방법일까?
이를 위해서 우리는 이전에 살펴봤던 URI
의 개념과 본질에 대해서 다시 생각해볼 필요가 있다.
📌
URI
Uniform Resource Identifier
의 약어로 인터넷에 있는 자원의 위치를 나타내는 유일한 주소
URI
는 인터넷에 있는 자원, 즉 리소스의 위치를 나타내는 유일한 주소이다.
따라서, URI
의 본질은 리소스를 식별하는 것이 되어야 한다.
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는 요청 메서드를 정의하여, 주어진 리소스에 수행하길 원하는 행동을 나타냅니다. 간혹 요청 메서드를 "HTTP 동사"라고 부르기도 합니다.
(https://developer.mozilla.org/ko/docs/Web/HTTP/Methods)
위는 HTTP Method
에 대한 정의를 MDN 에서 발췌한 내용이다.
즉, HTTP 메서드는 동사에 해당하며, 클라이언트가 대상 리소스에 수행하고자하는 행위를 서버에 요청하기 위해 사용한다.
B 의 URI
설계를 다시 확인해보면,
회원 목록 조회를 제외한 모든 기능의 URI
가 동일하여 조회, 등록, 수정, 삭제와 같은 행위에 대해 구분하지 못하고 있음을 알 수 있다.
이 때, 각각의 URI
와 HTTP 메서드를 함께 사용하게 되면,
URI
가 공통적으로 가리키는 리소스에 대해 수행하고자하는 행위를 서로 다른 HTTP 메서드를 활용하여 구분하고 요청할 수 있는 것이다. 여기서 HTTP 메서드에 해당하는 GET
POST
PATCH
DELETE
는 조회, 등록, 수정, 삭제의 리소스에 대한 행위를 정의할 것이다.
그럼 주로 쓰이는 HTTP 메서드들에 대해서 알아보도록 하자.
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-line
과 Header
를 보면 HTTP
의 버전과 상태 코드, Body
에 담긴 데이터의 타입과 길이 정보가 담겨있는 것을 확인할 수 있다.
물론 예시에서는 JSON 타입을 사용했지만, 이는 html 이 될 수도 있고, jpeg 나 XML 이 될 수도 있다.
이제 메시지를 전달받은 클라이언트는 리소스의 타입에 따라 데이터를 읽어 화면에 그려주거나 웹브라우저로 랜더링하는 등의 처리를 수행하게 되는 것이다.
다음은 POST
메서드이다.
POST
는 클라이언트가 서버에게 데이터를 보내면서, 해당 데이터에 대한 처리를 요청할 때 사용한다.
즉, POST
메시지의 Body
에 데이터를 담아 서버로 보내면, 서버는 Body
를 통해 들어온 데이터를 처리하는 모든 기능을 수행하는 것이다.
이 때의 처리는 클라이언트와 서버가 사전에 약속한 작업을 수행하는 것을 의미하며, 주로 신규 리소스의 등록이나, 프로세스의 처리를 의미한다.
위 그림에서 POST
메서드와 /members 를 명시하고 Body
에 JSON
데이터를 담은 모습을 확인할 수 있는데 이는 사전에 클라이언트와 서버가 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
은 리소스를 덮어쓰는 기능을 수행한다.
즉, 지정된 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
메서드는 컴퓨터의 파일 붙여넣기와 유사하다.
기존의 리소스가 존재할 경우 리소스를 덮어쓰고, 기존의 리소스가 존재하지 않을 경우에는 신규 리소스로 생성된다.
앞서 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
메서드와 함께 /members/100 URI
를 명시해주었다.
이는 /members/100 리소스를 삭제한다는 의미를 가진다.
따라서, 서버에서는 /members/100 에 위치한 리소스를 위와 같이 제거하게 될 것이다.
DELETE
를 마지막으로, 주요 메서드인 GET
POST
PUT
PATCH
DELETE
에 대해 알아보았다.
HTTP Method
에 대해서 학습할 때는 각 메서드들이 수행하는 기능적인 역할에 대해서 이해하는 것도 중요하지만, 메서드 간 차이점이나 장, 단점에 대해 파악하면서 이를 기반으로 특정 상황에서 최선의 메서드를 선택해 구현해낼 수 있는 능력의 기반을 다지는 일도 그에 못지않게 중요하다고 생각한다.
따라서 이번 포스팅에서는 HTTP API
의 URI
를 구현하는 A 와 B 의 예시를 들어 HTTP Method
의 필요성 혹은 역할에 대해 먼저 살펴보았고, 그 이후에 각 메서드 간 차이점이나 특징들을 설명하는데 집중하였다.
다음 포스팅에서는 HTTP Method
가 가지는 속성인 Safe, Idempotent, Cacheable 에 대해서 살펴보고, 설계한 HTTP Method
를 실제로 활용하는 방법에 대해서 작성할 예정이다.