doortts / blog

0 stars 0 forks source link

REST API 기반에서 리소스를 업데이트 할때는 POST, PUT, PATCH 어떤걸 써야 하나요? #16

Open doortts opened 4 years ago

doortts commented 4 years ago

@doortts (doortts) 님이 작성한 이슈입니다. ---

업데이트 할때는 POST? PUT? PATCH?

Summary

TL;DR

REST 기반의 API 서버를 만들다보면 애플리케이션의 기본 상태관리에 해당하는 CRUD 처리를 각각 HTTP의 POST, GET, PUT, DELETE 메서드로 대응해서 생각하기 쉽습니다.

이중에서 UPDATE 에 사용하는 PUT에 대해 이야기 해보려고 합니다.

예를들면 직원A의 전화번호를 변경해야 하는 경우의 예시입니다

PUT /employees/nl10216 HTTP/1.1 Host: navercorp.cm Accept: application/json Content-Length: 68

{"employeeNo":"nl10216", "mobilePhone":"010-9557-8181"}

사번과 전화번호 말고도 여러가지가 제 사원정보에 존재하는데 당연히 다 보낼 필요가 없고 uid에 해당하는 사번과 변경이 필요한 핸번만 본문으로 보냅니다. 아쉽지만 REST 를 잘 지키려고 POST 대신 PUT 쓴건데 이건 오히려 REST 스타일을 위반합니다.

HTTP PUT 메서드의 RFC 문서상 정의는 이렇습니다.

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.[주1]

즉, PUT 메서드는 요청시에 담아 보내는 데이터(message payload)를 이용해서 새로운 리소스를 생성(create)하거나 이미 존재할 경우 대체(replace) 하라는 명령입니다. 다시말해, HTTP 명령 대상 URI 의 Resource 부분을 업데이트 할때 사용하는 메서드가 아닙니다.

실제로 PUT의 정의대로라면 navercorp.com/employees/nl10216 에 PUT으로 보냈으니 둘 중 하나가 되야 합니다. 없다면 생성하거나 있다면 교체하거나.

그렇다면 UPDATE로는 뭘 써야 하나?

리소스의 일부 수정일 경우 POST 나 PATCH 를 사용합니다.

그리고 고민되면 그냥 POST 쓰면 됩니다. POST로 리소스를 업데이트하는건 오히려 REST 스타일을 잘 지키는 겁니다.

원래 POST가 그런 애매한 메서드입니다. 정의자체도 "리소스를 생성하거나 기존 리소스에 추가하거나 혹은 의미에 맞게 잘 쓰세요" 같은 식으로 애매한 부분들이 있습니다. 이건 HTTP가 GET 과 POST 두개만 있어서 'GET은 읽기 나머지는 다 POST!' 이런 시절이 있었기 때문입니다.

'부분 업데이트? 뭐 POST 쓰면 되지~ 이전에도 그랬는데 뭐~ 그리고 리소스의 네트워크상의 특정 위치에 존재하는 정보라 수정보다는 새걸로 갱신(replace) 하는 게 더 atomic 한 처리에도 좋을것같아(PUT)' 같은 식으로 생각했던걸지도 모르겠습니다. (마치 파일시스템의 file update의 atomic lock과 비슷한 식?)

PATCH 가 더 멋져보이는데?

PATCH 메소드는 나중에 따로 만들어진 스펙입니다. PATCH Method for HTTP (번호도 외우기 쉬운 5789. 이런식으로 낭비되는 내 뇌 메모리)

The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request- URI.

'이제부터 업데이트는 PUT이 아니라 PATCH 쓰겠어!' 라고 마음먹고 나면 이제 주의해야 할 부분이 있습니다.

특성(property) 측면에서 PUT 메소드는 'safe 하지는 않지만 idempotent' 합니다. 즉, 리소스의 변경을 일으키지만 여러번 실행되어도 1번 실행된것과 동일한 결과를 가져야 한다(더 어려운 말로 멱등성)입니다. PATCH는 'safe 하지 않고 idempotent 하지도 않는 메서드'라고 가정되어 있습니다. 그래서 여러번 실행되면 결과가 계속 달라질 가능성을 가정합니다. 그래서 신경쓸 부분이 상대적으로 많아집니다. 문서를 보면 알겠지만 PATCH 메소드를 사용하려면 Etag의 조합과 더불어 MUST, MUST NOT, SHOULD NOT 시리즈의 제약 내용들이 펼쳐집니다.

이런 저런 스펙내용을 떠나 기본적으로 리소스의 부분수정이라 충돌에 대한 상황이 고려되어야 하는 것도 필연적입니다.

관련해서는 고민해야 할 것들이 많은데 다행히/당연히 사람들이 미리 고민해서 관련 표준문서들을 만들어 놓았습니다.

다음 두 개를 참고하면 됩니다. JavaScript Object Notation (JSON) Patch Detecting the Lost Update Problem Using Unreserved Checkout

주의 HTTP PATCH 메소드를 사용하고 Message Body로 JSON 을 사용한다고 해서 반드시 JavaScript Object Notation (JSON) Patch 나 JSON Merge Patch 등을 구현해야 한다는 뜻은 아닙니다. 컨텐츠 수정에 대해서는 Detecting the Lost Update Problem Using Unreserved Checkout 같은 문제들과 상황들을 고려해야 하는데 위에 적힌 스펙기반으로 JSON PATCH 를 사용하면 그런 문제들에 대해 고민해 놓았으니까 쓰기만 하면 됩니다! 같은 이야기입니다.

그런데 읽다 보면 음.. 그냥 POST 쓰자..라는 생각이 들지도 모릅니다. (REST 스타일 잘 지켜서 API 서비스 만들기 어렵네)

각주

[주1] message payload: HTTP 통신때에 실어 보내는 본문(Message Body)은 곧잘 Enclosed Entity 혹은 Message Payload 라고도 부릅니다.

참고자료: Wikipedia의 REST 정의 Roy Fielding논문중 Experience and Evaluation 부분 - 진지하게 경험담 듣고 싶으면 일독을 권합니다

. . . . . . . . . One more thing...

아는 동생이 물어보면 아는척 대답해 줄 때 쓰는 REST 설명

우선 REST는 REST Architectural style 혹은 RESTful 이라고 불러야 합니다. 그리고 다음의 두 가지를 기반으로 가정하고 있습니다

비상태 프로토콜(Stateless protocol)과 표준명령들(Standard operations)

RESTful 웹 서비스에서는 비상태 프로토콜로 HTTP 를, 표준명령어로는 HTTP Method를 사용합니다. 이 두가지를 기반으로 자원(Resource)의 위치를 가르키는 네트워크 고유 주소(URI)와 자원의 상태 변이를 다루는 방식을 RESTful 웹 서비스라고 말합니다.

정확하게 쓰려고 하다니 말이 불필요하게 어려워졌네요.

다시 돌아가서, REST는 HTTP 표준 명령어(Methods)를 상황과 URL 에 맞게 잘 써야 더 RESTful 한 서비스가 된다는 이야기 입니다.

'이봐! 목적이 그게 아니잖아! 좋은 서비스나 앱을 만드는것이 목적이지 더 RESTful 해지는것이 목적이 아냐!' 라는 말이 당연히 나오는데요, 사실 더 RESTful 하려는 목적은 더 RESTful 할 수록 더 성능도 좋고 안정적이고, 재사용을 통해 성장하는 시스템이 가능해질거라 믿기 때문입니다.

RESTful systems aim for fast performance, reliability, and the ability to grow by reusing components that can be managed and updated without affecting the system as a whole, even while it is running.

RESTful 의 지향점이 그렇다는거지 RESTful 하면 무조껀 좋아요..는 당연히 아닌거겠죠. 하지만 기술적 이상에 한없이 다가가려는 노력, 그 노력과 수정, 그리고 넘어섬과 반복.

그게 어제보다는 1 mm라도 더 나아진 엔지니어가 되는 길이 아닐까 생각해봅니다.

네. 뭐. 그렇습니다.