노트 정리/모든 개발자를 위한 HTTP 웹

[HTTP 웹] 4. HTTP 메서드

DuL2 2022. 10. 27. 19:10

HTTP 메서드 만들기

만약 회원에 대한 CRUD 요구사항에 따라 URI를 만들게 된다면 다음과 같이도 만들 수 있을 것이다.

 

회원 목록 조회 :  /read-member-list

 

이렇게 만드는 것이 좋을까? RESTFUL api에 대해서 알고 있는 나는 좋지 않다고 생각할 것이다. 왜 일까?

 

API URI 고민 해보기

URI를 정의함에 있어 가장 중요한 것은 리소스이다. 이전 글에서 URI를 설명함에 있어 리소스를 식별하는 통합된 방법이라고 정의했다. 우리는 리소스를 얻기 위해 접근하기 때문이다.

 

그렇다면 리소스의 의미는 뭘까? 단순히 회원을 등록하고 수정하고 조회하는 CRUD 과정이 리소스인 것이 아니라 회원이라는 개념 자체가 리소르를 의미한다.

 

더 나아가 리소스는 어떻게 식별하는 것이 좋을까? 이에 대한 답으로는 회원에 대한 CRUD는 모두 배제하고 회원이라는 리소스만 식별하도록 만든다. 즉 회원 리소스를 URI에 매핑하면 된다. 예를 들자면 다음과 같을 것이다.

- 회원 목록 조회 /members
- 회원 조회 /members/{id}
- 회원 등록 /members/{id}
- 회원 수종 /members/{id}
- 회원 삭제 /members/{id}
참고: 계층 구조상 상위를 컬렉션으로 보고 복수단어 사용 권장

 

회원이라는 리소스를 가지고 위와 같은 URI를 만들었을 때 또 다른 문제가 생기게 된다. CRUD의 기능이 같은 URI에 매핑되어있으므로 조회, 등록, 삭제 등의 구분이 힘들다.

 

이를 어떻게 해결할까?

 

 

리소스의 행위를 분리!!

URI의 가장 중요한 것은 리소스를 식별하는 일이다. 그렇기 때문에 리소스와 리소스의 행위를 분리하게 되고 이 행위는 HTTP Method를 사용하여 분리해준다.

HTTP 메서드 

 

HTTP 메서드는 클라이언트가 서버에게 기대하는 행동을 말한다. 주요 메서드는 다음과 같다.

 

  • GET: 리소스(Representation) 조회
  • POST: 요청 데이터 처리, 주로 등록에 사용
  • PUT: 리소스를 대체, 해당 리소스가 없으면 생성
  • PATCH: 리소스 부분 변경
  • DELETE: 리소스 삭제

이제 하나씩 뜯어보자.

 

참고: 기타 메소드
 - HEAD: GET과 동일하지만 메세지 부분을 제외하고, 상태 줄과 헤더만 반환
 - OPTIONS: 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)
 - CONNECT: 대상 자원으로 식별되는 서버에 대한 터널을 설정
 - TRACE: 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행

 

GET

GET은 클라이언트가 서버에 리소스를 조회 요청할 때 쓰이는 메서드이다. 서버에 전달하고 싶은 데이터는 query(쿼리 파라미터, 쿼리 스트링)를 통해서 전달하게 되고, 또한, 메세지 바디를 사용해서 데이터를 전달할 수 있다. 하지만 지원하지 않는 곳이 많아서 메시지 바디를 사용하는 것은 권장하지 않는다.

 

GET /search?q=이동욱&oq=이동욱 HTTP/1.1
Host: www.google.com

이전 글에서 예시로 들었던 예제이다. 위와 같은 데이터를 보내게 되면 다음과 같은 예시 데이터가 오게 될 것이다.

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: {길이값}
...

{
       이동욱에 관한 json 데이터
       ...
}

 

POST

 이와 달리 POST는 요청 데이터를 처리하는 메서드이다.  메시지 바디를 통해 서버로 요청 데이터를 전달하여 서버는 요청 데이터를 처리하는 것이다. 메시지 바디를 통해 들어온 데이터를 처리하는 모든 기능을 수행하게 된다. 들어온 데이터는 신규 리소스를 등록하거나 프로세스 처리에 사용된다.

 

회원 등록에 대한 예시를 들어보자

POST /members HTTP/1.1
Content-Type: application/json

{
    "username": "Donguk",
    "gender": "male"
}

클라이언트에서 POST 메서드로 서버의 특정 URI에 보내게 되면 서버는 해당 리소스에 대해서 비즈니스 로직을 수행하여 신규 리소스 생성을 해준다.

HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 34
Location: /members/100

{
    "username": "Donguk",
    "gender": "male"
}

위와 같이 응답이 오며 Location 헤더에 방금 요청으로 생성한 리소스를 받아볼 수 있는 URI를 함께 보내주게 된다.

 

이렇게 신규 리소스를 생성한다고 설명을 했지만 실제 POST의 기능은 다양하다. 스펙에 써있는 POST의 설명으로는 다음과 같다.

 

POST 메서드는 대상 리소스가 리소스의 고유한 의미 체계에 따라 요청에 포함된 표현을 처리하도록 요청합니다.
- 스펙 (구글 번역) -

 

이 말을 설명하자면 리소스 URI에 POST 요청이 오게되면 요청 데이터를 어떻게 처리할지는 리소스마다 따로 정해주어야 한다는 뜻이다. 새로운 데이터를 처리할 수도 있고, 서버가 식별하지 않은 새 리소스를 생성할 수도 있으며 기존 자원에 데이터를 추가할 수 있는 등 다양한 역할을 할 수 있다.

 

POST 정리

  1. 새 리소스 생성(등록)
    1. 서버가 아직 식별하지 않은 새 리소스 생성
  2. 요청 데이터 처리
    • 단순히 데이터를 생성하거나, 변경하는 것을 넘어서 프로세스를 처리해야 하는 경우
      • 예) 주문에서 결제완료 -> 배달시작 -> 배달완료처럼 단순히 값 변경을 넘어 프로세스의 상태가 변경되는 경우
    • POST의 결과로 새로운 리소스가 생성되지 않을 수도 있음
      • 예) POST /orders/{ordersid}/start-delivery (컨트롤 URI)
      • 모든 경우를 리소스만으로 설계할 수 없기 때문이 이 컨트롤 URI를 사용하여 실무를 처리함.
  3. 다른 메서드로 처리하기 애매한 경우
    • 예) JSON으로 조회 데이터를 넘겨야 하는데, GET 메서드를 사용하기 어려운 경우
    • 애매하면 POST 사용 가능

 

PUT

PUT 메서드는 리소스를 대체하는 메서드이다. 만약 리소스의 위치를 알고 있고 PUT을 사용하여 리소스에 대한 처리를 요청하면 리소스가 존재할 때는 대체하며 없다면 새로 생성해주는 메서드이다. PUT을 사용한 요청은 데이터를 덮어버리는 성질을 가지고 있다. 또한, 중요한 점은 클라이언트가 리소스를 식별(전체 경로를 알고 있음)하여 로직을 처리한다는 것이다.

 

PUT /members/100 HTTP/1.1
Content-Type: application/json

{
    "username": "수정한 이름",
    "gender": "male"
}

기존의 이름은 Donguk 이었지만 리소스의 정보를 대체한 예제이다. 

{
    "username": "수정한 이름",
    "gender": "male"
}

만약 100번에 리소스가 없었다면 리소스 존재 여부와 상관없이 PUT 메서드는 리소스를 대체하는 것이므로 새롭게 생성된다.

 

또한, 여기서 주의할 점은 대체하기 때문에 기존의 리소스는 무시되고 다음과 같이 모든 데이터가 수정되게 된다.

PUT /members/100 HTTP/1.1
Content-Type: application/json

{
    "age": "20"
}

기존의 이름과 성별 정보는 사라지고 PUT 메서드와 함께 보낸 age라는 데이터만이 남아 대체하게 된다. 

{
    "age": "20"
}

 

이렇게 PUT을 사용하면 모든 데이터가 강제 대체되기 때문에 부분적으로 변경해야할 때는 모든 정보를 같이 보내주어야한다. 이런 불편한 점의 요구사항을 통해 만들어진 메서드가 PATCH다.

 

PATCH

PATCH 메서드는 리소스를 부분 변경하는 메서드이다.

 

DELETE

DELETE 메서드는 리소스를 삭제하는 메서드이다.

 

 

HTTP 메서드의 속성

https://ko.wikipedia.org/wiki/HTTP

  • 안전(Safe Methods)
  • 멱등(Idempotent Methods)
  • 캐시가능(Cacheable Methods)

 

안전 - Safe

 안전이라는 속성은 리소스를 호출해도 변경하지 않고 그대로 있는 것을 말한다. 대표적 예시로는 GET이 있다. GET 메서드는 클라이언트가 서버에 요청할 때 어떠한 리소스의 변경을 원하지 않고 데이터만을 반환하기 때문이다. 설령 너무 많은 GET 메서드 요청으로 트래픽이 몰려 서버가 터지더라도 리소스 자체에는 변경이 없으므로 안전한 속성을 가지게 된다.

 

멱등 - Idempotent

 한 번 호출하든 여러 번 호출하든 호출 결과가 똑같으면 멱등에 위배되지 않는다. 대표적인 메소드 4개로 예시를 들어보자.

 

  • GET: 한 번, 두 번 조회의 결과가 항상 똑같다. - 멱등 O
  • POST: 같은 데이터로 계속 리소스를 대체할 것이므로 같은 요청이라면 결과가 항상 똑같다. - 멱등 O
  • DELETE: 데이터를 삭제한다. 여러 번 보내더라도 삭제하고 없으면 아무일도 일어나지 않으므로 결과가 똑같다. - 멱등 O
  • POST: 두 번 호출할 경우 두 번 모두 결과가 반영된다. ex) 게시글에 같은 글을 써도 글이 작성되어 도배가 된다. - 멱등 X

 

 그렇다면 멱등이라는 개념은 왜 사용될까? 같은 행위로 반복하더라도 결과에 지장을 주지 않는 경우는 에러가 나거나 서버 통신이 TIMEOUT이 났을 때 아주 유용하다. 만약 DELETE 요청을 서버에 보냈을 때 서버에 문제가 생겨 응답이 없다면 클라이언트는 아무런 부담이 없이 다시 DELETE 요청을 보낼 수 있다. 이유는 바로 멱등하기 때문이다. 멱등성 덕분에 문제시 똑같은 요청을 다시 할 수 있다. 이런 자동 복구 메커니즘에 사용할 수 있기에 사용되는 것이다.

 

 단, 내가 데이터를 요청하는 동안 다른 변수에 의한 리소스의 변경사항은 고려하지 않는다. 나의 요청에 의한 것만 판단하기 때문이다.

 

캐시가능 - Cacheable

 캐시 가능은 응답 결과 리소스를 캐시해서 사용해도 되는지에 대한 여부이다. GET, HEAD, POST, PATCH가 캐시가능이며 실제로는 GET, HEAD 정도만 캐시로 사용한다. POST와 PATCH는 본문 내용까지 캐시 키로 고려해야하므로 구현이 쉽지 않기 때문이다. GET, HEAD의 경우에는 URI 값만을 캐시 키로 사용하면 되므로 사용하기가 구현하기가 쉬운 편이다.