모든 개발자를 위한 HTTP 웹 기본 지식을 수강했습니다.
그 중 POST와 PUT 메서드 관련 새롭게 알게 된 내용을 정리해봅니다.
요약
- POST 요청은 Collection 개념에 해당한다. 리소스 관리 주체가 서버이다.
- PUT 요청은 Store 개념에 해당한다. 리소스 관리 주체가 클라이언트이다.
- POST로 새로운 제품을 등록한다면, 해당 제품의 식별자는 서버가 생성해서 Location 헤더에 내려준다.
- PUT 요청은 클라이언트가 식별자 및 모든 정보를 포함해서 요청을 보내야 하며,
없으면 생성, 있으면 대체한다. - List의 add 메서드와 Map의 put 메서드를 생각해보면 이해가 쉽다!
이번 포스팅에서 작성하게 될 POST 는 이러이러 하다~ PUT 은 이러이러 하다~ 라는 표현은
그렇게 동작하게 된다 의 의미가 아니라
규약 상 이러이러하게 구현되는 것이 관례적으로 기대된다 정도로 해석해주시면 감사하겠습니다.
또한 항상 이러한 규약에 모두 맞춰서 개발을 해야하는 것은 아니며,
개념적으로 이런 차이가 있다 라는 정도로 알고 넘어가면 충분하다고 생각합니다.
그럼 시작합니다!
🚦 POST 는 자원의 관리 주체가 서버! - List.add(object)
POST로 새 자원 생성 시, 클라이언트는 해당 자원의 위치를 서버가 알려줘야 알 수 있다.
POST https://hitalittle.ga/api/products HTTP/1.1
Content-Type: application/json
{
"name": "제품 이름",
"price": 1000000,
"imageUrl": "https://i.imgur.com/jFrnJdW.png"
}
HTTP/1.1 201 CREATED
Location https://hitalittle.ga/api/products/{generatedProductId}
위 내용은 장바구니 미션에서 저희 팀이 사용했던 제품 등록 HTTP 요청, 응답 메시지 예시입니다.
여기서 제가 이야기하고 싶은 내용은 productId 에 대한 내용입니다.
POST 요청을 통해 서버에 신규 리소스 생성을 요청할 때,
클라이언트는 일부 정보가 누락된 채로 요청을 보내게 됩니다.
위 예시에서는 productId가 누락됩니다.
왜냐하면 해당 리소스의 식별자에 해당하는 productId는 서버에서 생성하기 때문입니다.
그렇기 때문에 201 CREATED 응답코드와 함께
Location 헤더를 통해 생성된 리소스의 위치를 응답으로 알려주게 됩니다.
이처럼 서버에서 리소스의 식별자를 생성하고 관리하는 경우,
서버를 리소스의 관리 주체로 보게 되고, 이와 같은 경우를 Collection 개념으로 이야기합니다.
Location 응답 헤더에 내려주는 내용이
"products 라는 컬렉션 안에 몇번째 인덱스를 갖는 자원으로 등록되었다"
라고 응답해주는 것이라고 이해하면 Collection 이라고 부르는 이유가 이해될 것 같습니다.
제 생각엔 POST 메서드를 List의 add 메서드로 바라보면 이해가 아주 쉬운 것 같습니다.
List에 add를 요청하는 클라이언트는 식별자를 요청시점에 알 수 없습니다.
따라서 당연히 일부 정보가 누락된 채로 저장 요청을 보내게 됩니다.
그러나 List를 관리하는 주체는 add하며 해당 자원이 몇 번째 인덱스를 갖는지 식별자를 알 수 있게 되는 거죠.
이렇게 생각해보니 POST응답으로 Location 헤더에 자원의 식별자를 내려주는 게 자연스럽게 보입니다.
(물론 List에서 자원 삭제가 일어났을 때 인덱스가 당겨지는 부분에서는 차이가 있습니다)
🪣 PUT 은 자원의 관리 주체가 클라이언트! - Map.put(key, value)
단순히 PUT은 수정 혹은 대체 정도로만 알았는데...
PUT에 대해서는 일단 몰랐던 사실들이 좀 많았습니다.
첫째로 PUT은 해당 자원이 존재하지 않으면 생성한다는 점이었습니다.
존재한다면 요청 내용으로 대체합니다.
둘째로 생성시엔 201을, 대체시엔 200 또는 204를 응답한다는 점입니다.
(물론 API 설계에 따라 생성할 수도 있고 아닐 수도 있습니다. 규약 자체도 선택적으로 명시된 것 같습니다.)
그렇다면 아래의 내용은 어떻게 해석해야 할까요?
PUT https://hitalittle.ga/api/products/111 HTTP/1.1
Content-Type: application/json
{
"name": "제품명",
"price": 200000,
"imageUrl": "https://i.imgur.com/jFrnJdW.png"
}
HTTP/1.1 201 CREATED
HTTP/1.1 200 OK
저는 위에 예시로 작성된 HTTP PUT REQUEST의 내응을 다음 문장과 같이 해석하곤 했습니다.
111번에 해당하는 제품의 내용을 내가 body에 담은 내용대로 수정해줘~
따라서 111번에 해당하는 제품이 없다면 404 혹은 400 을 응답해야 한다고 생각했습니다.
그러나 PUT 메서드는 규약 상 없으면 생성, 있으면 대체입니다.
이에 따르면 아래와 같이 해석하는 것이 좀 더 적절할 것 같습니다.
111번 사물함에 body에 담아 보내는 걸로 넣어줘~
따라서 111번에 해당하는 제품이 기존에 없었다면 CREATED, 있었는데 대체되었다면 200 또는 204를 응답해야 합니다.
여기에서 POST 와 PUT 의 차이가 드러나는데요,
POST는 클라이언트가 서버에 리소스 생성 요청을 보낼 때, 불완전한 정보를 보내게 됩니다.
위의 예시에선 productId는 서버가 생성하기에 클라이언트가 누락한 채로 보냈었죠.
반면 PUT은 클라이언트가 서버에 리소스 생성 요청을 보낼 때, 모든 정보를 이미 알고 있고, 이를 보냅니다.
즉, 자원의 관리 주체가 클라이언트고 서버는 맡아주기만 하는 거죠.
사실 위 예시에서는 조금 부자연스러워 보일 수 있는데, 파일로 생각해보면 훨씬 자연스럽습니다.
이미지 저장소에 image.png 라는 파일을 업로드한다면, 추후 해당 이미지를 원할 때,
URL에 /somePath/image.png 로 요청을 하는 게 자연스러울 것입니다.
리소스 식별자를 서버가 생성한 것이 아니라 클라이언트가 지정한 것입니다.
이처럼 리소스 관리 주체가 클라이언트가 되는 PUT 요청의 개념을 Store 로 이야기하기도 합니다.
앞서 POST 요청의 Collection 개념에 대해서 제가 List의 add 메서드로 예시를 들었는데요,
PUT 요청의 Store 는 마치 Map의 put 메서드와 비슷하다고 생각이 듭니다.
(심지어 메서드 이름까지 동일합니다 소름;;)
Map의 put 메서드도 key를 명시하며 value를 전달하면
key에 해당하는 자원이 없었다면 key에 value가 할당되고
key에 해당하는 자원이 있었더라도 그것이 제거되고 새로운 value가 할당됩니다.
만약 PUT 요청 body에 name 필드만 보내고 나머지 필드를 안 보낸다면 어떻게 해야할까요?
name만 왔으니 name 필드 값만 수정하면 될까요?
규약상으론 name 이외의 필드는 null로 대체되는 것이 맞다고 합니다.
이는 Map을 생각해보면 자연스러운데요,
만약 name, price, url을 필드로 갖는 클래스를 Map에 저장하는데,
name 이외의 필드를 초기화 하지 않은 인스턴스를 Map에 put 한다면,
나머지 price와 url 필드는 null 또는 기본값으로 할당되어 저장되는 것이 당연합니다.
이런 관점에서 PUT 메서드로 API를 설계한다면,
모든 필드를 body에 보내는 것이 규약상 자연스럽다고 하겠습니다.
최초 PUT 요청의 응답 예시로 돌아간다면,
자원이 새로 생성되었을 때에는 201 응답을, 대체되었을 때에는 200 또는 204를 응답하는 것이 권장됩니다.
저는 개인적으로 204는 DELETE 요청에 대한 정상 처리 응답으로 사용하는 것을 선호하고
PUT의 정상 처리 응답으로는 200을 선호합니다.
✨ 결론
- 개념적으론 이러한데.. 실무에선 앞서 소개한 PUT의 명세를 엄격하게 지키지는 않는다고 합니다.
- Spring과 MySQL의 설계에서도 비슷한 설계 사상이 보였는데, POST와 PUT도 List와 Map과 유사하게 보여지네요!
- 코딩을 지탱하는 기술에서 언급한 비교를 통한 학습이 확실히 효과적인 것 같습니다.
- 아래 사진은 장바구니 미션에서 사용했던 수량 수정 API 인데요,
실무적 차원에서 아래와 같은 설계가 완전히 틀렸다고는 여전히 생각하지 않습니다만
분명 개선의 여지는 있는 것 같습니다.
'우아한테크코스 4기' 카테고리의 다른 글
🧑💻 UX 워크샵 후기 (2) | 2022.07.02 |
---|---|
💿 Response Header 와 브라우저를 이용한 캐싱 (0) | 2022.06.19 |
JJWT 훑어보기 (0) | 2022.06.06 |
🪪 출입증 (8) | 2022.06.04 |
🪙JWT vs 🍪Session, 그리고 실제 사례 살짝 분석 (찜꽁, 프롤로그, LMS) (8) | 2022.06.01 |