프론트엔드 개발자와 백엔드 개발자가 만나는 지점인 API !
일반적으로 앱이나 웹 상에서 작동하는 어플리케이션을 개발할 때는 주로 HTTP나 HTTPS 프로토콜을 사용하여 API를 만들게 되는데, 이 API의 정의가 얼마나 직관적이고, 명확하냐에 따라 프로젝트의 복잡도가 크게 낮아지게 될 만큼 시스템 설계에 있어서 꽤나 중요한 자리를 차지하고 있다.
그래서 우리는 일종의 약속을 통해 이 API가 어떤 동작을 수행하는 API인지를 명확하게 정의해야 하며,
이 API 정의 과정에서 우리가 사용할 수 있는 요소들이 바로 HTTP 메소드와 URI (Uniform Resource Identifiers)이다.
GET https://evan.com/users/1
HTTP API의 엔드포인트는 위와 같이 HTTP 메소드와 URI를 사용하여 이 API가 어떠한 동작을 수행하는 API인지를 표현하게 된다.
여기서 중요한 포인트는, 사용자가 이 표현을 읽고 난 후 API에게 기대하는 동작과 실제로 서버가 수행하는 동작이
명확하게 일치되어야 한다는 것이다!!
그래서 우리는 REST와 같은 가이드 라인을 사용한다.
(REST는 지난 2000년, 로이 필딩(Roy Fielding)이 자신의 박사 학위 논문에서 소개한 API 아키텍처 가이드라인이며, 무려 20년이 지난 현재까지도 널리 사용되고 있다.
하지만 서버의 상태를 알려주는 HTTP 상태 코드와 같이 이건 말 그대로 가이드라인이기 때문에 지키지 않는다고 해서 에러가 발생하거나 하는게 아니지만, 그렇다고 해서 이런 가이드라인을 무시하고 마음대로 개발해도 된다는 뜻은 아니다.
REST라는 용어와 개념은 이미 업계에 널리 퍼져있기 떄문에, 많은 개발자들이 HTTP API를 만났을 때, 이 API가 당연히 RESTful하게 작성되었을 것이라고 생각한다. (사실 상 표준이라고 봐도 무방할 정도의 영향력 이다))
=> REST가 도대체 왜 나오게 된 것인지, REST가 무엇을 의미하는 것인지!
Endpoint 란?
- Endpoint는 API가 RESTful API를 인터페이스를 통해 서버의 리소스에 액세스 할 수 있도록 해주는 URL이다.
- 서비스를 사용 가능하도록 하는 서비스에서 제공하는 커뮤니케이션의 한쪽 끝
인터페이스에서(Web, Mobile... 사용자 조작 화면)에서 DB의 어떤 자원에 대해 CRUD 동작을 하려고 할 때,
구체적으로 요구하는 URL의 정보가 Endpoint라고 할 수 있다.
(예를 들어 특정 유저의 정보를 조회하고자 할 때 api_site.com/{apikey}형식의 API가 있고,
여기에 파라미터를 포함하여 요청을 보낸 api_site.com/{apikey}/users/{userId}/infos라는 URL이 EndPoint라는 것
REST가 의미하는 것이 무엇일까
REST는 REpresentational State Transfer 의 약자이다.
이 거창해보이는 단어의 핵심은 바로 Representatioanl State, 대표적인 상태 정도의 뜻을 가진 단어이며, 더 유연하게 번역해보면 표현된 상태 라고 할 수 있다.
이때 이야기하는 상태라 함은 서버가 가지고 있는 리소스의 상태를 이야기 한다.
즉, REST는 통신을 통해 자원의 표현된 상태를 주고받는 것에 대한 아키텍처 가이드라인이라고 할 수 있다.
REST에 대한 이야기를 할 떄, 표현된 상태 (Representational State)에 대한 이해를 어려워하는데, 이는 클라이언트와 서버가 API 통신을 통해 주고 받고 있는 것들이 리소스 그 자체라고 생각하기 때문이다.
하지만 조금만 생각해보며 우리가 통신을 통해 리소스를 직접 주고 받고 있지 않다는 사실을 알 수 있다.
사실 주고 받는 것은 리소스가 아니다
우리가 API를 통해 주고 받는 리소스는 어떤 문서일수도 있고, 이미지 혹은 단순한 JSON 데이터일수도 있다.
하지만 사실 우리는 리소스를 직접 주고 받는 것이 아니다. 한번 간단한 예시를 통해 이 말이 어떤 의미인지 살펴보자.
여기 클라이언트가 서버에게 특정 유저의 정보를 받아오는 API 엔드포인트를 통해 요청을 보냈다.
GET https://iamserver.com/api/users/2
Host: iamserver.com
Accept: application/json
클라이언트는 이 API 엔드포인트를 통해 서버에게 2번 유저의 자원을 요청했고,
서버가 요청을 성공적으로 처리했다면 클라이언트는 서버로부터
대략 이런 느낌의 응답을 받을 수 있다.
HTTP/1.1 200 OK
Content-Length: 45
Content-Type:application/json
{
id:2,
name:'Eva',
org:'Viva Republica'
}
서버가 보내준 응답의 body에는 2번 유저의 데이터가 담겨있다.
일반적으로 우리는 이 상황을 /api/users/2라는 엔드포인트를 통해서 2번 유저 데이터 리소스를 받아왔다고 표현하고는 한다.
그런데.. 정말로 서버가 보내준 저 JSON 데이터가 리소스 그 자체일까!
아니다!
사실 서버에서 보내준 저 JSON은 리소스 원본이 아니라, 데이터 베이스에 저장된 2번 유저의 데이터 리소스를 표현한 것에 불과하다.
서버는 클라이언트의 요청을 받고 2번 유저의 정보를 데이터 베이스에서 조회한 후,
요청의 헤더에 담겨있던 application/json 이라는 방식으로 표현하여 응답에 담아준 것이다.
곰곰히 생각해보면 당연한 이야기인 것이, 서버가 접근하는 진짜 리소스 원본은 그저 데이터베이스에 담겨있는 하나의 로우 이거나, 파일에 작성된 데이터일 것이다.
물론 서버의 로컬 시스템에 리소스를 JSON 파일로 저장하고 있을 수도 있지만, 어쨌든 포인트는 서버가 보내준 저 JSON이 원본 리소스가 아니라는 것이다!
서버가 보내준 JSON은 단지 데이터 베이스에 저장되어있는 원본 데이터 리소스의 현재 상태를 표현한 것이다.
리소스를 표현한 상태라는 것의 의미
앞서 이야기 했듯이 REST가 이야기하는 Representation State라는 단어는 원본 리소스를 표현한 상태라는 것을 의미한다.
원본 리소스는 데이터 베이스에 저장된 하나의 로우로써 존재하지만, 클라이언트에게 이걸 그대로 넘겨줄 수 없으니
서버가 원본 리소스를 읽어와서 적당한 상태로 표현해주는 것이다.
그리고 이 적당한 상태에 대한 힌트는 HTTP 요청 헤더나 응답 헤더에 전부 나와있다.
GET https://iamserver.com/api/users/2
Host: iamserver.com
Accept: application/json
위에서 예시로 들었던 상황에서 클라이언트는 서버에게 2번 유저의 리소스를 요청하며
요청 헤더의 Accept라는 키에 application/json이라는 값을 담아서 보냈다.
클라이언트가 서버에게 "2번 유저의 상태를 json으로 표현해줘" 라는 요청을 보낸 것이다.
만약 클라이언트가 application/json이 아닌, application/xml를 담아 보냈고,
서버가 XML 포맷의 표현을 지원하도록 작성되어있다면 2번 유저의 리소스는 XML 형태로 표현되어 내려왔을 것이다.
그리고 서버는 응답 헤더에 Content-Type이나 Content-Language와 같은 키를 사용하여 이 리소스가 어떤 방식으로 표현된 상태인지 클라이언트에게 알려주고, 클라이언트 또한 이 정보를 읽은 후 각 컨텐츠 타입에 맞게 정보를 파싱한다.
즉, 클라이언트는 2번 유저의 리소스를 받은 것이 아니다.
JSON으로 표현된 2번 유저 리소스의 현재 상태를 받은 것이다.
RESTful API
앞서 이야기 했듯, REST는 결국 리소스를 어떻게 하면 명확하게 표현할 수 있을 지에 대한 것에 집중하는 아키텍처 스타일이다.
하지만 우리가 HTTP API를 사용할 떄는 단순히 리소스의 표현 상태만으로는 클라이언트가 API를 호출했을 때 서버에서 정확히 어떤 일이 발생하는지는 알기가 어렵다.
REST는 단지 리소스가 표현된 상태만을 이야기 할 뿐,
어떠한 "행위" 에 대해서는 이야기 하고 있지 않다.
하지만 클라이언트가 서버의 API를 사용할 때 원하는 것은 소스를 생성/삭제/수정 등 명백히 어떠한 행위이다.
그래서 RESTful API에서는 REST 아키텍처를 통해 표현된 리소스와 더불어
어떠한 행위를 명시할 수 있는 HTTP 메소드와 URI까지 활용하게 되며, 각 요소들이 표현하고 있는 것들은 다음과 같다.
1. 리소스가 어떻게 표현되는지? - REST
2. 어떤 리소스인지? - URI
3. 어떤 행위인지? - HTTP 메소드
즉, 이 요소들을 사용하여 명확하게 정의된 API를 사용하는 클라이언트는
굳이 API에 대한 구구절절한 설명 필요 없이 GET /users/2와 같은 엔드포인트만 보고도
"음.. 2번 유저의 정보를 가져오는 API겠군"이라고 추측할 수 있게 되는 것이다.
이 3가지 요소들 중 리소스를 표현하는 방법인 REST에 대해서는 앞에서 이미 이야기 했으니,
이번 섹션에서는
어떤 리소스인지를 표현하는 URI와
어떤 행위인지를 표현하는 HTTP 메소드에 대해 알아보도록 한다.
URI를 사용하여 어떤 리소스인지 표현하자
RESTful API의 URI는 이 API가 어떤 리소스에 대한 API인지를 나타내는 요소이다.
예를 들어, 서비스를 사용하는 유저의 목록을 가져오는 API가 있다고 가정해보자.
이 API를 사용하는 클라이언트가 접근하고자 하는 리소스는 유저가 될것이고, 이 API의 URI는 명확하게 유저를 표현하고 있어야 한다.
GET /users
솔직히 이정도 URI면 지나가던 중학생이 봐도 뭔가 유저와 관련이 있다는 것을 알 수 있을 정도로 명확하다.
그런데 유저라는 리소스를 왜 user라고 표현하지 않고 굳이 users라는 복수형으로 표현하고 있는 것일까?
그 이유는 유저라는 리소스가 특정한 하나의 객체가 아니기 때문이다.
(이건 영어로 나는 고양이를 좋아해! 라는 문장을 이야기 할 때 i love a cat이 아닌 i love cats라고 하는 것과 같은 맥락이다.
어떤 특정한 고양이를 좋아하는 것이 아니라 고양이라는 생물 자체를 좋아하는 것이고...
고양이라는 단어는 우리 아파트 앞에서 쓰레기통 뒤지고 있는 점박이 고양이, 이름 모를 사람이 인스타그램에 이쁘다고 자랑하는 지네 집 고양이, 길 가다가 우연히 마주치는 고양이까지 모두 포함되는것과 같이
다소 추상적인 리소스를 의미하는 것이다.)
유저들이라는 추상적인 리소스에서 한단계 더 구체화 시켜보도록 하자.
유저라는 추상적인 리소스를 조금 더 구체화 시킨 다음 레벨의 리소스는 특정 유저이다.
리소스의 계층을 표현하기
일반적으로 유저들은 각각 고유한 ID를 가지고 있는 경우가 많으니, 이 ID를 사용하면 특정한 유저를 대충 이런 URI로 표현할 수 있다.
GET /users/2
굳이 설명하지 않아도 눈치 채겠지만, 이 URI는 유저들을 의미하는 /users라는 URI 뒤 쪽에 각 유저들이 고유하게 가지고 있는 ID를 추가하여 특정한 유저를 식별할 수 있도록 만든 것이다.
또한 이 URI가 표현하고 있는 리소스인 2번 유저는 유저라는 리소스의 하위 집합이라고 할 수 있고,
RESTful API는 이런 리소스 간의 계층 구조를 /를 사용하여 표현할 것을 권장하고 있다.
이러한 리소스 간의 계층 구조는 어플리케이션에서 사용하는 리소스들의 관계를 어떻게 설정할 것이냐에 대한 문제이기 때문에 API를 설계할 때 굉장히 중요하고 예민한 요소인데다가, 대부분의 설계 패턴이 그러하듯이 이건 정답이 정해져있는 것이 아니기 때문에 고민을 깊게 할 수 밖에 없는 문제이다.
정답이 정해져 있지 않다는 것은 프로필 사진이라는 리소스를 /users/2/profile-image라는 계층 구조가 아니라
/profile-images/users/2 와 같은 계층으로 설계해도 논리 상으로는 아무런 문제가 없다는 것을 의미한다.
여기에는 단지 프로필 사진이라는 리소스가 어떤 의미를 가질것이냐의 차이만 있을 뿐이다.
/users/2/profile-image
유저들 중 2번 유저의 프로필 사진
/profile-images/users/2
프로필 사진들 중 유저들의 프로필 사진 중 2번 유저의 프로필 사진
이처럼 같은 프로필 사진이지만 리소스의 계층을 어떻게 설계하냐에 따라 의미가 완전히 달라지게 된다.
이 케이스의 경우, 유저 외에 프로필 사진을 가질 수 있는 다른 리소스가 존재하지 않는다면 /users/2/profile-image가 적당하지만 유저 외에도 다양한 리소스가 프로필 사진을 가져야 하는 상황이라면 /profile-images/users/2라는 계층 구조도 고민해 볼 수있을 것이다.
결국 우리가 고민해야 할 문제는 특정 유저의 프로필 사진이라는 리소스를 포함하는 상위 계층 리소스가 "유저가 더 명확하냐", "프로필 사진이 더 명확하냐" 인 것이다.
URI에는 행위가 표현되면 안된다
RESTful API의 URI를 설계할 때 또 한가지 중요한 것은 URI에 어떠한 행위를 의미하는 표현이 포함되어서는 안되는 것이다.
예를 들어 유저를 삭제하는 엔드포인트가 하나 있다고 생각해보자.
이때 HTTP 메소드에 익숙하지 않는다면, 대략 이런 느낌의 엔드포인트를 설계할 수도 있다.
POST /users/2/delete
이 엔드포인트의 URI에는 삭제 행위를 의미하는 delete라는 표현이 포함되어 있다.
사실 이대로도 이 API가 어떤 역할을 수행하는 API인지 인지하기에는 큰 무리가 없지만,
RESTful API는 URI를 사용하여 행위를 표현하지 않을 것을 권고한다.
URI가 가지는 의미는 철저히 어떤 리소스인지, 그리고 리소스의 계층 구조에 대한 것 뿐이여야 한다.
API가 수행하는 행위는 되도록이면 올바른 HTTP 메소드를 사용하여 표현해주는 것이 좋다.
그리고 단순히 이건 RESTful API가 이런 설계를 권고하기 때문인 것도 있지만, RESTful API의 가이드라인을 지키지 않도록 개발된 여러분의 어플리케이션이 이미 RESTful API가이드 라인을 지키며 개발된 다른 어플리케이션들과 통신할 때 어떤 부작용이 발생할 지 모르기 때문이다. (당장 웹 브라우저만 해도 HTTP 메소드와 상태 코드에 상당히 종속되어 설계되어있다.)
따라서 올바르게 작성된 엔드포인트는 삭제를 의미하는 HTTP 메소드인 DELETE 를 사용한 이런 엔드포인트가 될 것이다.
DELETE /users/2
지금까지 URI를 사용하여 리소스를 표현하는 방법에 대해서 알아보았으니,
이제는 API의 행위를 표현하는 방법에 대해서 알아보자.
HTTP 메소드를 사용하여 어떤 행위인지 표현하자
RESTful API는 HTTP 메소드를 사용하여 API가 수행하는 행위를 표현하도록 권고하고 있다.
HTTP 메소드는 나름 RFC-2616에서 정의된 표준이기 때문에 상황에 맞지 않는 메소드를 사용하게 되면 어플리케이션이 예상하지 못한 동작을 일으킬 수 있다는 사실을 기억하도록 하자.
사실 API를 사용하여 하게 되는 행위는 대부분 CRUD(Create, Read, Update, Delete)이기 때문에
몇가지 특수한 경우를 제외하면 단 5가지의 HTTP 메소드만으로도 대부분의 API를 정의할 수 있다.
Method | 의미 |
GET | 리소스를 조회한다 |
PUT | 리소스를 대체한다 |
DELETE | 리소스를 삭제한다 |
POST | 리소스를 생성한다 |
PATCH | 리소스의 일부를 수정한다 |
이 외에도 HEAD, OPTION, TRACE 등의 메소드도 존재하기는 하지만, 사실 이 5가지 메소드가 가지는 역할만 확실히 알고 있어도 HTTP 메소드를 사용하여 올바를 행위를 표현하거나 RESTful API를 설계하기에는 전혀 문제가 없다.
여기서 한 가지 헷갈릴 만한 것은 바로 PUT과 PATCH 메소드인데, 이 메소드들은 동일하게 "리소스를 수정한다"라는 의미로 해석되는 경우가 많기 때문에 정확히 어떠한 경우에 PUT을 사용하고 어떠한 경우에 PATCH를 사용해야 하는지 구분하기 어려운 경우가 많다.
PUT과 PATCH의 차이?
흔히 PUT 메소드를 리소스를 수정한다는 개념으로 설명하고는 하지만, 실제 PUT 메소드가 의미하는 것은 리소스를 수정하는 것이 아니라 리소스를 요청 바디에 담긴 것으로 대체하는 것이다.
한번 {id: 1, name: 'evan'}이라는 유저 리소스의 이름을 ethan으로 수정해야 하는 상황을 생각해보자.
우리가 PUT 메소드를 사용하여 이 리소스를 수정한다면 우리는 반드시 요청 바디에 유저 리소스 전체를 표현하여 보내야 한다.
즉, 수정할 사항만 보내는 것이 아니라 수정하지 않을 사항까지도 모두 보내야 한다는 것이다.
PUT /users/1
{id: 1, name:'ethan'}
이렇게 리소스를 대체한다는 PUT 메소드의 특성 상, 실수로 {id: null, name:'ethan'} 과 같은 리소스를 전송해버리기라도 한다면 이 유저는 영영 ID를 잃어버린 비운의 유저가 되어버리는 경우도 발생할 수 있다. (사실 이 정도 예외처리는 다들 기본적으로 해놓기는 한다.)
하지만 그냥 리소스를 받아서 대체하면 된다는 동작 자체가 워낙 심플하기 때문에 리소스를 수정하는 쪽이든,
받아서 처리하는 쪽이든 이것저것 신경써줘야할 일이 별로 없어서 편하기는 하다.
반면 우리가 PATCH 메소드를 사용하여 방금과 같이 동일한 행위를 하려고 하면 어떻게 될까
PATCH /users/1
{name: 'ethan'}
PUT 메소드와 다르게 PATCH 메소드는 진짜로 현재 저장되어 있는 리소스에 수정을 가하는 행위를 의미하기 때문에 굳이 수정하지 않은 사항을 요청 바디에 담아줄 필요도 없다.
PATCH 메소드는 PUT처럼 수정하지 않을 사항까지 보낼 필요가 없고 진짜 수정하고 싶은 사항만 깔끔하게 보내면 되기 떄문에, 쓸떼없이 큰 요청 바디를 만들지 않을 수 있다.
또한 실제로 이러한 수정 동작을 수행하는 API를 사용할 때는 SQL의 UPDATE와 동일한 의미를 떠올리는 경우가 많기 때문에, 리소스를 대체하는 PUT 메소드보다 리소스의 일부를 수정하는 PATCH 메소드가 수정이라는 의미를 가지기에도 더 적합하다고 할 수 있다.
아직까지는 리소스를 수정하는 행위를 표현할 때 PUT 메소드를 주로 사용하는 경우가 많기는 하지만,
PUT 메소드와 PATCH메소드의 의미적인 차이는 분명히 존재하므로 API의 엔드포인트를 설계할 때
리소스를 대체, 리소스를 수정 중 원하는 행위와 일치하는 메소드를 사용하는 것을 권장한다.
그러나 PUT 메소드와 PATCH 메소드의 중요한 차이점은 이러한 행위의 의미가 아니라,
PUT 메소드는 반드시 멱등성을 보장하지만 PATCH 메소드는 멱등성을 보장하지 않을 수도 있다는 것이다!
메소드가 멱등성을 보장하는가?
멱등성이란, 수학이나 전산학에서 어떤 대상에 대한 연산을 여러번 적용해도 결과가 달라지지 않는 성질을 의미한다.
즉, 단순히 HTTP 메소드에만 국한된 이야기는 아니고 이는 데이터 베이스나 파일에 자원을 읽고 쓰는 등 컴퓨터가 수행하는 모든 연산에 해당되는 이야기이다.
가장 대표적으로 멱등성이 보장되는 연산은 어떤 수에 1을 곱하는 연산이다 x=>x*1 과 같은 함수는 어떠한 값에 1번을 적용하든, 10000번을 적용하든 항상 x를 반환한다.
그러나 1을 계속 곱하는 것이 아니라 1을 더하거나 빼는 함수라면 한번 호출될때마다 인자로 주어진 값을 계속 증가시키거나 감소시킬 것이므로 항상 같은 값을 반환하지 않는다.
이러한 성질의 연산이 바로 멱등성을 보장하지 않는 연산의 대표적인 예이다.
HTTP 메소드 또한 결국 어떤 자원을 읽고 쓰고 수정하고 지우는 CRUD에 대한 의미를 가지기 때문에 우리는 어떤 행위가 멱등성을 보장하고 어떤 행위가 멱등성을 보장하고 있는지 알고 있어야 어플리케이션이 예상치 못한 방향으로 동작하는 것을 방지할 수 있다.
Method | 멱등성 보장 |
GET | O |
PUT | O |
DELETE | O |
POST | X |
PATCH | X |
GET 메소드는 단지 리소스를 읽어 오는 행위를 의미하기에 아무리 여러번 수행해도 결과가 변경되거나 하지는 않을 것이다.
마찬가지로 요청에 담긴 리소스로 기존 리소스를 그대로 대체해버리는 PUT 메소드 또한 여러 번 수행한다 한들 요청에담긴 리소스가 변하지 않는 이상 연산 결과가 동일할 것이다.
즉, 어떤 리소스를 읽어오거나 대체하는 연산은 멱등성을 보장한다고 이야기할 수 있다.
그렇다면 멱등성이 보장되지 않는 케이스는 어떤 것이 있을까?
POST 메소드의 경우 리소스를 새롭게 생성하는 행위를 의미하기 때문에 여러번 수행하게 되면 매번 새로운 리소스가 생성될 것이고, 그 말은 즉슨 역산을 수행하는 결과가 매번 달라진다는 것을 의미한다.
POST 메소드와 같이 멱등성을 보장하지 않는 동작은 한 번 수행될 때마다 어플리케이션의 상황을 전혀 다르게 변화시킬 수도 있다.
이러한 HTTP 메소드의 멱등성에 대한 지식은 에러에 대한 정보가 별로 없는 상태에서 디버깅을 진행할 때도 활용될 수 있기 때문에 여러모로 알고 있는 편이 좋다.
똑같이 통신 후에 발생하는 에러라고 해도 GET을 여러번 수행했을 때 발생하는 에러와
POST를 여러 번 수행했을때 발생하는 에러는 전혀 다른 컨텍스트를 가지고 있을 수 있다는 것이다.
PATCH는 왜 멱등성이 보장되지 않는 것일까
위 테이블에서 PATCH 메소든느 POST 메소드와 동일하게 멱등성이 보장되지 않는 메소드로 표기되어있다.
사실 정확하게 이야기하자면 PATCH 메소드는 구현 방법에 따라서 PUT 메소드처럼 멱등성이 보장될 수도 있고,
보장되지 않을 수도 있다고 할 수 있다.
PATCH 메소드는 PUT 메소드처럼 리소스를 대체하는 행위가 아니기 때문에 요청을 어떤 방식으로 사용하는지에 대한 제한이 딱히 없기 때문이다. RFC 스펙 상의 PATCH 메소드는 단지 리소스의 일부를 수정한다는 의미를 가질 뿐이다.
예를 들어, 앞서 설명했던 예시처럼 PATCH 메소드에 수정할 리소스의 일부분만 담아서 보내는 경우에는
당연히 멱등성이 보장된다.
// 기존 리소스
{
id:1,
name:'evan',
age:30,
}
PATCH users/1
{age:31}
// 새로운 리소스
{
id:1,
name:'evan',
age:31, //변경!
}
이 PATCH 요청은 명확하게 age라는 필드를 31로 수정하는 행위만을 의미하므로 아무리 여러 번 수행한다고 해도 늘 age는 31이라는 값을 가질 것이기 때문이다.
이것은 굉장히 일반적인 PATCH 메소드의 구현 방법이고, 실제로 PATCH 메소드를 사용해야 한다면 이렇게 구현한다.
그런데 왜 PATCH 메소드는 멱등성 보장이 안될 수도 있다는 것일까
... RFC-5789 문서에 있는 예시 요청의 바디에는 단지 description of changes라는 설명만 적혀있을 뿐, 어떤 제약 조건도 적혀 있지 않다. 즉 별다른 제약 없이 개발자 마음대로 API의 인터페이스를 정의해도 된다는 의미이기 때문에 이런 느낌으로 API를 구현하는 것도 가능하다는 것이다.
PATCH users/1
{
$increase:'age',
value:1,
}
이 요청의 $increase 필드의 값은 증가시키고 싶은 속성을 의미하고,
value 필드의 값은 그 속성을 얼마나 증가시킬 것인지를 나타내고 있다.
이 경우 API가 호출될 때마다 evan의 나이는 1씩 증가할 것이기 때문에 이 API는 멱등성을 보장하지 않는다...
PATCH 메소드를 어떻게 구현해야 하는지에 따른 제약이 존재하지 않으니 이런 방식으로 사용한다고 해서 표준을 어기는 것도 아니다.
즉, 자세한 스펙 상 구현 방법에 대한 제약이 없으니 API를 어떻게 구현하느냐에 따라서 PATCH 메소드는 멱등성을 보장할수도 있고 아닐 수도 있는 것이다.
API 와 Endpoint ? (둘 다 정확히 알고 있다면 안 봐도 되는 글) : 네이버 블로그 (naver.com)
Endpoint ? (feat. API) (tistory.com)
프론트엔드와 백엔드가 소통하는 엔드포인트, RESTful API | Evans Library (evan-moon.github.io)
'FrontEnd' 카테고리의 다른 글
[Frontend] OAuth 2.0을 구현하기 위해 알아야 할 것들 (1) | 2024.08.25 |
---|---|
[쿠키 2부]: 세션과 쿠키 (0) | 2024.08.06 |
[쿠키 1부] 쿠키란 무엇이며, 어떤 특징을 가지고 있을까? (1) | 2024.08.06 |