[새싹] 웹을 지탱하는 기술 Chapter07

채상엽·2022년 5월 10일
0

Sproutt 2nd - Spring Study

목록 보기
19/32

layout: post
title: "웹을 지탱하는 기술 스터디 - Chapter7"
date: 2022-04-04T00:00:00-00:00
author: sangyeop
categories: Sproutt-2nd


새싹 개발 서적 스터디 - 웹을 지탱하는 기술 Chapter07

Chapter 07. HTTP 메서드

8개밖에 없는 메서드

  • GET : 리소스 취득
  • POST : 서브 리소스의 작성, 리소스 데이터의 추가, 그 밖의 처리
  • PUT : 리소스 갱신, 리소스 작성
  • DELETE : 리소스 삭제
  • HEAD : 리소스의 헤더(메타 데이터) 취득
  • OPTIONS : 리소스가 서포트하는 메서드의 취득
  • TRACE : 자기 앞으로 요청 메시지를 반환
  • CONNECT : 프록시 동작의 터널 접속으로 변경

HTTP 메서드와 CRUD

HTTP 메서드중 GET,POST,PUT,DELETE는 4개의 메서드로 CRUD를 충족시키고 있으므로 대표적인 메서드라고 할 수 있다.

  • Create
    • 작성
    • POST, PUT
  • Read
    • 읽기
    • GET
  • `Update
    • 갱신
    • PUT
  • Delete
    • 삭제
    • DELETE

GET - 리소스의 취득

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 - 리소스의 작성, 추가

서버 리소스의 작성

어떤 리소스에 대해 서브 리소스를 작성한다.

  • 요청
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에서는 요청메시지와 바디에 포함시켰다.

PUT - 리소스의 갱신, 작성

리소스의 갱신

  • 요청
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와 PUT의 사용 구분

POST로 리소스를 작성할 경우, 클라이언트는 리소스의 URI를 지정할 수 없다. URI의 결정권은 서버측에 있기 때문이다.
PUT으로 리소스를 작성할 경우, 리소스의 URI는 클라이언트가 결정한다.

클라이언트가 리소스 URI를 결정할 수 있다는 것은 클라이언트를 만드는 프로그래머가 서버의 내부구현을 숙지하고 있어야 한다. 때문에 PUT이 서버와의 결합이 밀접하며, 특별한 이유가 없는한 리소스의 작성은 POST로 수행하여 URI를 서버 측에서 결정하는 설계가 바람직하다.

DELETE - 리소스의 삭제

말 그대로 리소스를 삭제하는 메서드이다.

  • 요청
DELETE /list/item2 HTTP/1.1
Host:example.com
  • 응답
HTTP/1.1 200 OK

DELETE의 응답은 바디를 가지지 않는다. 때문에 바디가 없다는 의미의 204 No Content가 사용되기도 한다.

HEAD - 리소스의 헤더 취득

HEADGET과 유사하다. 리소스를 취득하는 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는 그 리소스가 지원하고 있는 메서드의 목록을 반환한다.

  • 요청
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 헤더는 그 리소스가 허용하는 메서드의 목록이다.

POST를 PUT/DELETE 대신 사용하는 방법

현실적으로 가장 많이 사용되는 메서드는 GETPOST 2가지이다. 이것은 HTML의 폼에서 지정할 수 있는 메서드가 GETPOST뿐이라는 점에서 기인한다.

이런 제한은 Ajax의 발전과 함께 점차 해소되고 있지만, 여전히 GETPOST 이외의 접근을 제한하는 경우도 있다. 이러한 상황에서 PUT이나 DELETE를 전달하는 방법이 2가지 있다.

_method 파라미터

첫 번째 방법은 _method 파라미터를 이용하는 방법이다. 폼의 hidden 파라미터에 _method 파라미터를 준비하고 그 곳에 보내고자 하는 요청 메서드 이름을 넣는다.

<form target="/List/item1" action="POST">
  <input type="hidden" id="_method" value="PUT">
  <textarea id="body">...</textarea>
</form>

이 폼을 송신하면 PUT 요청이 보내진다. 웹 애플리케이션 프레임워크 등의 서버 측 구현에서는 _method 파라미터를 보고 이 요청 자체를 PUT으로써 다룬다.

X-HTTP-Method-Override

_method로는 POST 내용이 XMLapplication/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 메서드와 갱신일자 등으로 헤더를 구성하면 메서드 실행 여부를 리소스 갱신일자를 조건으로 서버가 선택할 수 있는데 이를 조건부 요청이라고 부른다.

예시

  • GET에 리소스의 갱신일자를 조건으로 넣기 위해서는 If-Modifed-Since 헤더를 사용한다.
  • PUT과 If-Unmodified-Since 헤더를 조합하면 이 시간 이후로 갱신되어 있지 않으면 리소스를 갱신한다는 의미가 된다.

멱등성과 안전성

멱등성이란? : 어떤 조작을 몇 번을 반복해도 결과가 동일한 것

안전성이란? : 조작 대상의 리소스의 상태를 변화시키지 않는 것

HTTP 메서드의 성질

  • GET, HEAD : 멱등이고 안전하다
  • PUT, DELETE : 멱등이지만 안전하지 않다
  • POST : 멱등이지도 안전하지도 않다

리소스 상태에 변화를 부여하는 것 = 부작용

PUT은 멱등

PUT은 여러번을 송신해도 결과는 한 번 송신했을 때와 같으므로 멱등하다

DELETE도 멱등

처음 리소스가 삭제되었을때 200 OK가 반환되고, 이미 리소스를 삭제했기 때문에 이후에는 존재하지 않는다는 의미의 404 Not Found가 돌아온다. 이는 이미 리소스가 삭제 되어 있다는 같은 결과를 의미하므로, 여러번 반복해서 송신해도 결과가 변하지 않는 멱등성에 해당한다.

GET과 HEAD는 멱등 + 안전

GETHEAD는 멱등하다. 또한 PUTDELETE는 리소스 상태를 변화시키지만 GETHEAD는 리소스 상태를 변화시키지 않기 때문에, 멱등하면서도 안전하다.

안전하지도 멱등도 아닌 POST

요청의 결과로 무엇이 일어날지 모르기 때문에, 클라이언트는 POST를 여러번 보내는 것에 신중해야한다.

메서드의 오용

이전에 멱등하고 안전한 메서드들을 알아보았는데, 이를 잘못 사용하면 멱등하지도 안전하지도 않게 될 가능성이 있다.

GET이 안전하지 않게 되는 예

GET /resources/1/delete HTTP/1.1
Host:example.com

http://example.com/resources/1을 삭제하기 위해서 http://example.com/resources/1/deleteGET하고 있다. GET으로 리소스를 변경하거나 리소스르 삭제하는 것은 잘못된 사용방법이다.

또한 GET 해야하는 URI에 동사가 포함된 것도 모순된다.

다른 메서드로 할 수 있는데 POST를 오용한 예

POST는 만능이기는 하지만 GET ,PUT, DELETE로 가능한 기능을 POST로 구현하려는 것은 위험 신호이다. 왜냐하면 GET,PUT, DELETE가 가진 멱등성과 안전성을 이용할 수 없게 되기 때문이다.

PUT이 멱등이 아니게 되는 예

토마토 가격을 나타내는 리소스 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 

위와 같이 리소스 내용을 상대적인 차분을 전송하면 멱등이 아니게된다. 차분이 아닌 그 리소스가 변경될 값으로 표현해야 한다.

DELETE가 멱등이 아니게 되는 예

http://example.com/latest라는 alias 리소스가 있을 경우, 이 리소스가 삭제될 경우에는 멱등성이 유지가 된다. 그러나 alias 리소스가 아닌, /latest라는 URI가 의미상 가리키는 실제 최신 버전의 리소스(/1.1, /1.2 등)을 삭제한다면 DELETE가 멱등이 아니게 된다.

이러한 특수한 리소스의 경우에는 특별한 이유가 없는 한, 갱신과 삭제 등의 조작을 할 수 없도록 설계한다.

웹의 성공 이유는 HTTP 메서드에 있다

HTTP에 아주 적은 메서드들 만을 정의하고 있는 것이 REST의 통일 인터페이스 제약이라고 할 수 있다. 메서드를 한정하고 고정시켰기 떄문에 프로토콜이 심플하게 유지되었고 웹은 성공했다고 볼 수 있다.

각 메서드들이 가진 안정성, 멱등성, 만능 메서드들은 각각의 메서드에 맞는 성질과 확장성을 갖춘 뛰어난 프로토콜이다.

profile
프로게이머 연습생 출신 주니어 서버 개발자 채상엽입니다.

0개의 댓글