API URL의 올바른 설계와 HTTP 메서드
📌 API URL의 올바른 설계
많은 개발자들이 API의 URL을 설계할 때 다음과 같이 기능에 대한 이름으로 URL을 설계한다.
회원 정보 관리 API 설계
- 회원 목록 조회 : /read-member-list
- 회원 조회 : /read-member-by-id
- 회원 등록 : /create-member
- 회원 수정 : /update-member
- 회원 삭제 : /delete-member
해당 API URL을 설계는 좋지 못한 설계이다. API URL 설계는 리소스와 행위를 구분하여 설계해야 한다.
리소스(Resource)란
리소스란 웹 상에서 고유한 식별자를 가지고 있는 모든 개별 항목을 나타낸다. HTTP 리소스는 클라이언트가 요청하고 서버가 응답하는 대상으로 일반적으로 웹 페이지, 이미지, 동영상, API 엔드포인트 등을 HTTP 리소스로 간주할 수 있다.
예를 들어 회원을 등록, 수정, 삭제, 조회한다면 회원이라는 개념 자체가 리소스이고 등록, 수정, 삭제, 조회가 행위인 것이다.
리소스를 식별하여 다음과 같이 API URL을 재설계할 수 있다.
- 회원 목록 조회 : /members
- 회원 조회 : /members/{id}
- 회원 등록 : /members/{id}
- 회원 수정 : /members/{id}
- 회원 삭제 : /members/{id}
참고 : 계층 구조상 상위를 컬렉션으로 보고 복수형 단어 사용을 권장한다.(member → members)
API URL을 재설계를 했지만 중복된 API URL로 등록, 수정, 삭제, 조회의 행위가 구분되지 않는다.
행위를 구분하는 역할을 바로 HTTP 메서드가 한다.
HTTP 메서드의 종류
- 주요 메서드
- GET : 리소스를 조회한다.
- POST : 요청 데이터를 처리한다. 주로 등록 기능에 사용한다.
- PUT : 리소스가 있으면 대체하고 해당 리소스가 없으면 생성한다.
- PATCH : 리소스를 부분적으로 변경한다.
- DELETE : 리소스를 삭제한다.
- 기타 메서드
- HEAD : GET과 동일하지만 메시지 부분을 제외하고, 상태 줄과 헤더만 반환
- OPTIONS : 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)
- CONNECT : 대상 자원으로 식별되는 서버에 대한 터널을 설정
- TRACE : 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행
📌 HTTP 메서드
GET
리소스를 조회 목적으로 데이터를 전달하는 방식
- 서버에 전달하는 파라미터를 다음과 같이 쿼리 파라미터와 쿼리 스트링을 통해서 전달한다.
GET api/patients?pageNo=1&pageSize=3 - 메시지 바디를 사용해서 데이터를 전달할 수 있지만, 지원하지 않는 곳이 많아 권장하지 않는다.
GET 예시
[요청]
GET /member/100 HTTP/1.1
[응답]
Content-Type: application/json
Content-Length: 34
{
"username": "young",
"age": 20
}
- 서버에서 클라이언트로 조회 결과를 응답메시지로 만들어 보낸다.
POST
클라이언트에서 서버에 요청 데이터를 처리 목적으로 데이터를 전달하는 방식
- 클라이언트가 메시지 바디를 통해 서버로 요청 데이터를 전달하고 서버는 요청 데이터를 처리하는 모든 기능을 수행한다.
- 주로 전달된 데이터로 신규 리소스를 등록, 프로세스 처리에 사용된다.
- URL을 리소스와 행위만으로 생성하는데 제한이 있다면 컨트롤 URL로 생성한다.
예) POST /orders/{orderId}/start-delivery (컨트롤 URL) - 다른 메서드로 처리하기 애매한 경우에 사용한다.
- 예) JSON으로 조회 데이터를 넘겨야 하는데, GET 메서드를 사용하기 어려운 경우
- 애매하면 POST를 사용하면 된다.
참고 : POST는 메시지를 외부에 보내는 모든 행위를 수행할 수 있지만 행위마다 약속된 HTTP 메서드가 더 많은 기능을 지원함으로 최대한 약속된 HTTP 메서드를 사용하도록 하고 정말 어쩔 수 없는 경우에만 POST로 사용하자.
POST 예시
[요청 데이터]
POST /members HTTP/1.1
Content-Type: application/json
{
"username": "young"
"age": 20
}
[응답 데이터]
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 34
Location: /members/100
{
"username": "young"
"age": 20
}
- 신규로 자원이 생산된 경로를 응답메시지로 보낸다.
PUT
리소스를 대체하는 방식
- 리소스가 없으면 리소스를 생성한다.
- 리소스가 있으면 리소스를 완전히 대체한다.
- 쉽게 이야기해서 덮어버린다.
- 클라이언트가 리소스를 식별한다. 즉, 클라이언트가 리소스 위치를 알고 URL을 지정한다.
참고 : POST와 PUT의 차이
POST는 클라이언트가 리소스를 알지 못한 상태로 서버에 요청하고 서버는 데이터를 등록할 때 리소스를 생성한다.
PUT은 클라이언트가 리소스를 URL에 명시하여 서버에 데이터 처리를 요청한다.
PUT - 클라이언트에서 보낸 리소스가 서버에 없을 경우
- 클라이언트에서 보낸 리소스가 서버에 존재하지 않기 때문에 서버에서 클라이언트에서 보낸 리소스를 생성한다.
PUT - 클라이언트에서 보낸 리소스가 서버에 있을 경우
- 클라이언트에서 보낸 리소스가 서버에 존재하기 때문에 서버의 리소스가 클라이언트에서 보낸 리소스로 완전히 대체된다.
PUT - 리소스를 완전히 대체한다는 것은
- PUT 메서드에서 가장 중요한 것은 리소스를 수정하는 것이 아니라 완전히 대체된다는 것이다.
- 클라이언트가 username 필드를 제외한 데이터를 서버로 보내면 서버에서는 username 필드가 있었다고 해도 username 필드가 삭제된다. 리소스를 완전히 대체하기 때문이다.
PATCH
리소스를 부분 변경하는 방식
- PUT과 다르게 리소스를 부분 변경하는 방식이기 때문에 데이터를 업데이트하는 데 사용한다.
- PATCH를 지원하지 않는 서버의 경우 POST를 사용하면 된다.
참고 : PATCH를 사용할 때 서버에 리소스가 없다면 리소스가 생성될까?
PATCH 서버가 해당 리소스를 생성하도록 구현되어 있다면 리소스가 생성될 수도 있다. 그러나 일반적으로 PATCH 메서드는 기존 리소스를 업데이트하는 용도로 사용되며, 리소스를 생성하는 데는 POST나 PUT 메서드가 보다 적합하다.
DELETE
리소스 제거한다.
- 클라이언트에서 보낸 리소스를 서버에서 제거한다.
📌 HTTP 메서드의 속성
- 안전성(Safe Methods)
- 멱등성(Idempotent Methods)
- 캐시가능(Cacheable Methods)
안전성(Safe)
- 클라이언트에서 서버를 호출해도 리소스를 변경하지 않는다.
- 안전은 해당 리소스만을 고려한다. 만약 계속 호출해서 로그가 수없이 쌓여 장애가 발생되는 경우는 고려하지 않는다.
멱등성(Idempotent)
- 한 번 호출하든 여러 번 호출하든 결과가 동일하다.
- 멱등은 외부 요인으로 중간에 리소스를 변경되어 조회되는 것 까지는 고려하지 않는다.
GET요청(기존의 데이터 조회) → PUT요청으로 기존의 데이터 수정 → GET 요청(변경된 데이터 조회)
이런 상황은 고려하지 않는다는 것이다. GET은 메서드 목적에 맞게 사용 시 동일한 결과가 조회되기 때문에 멱등성을 가진다. - 멱등성을 가진 HTTP 메서드(POST, PATCH 제외)
- GET : 리소스의 조회를 위해 사용되는 메서드로, 몇 번을 조회해도 같은 결과가 조회된다. 따라서 GET은 멱등성을 가진다.
- PUT : 리소스를 생성하거나 수정하기 위해 사용되는 메서드로, 동일한 리소스를 여러 번 PUT 요청하더라도 계속 동일하게 대체한다. 따라서 PUT도 멱등성을 가진다.
- DELETE : 리소스를 삭제하기 위해 사용되는 메서드로, 같은 리소스에 대해 여러 번 DELETE 요청을 보내도 삭제된 결과는 동일하다. 따라서 DELETE도 멱등성을 가진다.
- POST : 멱등이 아니다. 두 번 호출하면 같은 결제가 중복해서 발생할 수 있다.
- PATCH : 멱등으로 설계할 수 있지만 멱등이 아니게도 설계할 수 있다.
PUT과 다르게 PATCH가 멱등이 아닌 이유
멱등의 조건을 파악하는 것은 HTTP 메서드를 원래 목적에 맞게 올바르게 사용한 경우에 해당한다.
PATCH 메서드의 목적은 부분적인 변경을 수행하고, 변경 방식이 자율적이며, 존재하지 않는 필드에 대한 업데이트도 허용한다. PATCH를 부분 변경을 동일하게 수행하도록 PUT처럼 사용한다면 멱등성을 보장하는 것이지만, PATCH가 허용하는 메서드의 목적 중 멱등성이 보장하지 않는 다른 방법으로 사용한다면 멱등성을 보장되지 않는 것이다.
그렇기에 PATCH는 멱등성을 보장하지 않을 수도 있기 때문에 메서드의 원래 목적이 멱등성을 보장할 수도, 멱등성을 보장하지 않을 수도 있다.
그러나, PUT 메서드는 리소스의 전체를 대체하는 업데이트를 수행하는 것이 목적이므로, 동일한 PUT 요청을 여러 번 실행하더라도 항상 동일한 상태로 리소스가 변경된다. PUT 메서드는 멱등성을 보장하는 것이 일반적인 규칙이다.
반면 PUT의 경우 메서드의 목적 자체가 리소스를 대체하는 요청이기에, 정상적으로 사용한다면 멱등이며, PATCH처럼 사용을 했다면, 그 목적대로 사용하지 않았기 때문에 멱등이 되지 않을 수 있으나, 메서드의 목적에서 벗어나 잘못 사용한 것에 대해서는 고려하지 않는다.
예를 들어 GET 메서드는 데이터 조회를 목적으로 하고, 서버의 상태를 변경하지 하기 때문에 멱등성을 가진다. 파라미터를 전송하여 내부 데이터 처리에 의해 서버 상태가 변경된다면, 이는 GET 메서드의 목적과는 맞지 않는 사용 방식이므로 멱등성을 보장하지 않는 것이다.
각각의 HTTP 메서드는 그 목적과 의도에 따라 멱등성을 가지거나 가지지 않을 수 있다. 메서드를 올바르게 사용하고, 그 목적에 맞게 설계하면 멱등성을 보장할 수 있다.
캐시가능
- 웹 브라우저에 용량이 큰 이미지를 요청하면 다음에 똑같은 리소스를 요청할 필요가 없다. 만약, 같은 용량이 큰 이미지를 서버에서 내려받는다면 오래 걸릴 것이다. 그래서 리소스를 웹 브라우저의 로컬 저장소에 저장하고 다음 요청 시 서버에서 내려받는 것이 아니라 웹 브라우저가 사용하는 로컬 저장소에서 내려받는다. 이처럼 데이터나 값을 미리 복사해 놓는 임시 장소를 가리키는 것을 캐시라고 한다.
- GET, HEAD, POST, PATCH 메서드는 스펙상 캐시 가능하다.
- 하지만 실무에서는 GET, HEAD 정도만 캐시로 사용한다.
POST, PATCH는 본문 내용까지 캐시 키로 고려해야 하는데 구현이 쉽지 않다.
'Base > Web' 카테고리의 다른 글
세션 인증과 JWT를 이용한 토큰 인증 방식 (0) | 2023.12.14 |
---|
댓글