REST는 자원(Resource), 행위(Verb), 표현(Representaions)로 나뉩니다. 이전에 자원은 URI에서 식별이 가능하여야 하고 표현은 응답(Respone)의 본문(body)에서 확인할 수 있다고 하였습니다. 그리고 리소스와 분리가 되는 행위가 있다고 했죠. 그렇다면 행위는 무엇이고 어디서 찾을 수 있을까요?

출처: phpenthusiast.com, What is REST API?

행위는 요청(request)에서 찾을 수 있습니다. URI에 주어진 리소스에 대하여 어떤 행동을 할지 정의하는 역할을 합니다. 현재 가장 널리 사용되는 HTTP의 주요 메서드는 다섯 가지가 있습니다. GET, POST, PUT, PATCH, DELETE입니다. 그 외에도 HEAD, OPTIONS, CONNECT, TRACE 메서드가 존재합니다.

GET

서버에서 리소스를 조회할 것을 요청하는 메서드입니다. GET 요청을 받은 서버는 HTML 페이지나 특정 데이터를 응답으로 돌려줄 것입니다. 우리가 인터넷에서 특정한 정보를 조회하는 것은 GET 요청으로 이루어집니다. GET은 '안전한' 요청을 위한 메서드입니다. 리소스를 전혀 조작하지 않고 데이터를 읽는 것이 목적입니다. GET 요청은 서버 혹은 DB에 아무런 변화를 일으켜서는 안됩니다. 순수하게 데이터를 읽기만 하여야 합니다. 이러한 GET의 특성 때문에 GET 요청에 대한 응답은 캐시(cache) 서버가 담당하는 경우가 많습니다. 데이터 조회만 할 수 있으면 되기 때문이죠.

'안전한' 메서드?
HTTP 메서드가 서버의 상태를 바꾸지 않으면 그 메서드가 안전하다고 말합니다. GET, HEAD, OPTIONS와 같이 읽기 작업만 수행하는 메서드는 안전한 메서드 입니다. 다만 안전한 메서드가 읽기 전용의 의미를 내포하긴 하지만, 서버가 요청 정보와 통계 등을 기록함으로써 자신의 상태에 변경을 가하는 것도 가능합니다. 안전함의 중요점은 그 메서드를 호출해도 클라이언트가 서버의 상태 변화를 특정한 방향으로 직접 요청하는 것입니다. 그래서 요청으로 인한 간접적인 상태 변화가 안전함을 해치는 것은 아닙니다. 안전함이라는 용어 그대로 클라이언트의 요청이 서버에 해를 끼치지 않아 자유롭게 호출할 수 있는 것이 주요 쟁점입니다.

일반적으로 GET 요청은 요청 본문(Request body)를 가지고 있지 않습니다. 만약, GET 요청과 함께 서버가 알아야 될 정보가 있다면 대신 쿼리 파라미터를 URL에 포함시킬 수 있습니다. GET 요청에서 요청 본문을 추가할 수 없는 것은 아니지만 서버 측에서 거부당할 수 있어 GET 요청에는 본문이나 페이로드를 담지 않는 것이 바람직합니다.

 

쿼리 파라미터는 다음과 같이 사용합니다. 서버는 해당 URL이 param1을 키로, value1을 값으로 가지는 것으로 이해하고 해당 요청을 처리합니다.

GET /resource?param1=value1&param2=value2

 

GET 메서드는 Accept, Accept-Language, Accept-Encoding을 헤더에 포함시킬 수 있습니다. 해당 헤더들은 순서대로 클라이언트가 이해할 수 있는 콘텐츠 타입을 알리고, 클라이언트가 이해할 수 있는 언어를 알리고, 클라이언트가 이해할 수 있는 인코딩 방식을 알려주는 역할을 합니다. 또한 Range라는 헤더를 이용하면 리소스 중에서 원하는 부분만 요청할 수 있습니다.

POST

클라이언트가 데이터를 서버로 보내고 서버에서 해당 데이터를 처리하라고 요청하는 메서드입니다. 일반적으로 새로운 리소스를 생성하는 등, 사용자가 입력한 데이터를 서버 측에서 특정한 방식으로 처리를 하여 서버에 새로운 리소스를 만들것을 요청할 때 사용합니다. 요청 바디를 가지는 것이 일반적이며, 서버가 데이터를 처리하는 것을 돕기 위해 바디에 있는 데이터의 유형을 Content-Type이라는 헤더에 . Content-Type: application.json과 같은 형식으로 요청 본문의 유형을 명시해 놓을 수 있습니다.

 

POST 메서드는 서버의 상태를 변경할 수 있으며, 이로 인해 부수 효과(Side Effect)가 발생할 수도 있습니다. '안전한' 메서드가 아닌 것이죠. 그래서 GET과는 달리 캐시로 처리 할 수 없습니다. 어떤 POST 메서드가 클라이언트에서 외부로 향해 나가면 CDN이나 브라우저가 감지하면, 기존에 캐시해 놓았던 관련 리소스를 무효화 및 삭제합니다.

 

그리고 GET 메서드와는 달리 Request body가 존재하며 해당 데이터는 Header와 달리 기본적으로 암호화되어 전송됩니다. 그리고 Query Parameter로 데이터를 전송하지 않기 때문에 데이터의 길이(크기)제한이 없습니다. 만약 Query Parameter로 데이터를 전송한다면 서버에서 지정한 URL의 길이를 넘을 수 없기때문에 전송할 수 있는 데이터에 제한이 생깁니다.

PUT

요청 페이로드를 사용해 새로운 리소스를 생성하거나, 대상 리소스를 나타내는 데이터를 대체하는 메서드입니다. PUT과 POST의 차이는 멱등성으로, PUT은 멱등성을 가집니다. PUT은 한 번을 보내도, 여러 번을 연속으로 보내도 같은 효과를 보입니다. 즉, 부수 효과가 없습니다.

 

일반적으로 API에서는 어떤 데이터에 대한 작업 중에서도 생성이나 업데이트 명령을 수행하기 위해서 PUT 메서드를 사용합니다. POST와 마찬가지로 PUT 또한 요청 본문을 가질 수 있고 서버의 상태도 변화시킬 수 있습니다. 다만 PUT은 멱등성을 가지기에 완전히 동일한 여러 번의 PUT 요청은 한 번의 요청을 보낸 것과 동일한 상태를 유지해야 합니다.

멱등성
동일한 요청을 한 번 보내는 것과 여러 번 연속으로 보내는 것이 (서버에서) 같은 상태일 때, 해당 HTTP 메서드가 멱등성을 가졌다고 말합니다. 다시 말해, 멱등성 메서드에는 통계기록 등을 제외하면 어떠한 부수효과(side effect)도 존재해서는 안됩니다. 멱등성을 따질 땐 실제 서버의 백엔드 상태만 보면 되며, 각 요청에서 반환하는 응답 코드는 다를 수 있습니다. 다만, 서버는 메서드의 멱등성을 보장하지 않으며, 일부 애플리케이션은 잘못된 구현으로 멱등성 제약을 어길 수도 있습니다. 멱등성을 가진 메서드가 서버의 상태를 변화시킬 수 있기에 안전한 메서드는 멱등성을 가지지만 멱등성을 가진 메서드가 곧 안전한 메서드 인것은 아닙니다.

 

POST와의 차이점을 더 상세하게 알아보겠습니다. POST는 주로 '새로운 리소스 생성을 포함하는 요청'을 의미하며, PUT은 '기존의 문서 전체에서 클라이언트가 전달한 내용으로 교체하되, 기존의 문서가 없다면 새로운 문서를 생성'을 의미합니다. 따라서 POST는 새로운 리소스 생성을 포함할 수 있는 요청에서 사용하고, PUT은 리소스의 표현을 클라이언트가 제공한 데이터로 교체하는 것에 중점을 둔 요청에 사용을 합니다. 그리고 PUT은 리소스의 표현을 대체할 대이터를 요청 본문에 담아서 요청을 보내므로 같은 요청은 항상 같은 결과를 가집니다.

DELETE

단어 그대로 서버에 리소스를 삭제하라는 요청을 보내는 메서드입니다. 서버의 리소스를 변화시키므로 안전한 메서드가 아닙니다. 하지만 DELETE 요청에 의해 서버가 리소스를 삭제시키면 서버는 해당 리소스가 없는 상태가 된다는 점에서 항상 같은 결과를 가지는 메서드입니다. 물론 첫 번째 DELETE 요청이 200을 반환한다면, 그 이후에는 해당 리소스가 존재하지 않기에 아마 404를 반환할 것입니다. DELETE가 멱등성을 가진다는 것은, REST API에서 개발자는 DELETE 메서드를 사용해 "목록의 마지막 항목 제거"와 같은 기능을 구현해서는 안 된다는 것을 의미합니다.

Patch

클라이언트의 요청에 의해 리소스의 부분적인 수정을 할 때에 사용합니다. 이미 존재하는 리스소를 다시 덮어 씌우는 PUT과는 달리 리소스의 일부만을 수정하는 작업에 사용할 수 있습니다. PUT이나 POST와 마찬가지로 요청 본문을 가지고 있습니다. 요청 본문에 수정할 데이터가 포함되어 있죠. 또한 서버의 리소스를 변경하므로 안전한 요청이 아닙니다. 그리고 비슷한 작업을 하는 PUT과 달리 멱등성을 가지지도 않죠.

 

PATCH가 PUT과 달리 멱등성을 가지지 않는 이유는 PUT은 대체할 리소스가 포함되어 있어 완전한 교체가 이루어지는 반면 PATCH는 리소스의 현재 상태를 기반으로 리소스를 업데이트할 수 있기 때문입니다. 예를 들어 좋아요 수를 증가시키는 경우를 생각해보겠습니다. 좋아요 수는 요청을 받은 시점에서 1이 증가하게 됩니다. 서버의 상태를 변화시키므로 GET 요청이 아니죠. 좋아요 수를 특정한 수치로 변화시키는 것도 아니기 때문에 PUT도 아닙니다. 따라서 이 경우는 PATCH메서드를 사용하는 것이 바람직합니다. 그리고 이 요청을 보낸 횟수만큼 좋아요 수가 증가하므로 멱등성을 가지지 않습니다.

 

물론 PATCH를 PUT처럼 요청 본문에 포함된 데이터로 리스소를 교체하는 방식으로 사용할 수도 있습니다. 예를 들어 유저 닉네임을 요청 본문에 포함된 내용으로 변경하는 식으로 사용하는 것입니다. 리소스를 완전히 교체하지는 않으니 PATCH를 사용할 수도 있는 것이죠. 만약 닉네임 변경시 마다 모든 회원 정보를 기존의 정보로 서버에 전송한다면 PUT 메서드인것이죠.

 

위에서 언급한 GET, POST, PUT, DELETE 만큼 자주 쓰이지는 않지만 부분 업데이트가 일반적인 시나리오에서 PUT보다는 유리한 점이 있기에 해당 메서드를 채택하는 경우가 적지 않습니다.

그외의 메서드

자주 쓰이지는 않지만 이 외에도 몇 가지의 메서드가 더 존재합니다.
HEAD: 특정 리소스를 GET 메서드로 요청했을 때 돌아올 헤더를 요청합니다. 다시 말해, GET 메서드로 동일한 리소스를 요청했을 때의 본문을 설명합니다.
CONEECT: 요청한 리소스에 대해 양방향 연결을 시작하는 메소드로 터널을 열기 위해서 사용될 수 있습니다.
OPTIONS: 주어진 URL 또는 서버에 대해 허용된 통신 옵션을 알려달라는 요청입니다.
TRACE: 목적 리소스의 경로를 따라 메시지 loop-back 테스트를 합니다. 네트워크를 거치면서 메세지가 변경되는지 확인하는 용도로 사용하지만 실제로 사용하는 경우는 거의 없다고 합니다.

참고 자료

MDN, [HTTP 요청 메서드, 안전함 (HTTP 메서드), 멱등성]

+ Recent posts