layout: post
title: "웹을 지탱하는 기술 스터디 - Chapter7"
date: 2022-04-04T00:00:00-00:00
author: sangyeop
categories: Sproutt-2nd
GET
: 리소스 취득POST
: 서브 리소스의 작성, 리소스 데이터의 추가, 그 밖의 처리PUT
: 리소스 갱신, 리소스 작성DELETE
: 리소스 삭제HEAD
: 리소스의 헤더(메타 데이터) 취득OPTIONS
: 리소스가 서포트하는 메서드의 취득TRACE
: 자기 앞으로 요청 메시지를 반환CONNECT
: 프록시 동작의 터널 접속으로 변경HTTP 메서드중 GET
,POST
,PUT
,DELETE
는 4개의 메서드로 CRUD를 충족시키고 있으므로 대표적인 메서드라고 할 수 있다.
POST
, PUT
GET
PUT
DELETE
GET
은 지정한 URI의 정보를 가져온다.
GET /list HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Content Type:application/json
[
{"uri":"http://example.com/list/item1"}
{"uri":"http://example.com/list/item2"}
{"uri":"http://example.com/list/item3"}
{"uri":"http://example.com/list/item4"}
]
요청에 대해서 서버는 지정된 URI에 대응하는 데이터를 응답으로 반환한다.
어떤 리소스에 대해 서브 리소스를 작성한다.
POST /list HTTP/1.1
Host: example.com
Content-Type:text/plain:charset=utf-8
안녕하세요!
HTTP/1.1 201 Created
Content-Type:text/plain:charset=utf-8
Location:http://example.com/list/item5
안녕하세요!
POST
의 바디에는 새로 작성할 리소스 내용이 들어가 있고, 응답에서 201
스테이터스 코드는 새로운 리소스가 생성되었음을 보여준다.
그리고 Location 헤더에는 새롭게 생성된 리소스의 URI가 들어간다. 여기서는 item5가 새로운 서브 리소스로 생성된 것이다.
기존 리소스에 데이터를 추가하는 기능이 있다. 먼저 로그 리소스를 GET
하는 예시를 보자
GET /log HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Content-Type:text/csv:charset=utf-8
2010-10-10T10:10:00Z, GET /list, 200
2010-10-10T10:11:00Z, POST /list, 201
2010-10-10T10:20:00Z, GET /list, 200
이 리소스에 새로운 로그를 추가하기 위해서 POST
를 사용한다.
POST /log HTTP/1.1
Host:example.com
2010-10-10TO10:13:00Z, GET /log,200
HTTP/1.1 200 OK
여기서는 201
이 아니라 200
이 반환되었는데, 신규 리소스 작성이 아닌 데이터가 추가되었기 때문이다.
다른 메서드로는 대응할 수 없는 처리의 실행을 할 수 있다. 다음은 검색 결과를 표현하는 URI의 예이다.
http://example.com/search?q={키워드}
일반적으로는 이 URI를 GET
하여 검색을 실행하지만, 매우 긴 URI의 경우에는 구현상에 2,000자 등의 상한선이 존재한다. 이럴 경우네는 다음과 같이 POST
를 사용한다.
POST /search HTTP/1.1
Content-Type:application/x-www-form-urlencoded
q=very+long+keyword+foo+bar+..............
GET
에서 URI에 포함 시켰던 키워드를 POST
에서는 요청메시지와 바디에 포함시켰다.
GET /list/item5 HTTP/1.1
Host:example.com
HTTP/1.1 202 OK
Content-Type:text/plain:charset=utf-8
안녕하세요!
이 리소스를 PUT
을 이용해 ''좋은 밤이네요!'로 갱신해보자
PUT /list/item5 HTTP/1.1
Host:example.com
Content-Type:text/plain:charset=utf-8
좋은 밤이네요!
HTTP/1.1 200 OK
Content-Type:text/plain:charset=urf-8
좋은 밤이네요!
예를 들어서 http://example.com/newitem이 존재하지 않는다고 가정하자
PUT /newitem HTTP/1.1
Host:example.com
Content-Type:text/plain:charset=utf-8
새로운 리소스 /newitem의 내용
HTTP/1.1 201 Created
Content-Type:text/plain:charset=utf-8
새로운 리소스 /newitem의 내용
존재하지 않는 URI에 대한 요청을 했기 때문에, 리소스를 새로 작성한다고 해석하고 201 Created
를 반환한다.
POST
의 경우에는 새롭게작성한 URI가 Location 헤더로 반환되었지만, PUT
의 경우는 클라이언트가 미리 리소스의 URI를 알고 있기 때문에 Location 헤더를 반환할 필요가 없다.
/newitem
이 이미 존재하는 경우엔 앞서 말했던 리소스 갱신 처리가 일어난다.
POST
로 리소스를 작성할 경우, 클라이언트는 리소스의 URI를 지정할 수 없다. URI의 결정권은 서버측에 있기 때문이다.
PUT
으로 리소스를 작성할 경우, 리소스의 URI는 클라이언트가 결정한다.
클라이언트가 리소스 URI를 결정할 수 있다는 것은 클라이언트를 만드는 프로그래머가 서버의 내부구현을 숙지하고 있어야 한다. 때문에 PUT
이 서버와의 결합이 밀접하며, 특별한 이유가 없는한 리소스의 작성은 POST
로 수행하여 URI를 서버 측에서 결정하는 설계가 바람직하다.
말 그대로 리소스를 삭제하는 메서드이다.
DELETE /list/item2 HTTP/1.1
Host:example.com
HTTP/1.1 200 OK
DELETE
의 응답은 바디를 가지지 않는다. 때문에 바디가 없다는 의미의 204 No Content
가 사용되기도 한다.
HEAD
는 GET
과 유사하다. 리소스를 취득하는 GET
과는 다르게 HEAD
는 리소스의 헤더만을 취득하는 메서드이다.
HEAD /list/item1 HTTP/1.1
Host:example.com
HTTP/1.1 200 OK
Content-Type:text/plain:charset=utf-8
HEAD
에는 응답에는 바디가 포함되지 않는다. 이 성질을 이용하여 네트워크의 대역을 절약하면서 리소스의 크기를 조사하거나, 리소스의 갱신인자를 구할 수 있다.
마지막 메서드는 OPTIONS
입니다. OPTIONS
는 그 리소스가 지원하고 있는 메서드의 목록을 반환한다.
OPTIONS /list HTTP/1.1
Host:example.com
HTTP/1.1 200 OK
Allow:GET,HEAD,POST
OPTIONS /list/item1 HTTP/1.1
Host:example.com
HTTP/1.1 200 OK
Allow:GET,HEAD,PUT,DELETE
응답에 포함되는 Allow 헤더는 그 리소스가 허용하는 메서드의 목록이다.
현실적으로 가장 많이 사용되는 메서드는 GET
과 POST
2가지이다. 이것은 HTML의 폼에서 지정할 수 있는 메서드가 GET
과 POST
뿐이라는 점에서 기인한다.
이런 제한은 Ajax의 발전과 함께 점차 해소되고 있지만, 여전히 GET
과 POST
이외의 접근을 제한하는 경우도 있다. 이러한 상황에서 PUT
이나 DELETE
를 전달하는 방법이 2가지 있다.
첫 번째 방법은 _method
파라미터를 이용하는 방법이다. 폼의 hidden
파라미터에 _method
파라미터를 준비하고 그 곳에 보내고자 하는 요청 메서드 이름을 넣는다.
<form target="/List/item1" action="POST">
<input type="hidden" id="_method" value="PUT">
<textarea id="body">...</textarea>
</form>
이 폼을 송신하면 PUT
요청이 보내진다. 웹 애플리케이션 프레임워크 등의 서버 측 구현에서는 _method
파라미터를 보고 이 요청 자체를 PUT
으로써 다룬다.
_method
로는 POST
내용이 XML
등 application/x-www-form-urlencoded
이외의 경우에는 이용할 수 없다. 이 경우 활용할 수 있는 것이 이 방법이다
POST /list/item1 HTTP/1.1
Host:example.com
Content-Type:application/xml:charset=utf-8
X-HTTP-Method-Override:PUT
<body>...<body>
웹 애플리케이션 프레임워크 등의 서버 측 구현은 X-HTTP-Method-Override
헤더를 보고 이 요청 자체를 PUT
으로 취급한다.
조건부 요청이란? : HTTP 메서드와 갱신일자 등으로 헤더를 구성하면 메서드 실행 여부를 리소스 갱신일자를 조건으로 서버가 선택할 수 있는데 이를 조건부 요청이라고 부른다.
예시
If-Modifed-Since
헤더를 사용한다.If-Unmodified-Since
헤더를 조합하면 이 시간 이후로 갱신되어 있지 않으면 리소스를 갱신한다는 의미가 된다.멱등성이란? : 어떤 조작을 몇 번을 반복해도 결과가 동일한 것
안전성이란? : 조작 대상의 리소스의 상태를 변화시키지 않는 것
HTTP 메서드의 성질
리소스 상태에 변화를 부여하는 것 = 부작용
PUT은 여러번을 송신해도 결과는 한 번 송신했을 때와 같으므로 멱등하다
처음 리소스가 삭제되었을때 200 OK
가 반환되고, 이미 리소스를 삭제했기 때문에 이후에는 존재하지 않는다는 의미의 404 Not Found
가 돌아온다. 이는 이미 리소스가 삭제 되어 있다는 같은 결과를 의미하므로, 여러번 반복해서 송신해도 결과가 변하지 않는 멱등성에 해당한다.
GET
과 HEAD
는 멱등하다. 또한 PUT
과 DELETE
는 리소스 상태를 변화시키지만 GET
과 HEAD
는 리소스 상태를 변화시키지 않기 때문에, 멱등하면서도 안전하다.
요청의 결과로 무엇이 일어날지 모르기 때문에, 클라이언트는 POST
를 여러번 보내는 것에 신중해야한다.
이전에 멱등하고 안전한 메서드들을 알아보았는데, 이를 잘못 사용하면 멱등하지도 안전하지도 않게 될 가능성이 있다.
GET /resources/1/delete HTTP/1.1
Host:example.com
http://example.com/resources/1
을 삭제하기 위해서 http://example.com/resources/1/delete
를 GET
하고 있다. GET
으로 리소스를 변경하거나 리소스르 삭제하는 것은 잘못된 사용방법이다.
또한 GET
해야하는 URI에 동사가 포함된 것도 모순된다.
POST
는 만능이기는 하지만 GET
,PUT
, DELETE
로 가능한 기능을 POST
로 구현하려는 것은 위험 신호이다. 왜냐하면 GET
,PUT
, DELETE
가 가진 멱등성과 안전성을 이용할 수 없게 되기 때문이다.
토마토 가격을 나타내는 리소스 http://example.com/tomato
GET /tomato HTTP/1.1
Host:example.com
HTTP/1.1 200 OK
Content-Type:text/plain:charset=utf-8
100
현재 토마토 가격은 100원이다. 이를 PUT
으로 갱신해보자
PUT /tomato HTTP/1.1
Host:example.com
Content-Type:text/plain:charset=utf-8
+50
HTTP/1.1 200 OK
Content-Type:text/plain:charset=utf-8
150
위와 같이 리소스 내용을 상대적인 차분을 전송하면 멱등이 아니게된다. 차분이 아닌 그 리소스가 변경될 값으로 표현해야 한다.
http://example.com/latest
라는 alias 리소스가 있을 경우, 이 리소스가 삭제될 경우에는 멱등성이 유지가 된다. 그러나 alias 리소스가 아닌, /latest
라는 URI가 의미상 가리키는 실제 최신 버전의 리소스(/1.1, /1.2 등)을 삭제한다면 DELETE
가 멱등이 아니게 된다.
이러한 특수한 리소스의 경우에는 특별한 이유가 없는 한, 갱신과 삭제 등의 조작을 할 수 없도록 설계한다.
HTTP에 아주 적은 메서드들 만을 정의하고 있는 것이 REST의 통일 인터페이스 제약이라고 할 수 있다. 메서드를 한정하고 고정시켰기 떄문에 프로토콜이 심플하게 유지되었고 웹은 성공했다고 볼 수 있다.
각 메서드들이 가진 안정성, 멱등성, 만능 메서드들은 각각의 메서드에 맞는 성질과 확장성을 갖춘 뛰어난 프로토콜이다.