HTTP Method의 멱등성
HTTP 메서드의 멱등성(Idempotent)에 대해 알아보자
velog.io
0. 서론
RESTFul한 API를 설계하기 위해 포스팅을 한 적이 있는데
https://seungnong.tistory.com/entry/RESTFul%ED%95%9C-API-%EC%84%A4%EA%B3%84
사실 그냥 URI 규칙만 정리한거지, 실제 적용은 어려운 포스팅인 것 같다.
(직접 해보니까 할수록 질문만 생긴다)
앞 포스팅에서 멱등성이 나왔는데,
이번엔 HTTP Method들의 멱등성에 대해 더 알아보ja ja ja ja
1. 멱등성
멱등성(Idempotent)는 수학이나 전산학에서 연산을 여러 번 적용해도 결과가 달라지지 않는 성질을 의미한다.
f( f(x) ) = f(x) 와 같이...
HTTP Method에 적용하면,
동일 요청을 한 번 보내는 것 과 여러 번 연속으로 보내는 것이 동일한 효과를 가지고, 서버의 상태도 동일하게 남을 때 HTTP Method가 멱등성을 가진다고 할 수 있다.
흔히 사용하는 HTTP Method인 GET, POST, PUT, PATCH, DELETE 중, HTTP 스펙에 명시된 것에 의하면 GET, PUT, DELETE는 멱등성을 가지고, POST, PATCH는 멱등성을 가지지 않도록 구현해야 한다고 한다.
조금 더 자세히 aRA보자~
1-1. GET
GET 메서드는 멱등성을 가져야 한다.
이건 직관적이긴하다.
GET /postings/1
1. DB에서 pk가 1인 게시글을 찾는다.
2. 응답으로 해당 게시글 데이터를 반환한다.
애초에 조회하기 위해 GET 요청을 보내는데, 조회함으로 인해 서버의 상태가 변경된다? dat makes no sense~
하지만?
GET /postings/1
1. DB에서 pk가 1인 게시글을 찾는다.
2. 해당 게시글의 조회수 컬럼의 값을 1 증가시킨다.
3. 응답으로 해당 게시글 데이터를 반환한다.
위와 같이 조회수를 기록하고 싶다면, 조회를 함으로 조회수 값이 변경되버린다.
즉, 요청할 때마다 서버의 상태가 매번 바뀌게 되고, 멱등성을 갖지 않게 되기 때문에, HTTP 스펙에 부합하지 않은 구현이라고 볼 수 있는 것이다.
그래도 하고싶다면?
만약 이런 로직이 필요하다면, 조회수 컬럼의 값을 증가시키는 요청을 따로 분리하는 것이 스펙상 올바르다.
아래 PATCH 메서드에 대한 설명에 적용할 만한 방법이 있는 듯 하다??
1-2. POST
POST 메서드는 멱등성을 가지지 않는다.
POST는 서버에 데이터를 전송하는 메서드이다.
CRUD 관점에서 일반적으로 새로운 자원을 생성하는 역할을 하기 때문에, 서버의 상태를 변경한다.
때문에 멱등성을 가지지 않는다~
1-3. PUT
PUT 메서드는 멱등성을 가진다!
PUT은
1) 새로운 리소스를 생성하거나,
2) 대상 리소스를 나타내는 데이터를 (전체를) 덮어쓴다.
POST와의 차이점은 멱등성의 유무인데, POST는 매번 새로운 자원을 만드는 반면, PUT은 해당 자원이 없으면 생성하고, 있으면 덮어쓰는 역할을 한다. 때문에 요청을 한 번 하든 여러번 하든 결국 서버의 상태는 동일하게 나타난다.
따라서 PUT은 멱등성을 가지는 HTTP Method이다.
POST는 스펙 상 응답 코드를 200, 201, 204로 보낼 수 있다. 그래도 멱등성이 있다고 한다. 왜?
어쨋든 서버의 상태는 동일하니까.
즉, 새로 생성해서 201(Created)를 반환하고,
다시 요청해서 덮어쓴 다음 200(OK) 혹은 204(No Content)를 반환하더라도, 서버의 상태는 동일하니까.
1-4. PATCH
PATCH 메서드는 멱등성을 가지지 않아도 된다.
가지면 가지는거고 안가지면 안가지는거지 먼소릴까?
"A PATCH is not necessarily idempotent, although it can be." - MDN web docs
이렇게 나와있다고 앞 포스팅에 언급한 바 있다.
it can be 라는건 PUT과 같이 쓰려면 쓸 수는 있다고 해석할 수 있다. (수정을 한다라는 관점에서)
아무튼, PATCH는 PUT과 달리 전체가 아닌 일부를 수정하는 의미로 쓰인다.
여기서 이미 다르지만, 또 멱등성을 지키지 않아도 되기 때문에 아래와 같은 요청도 가능해진다.
posting 테이블의 기본키가 3인 레코드에서 age 값을 1 증가하라
이것도 업데이트는 부분 업데이트인데 멱등성은 갖지 않는다고 볼 수 있겠지??
이게 가능한 이유는, PATCH는 HTTP 스펙상 구현 방법에 제한이 없기 때문이다. 때문에 꼭 요청 body로 덮어쓸 데이터가 있을 필요는 없다. 위 예시처럼 동작을 지정해줄 수도 있는 것이다.
필자같은 경우 PATCH 메서드를 사용하고 멱등성을 지키지 않는 중인데, 아래와 같은 요청을 보낼 수 있게 했다.
posting의 title을 {요청값}으로 변경하고, updated_datetime을 현재 서버 시간으로 변경하라
update 시간을 DB에 저장하려는 설계 때문에 멱등성을 갖지 않게 되는 것이다. 갠춘?
1-5. DELETE
DELETE 메서드도 멱등성을 갖는다.
GET과 같이 직관적이다.
DELETE는 지정 리소스를 삭제하는 메서드이기 때문에,
처음에 이 리소스가 존재한다면 삭제하고 200 / 202(Accepted) / 204를 반환할 것이고,
다시 동일한 요청을 한다면 실패하겠지만 서버 상태는 동일하기 때문이다.
하지 만? 아래와 같은 예시가 있을 수도 있다.
DELETE /postings/last
딱봐도 멱등성이 깨진다.
때문에 이런 경우는 스펙을 지키기 위해서 POST 메서드를 쓰는 게 맞기는 하다...하지만?
그러면 RESTFul하지 않고, (행위를 정확히 나타내지 않음) POST의 바디엔 뭘 보낼건데? 뭐 이런저런 이유로 망설이게 되는데... 그래도 팀원이랑 상의해서 DELET 그대로 쓰는 것도 나쁘진 않아보인다... 이 상황이 실제로 오면 또 협의해봐야겠다?!
2. 정리
근데 아무래도 HTTP 스펙의 규약일 뿐이지 멱등성을 이대로 꼭 지켜야만 하는 건 아니다. 다만 API 동작 유추가 어려워지고(RESTFul하지 않아지고), 원치 않는 동작을 야기할 수도 있기 때문에, 가능한 규약을 지키는 편이 좋다아
다음 포스팅엔 각 HTTP 스펙에 대해 좀 더 정리해보자.
'코딩 > WEB 개발' 카테고리의 다른 글
Spring - @PathVariable 에서 마지막 "~.com"이 짤리는 현상에 대해 (0) | 2023.07.19 |
---|---|
HTTP Method의 스펙 (0) | 2023.06.28 |
HTTP Method, PUT과 PATCH의 차이점 (0) | 2023.06.28 |
동시 편집에 대해... (0) | 2023.06.19 |
RESTFul한 API 설계 (0) | 2023.06.14 |