HTTP 메서드 — 안전·멱등이라는 약속
GET·POST·PUT·DELETE·HEAD 를 「무엇을 하고 싶은가」로 구분하고, 안전(서버의 상태를 바꾸지 않음)과 멱등(몇 번을 해도 같음)이라는 약속으로 경계를 그어, 재시도 판단에 쓸 수 있게 한다.
「일단 전부 POST」로 만들고 있지 않은가
폼도 가져오기도 삭제도, 돌아가니까 전부 POST 로 만들어 버린다. 혹은 반대로, 전송 버튼을 다시 클릭해서 주문이 이중으로 등록되어 버렸는데 원인을 모른다 — 메서드의 구분은, 돌아가기만 하면 애매해도 통과해 버리는 만큼, 사고가 난 뒤에야 효과가 드러나는 지식입니다.
앞 장에서 읽은 대로, 리퀘스트 행의 맨 앞에 있는 메서드는 왕복 서신의 동사입니다. HTTP 에는 메서드가 여럿 있지만, 우선 이 5 가지를 구분해 쓸 수 있으면 충분합니다.
예를 들어 PUT /users/42/profile 은 「42 번 사용자의 프로필을 이 내용으로 하라」, POST /orders 는 「이 주문 내용을 처리해 새 주문을 만들라」. 받는 곳이 정해져 있는 치환은 PUT, 새로 생겨나는 것이나 처리 의뢰는 POST — 이것이 기본 구분입니다.
연습 2-1 — 5 가지 동사를 구분해 쓴다
「무엇을 하고 싶은가」에서 적절한 메서드를 고르는 연습입니다.
Q1. 온라인 숍 https://shop.example.com 에서, 주문 폼에 입력한 내용을 전송해 새 주문을 만들 때, 가장 적절한 메서드는 어느 것입니까.
「새로운 것을 만들어 달라·이 내용을 처리해 달라」는 부탁은 POST 입니다. GET 은 가져오기 전용으로 서버의 상태를 바꾸지 않는다는 약속이 있어, 주문 데이터 같은 내용을 바디로 보내는 용도에는 쓰지 않습니다. HEAD 는 본문 없는 확인, DELETE 는 삭제 의뢰로, 어느 쪽도 주문 작성이 될 수 없습니다.
Q2. 거대한 동영상 파일에 대해, 다운로드는 하지 않고 크기나 갱신 일시만 알고 싶을 때, 가장 적절한 메서드는 어느 것입니까.
HEAD 는 「GET 과 같은 답을, 바디 없이」라는 부탁입니다. 서버는 헤더(Content-Length 나 Last-Modified 등)만 돌려주므로, 거대한 본문을 나르지 않고 메타 정보를 확인할 수 있습니다. GET 으로도 정보는 얻을 수 있지만 본문까지 전송되어 버려, 목적에 비해 낭비가 너무 큽니다.
Q3. PUT 과 POST 의 차이에 대한 설명으로 가장 적절한 것은 어느 것입니까.
PUT 은 받는 곳 URL 을 지정한 「덮어쓰기 저장」으로, 같은 리퀘스트를 2 번 보내도 최종 상태는 1 번일 때와 같습니다. POST 는 「처리의 의뢰」이므로, 2 번 보내면 주문이 2 건 생기듯 실행할 때마다 효과가 쌓일 수 있습니다. 이 차이가 다음 연습에서 다루는 「멱등」의 경계이며, 암호화나 데이터 형식·HTTP 버전과는 무관합니다.
2 가지 약속 — 안전과 멱등
5 가지 동사를 암기하는 것보다 중요한 것이, 메서드에 묶여 있는 2 가지 약속입니다. 이 두 단어는 사양서에도 오류 조사에도 자주 나옵니다.
여기서 손을 움직여 확인해 봅시다. PUT /users/42/profile 로 「이름 = 고무라」로 치환하는 리퀘스트를 3 번 보내도, 최종 상태는 「이름 = 고무라」— 1 번일 때와 같습니다. DELETE /orders/7 을 2 번 보내도, 7 번 주문이 지워져 있다는 최종 상태는 같습니다(2 번째는 404 가 돌아올지도 모르지만, 멱등성이 보는 것은 서버 쪽의 최종 상태입니다). 한편 POST /orders 를 3 번 보내면 주문이 3 건 생긴다 — POST 는 멱등이 아닙니다.
관계를 정리하면, 안전하면 반드시 멱등입니다(아무것도 바꾸지 않으니, 몇 번을 해도 같음). 역은 성립하지 않고, PUT 과 DELETE 는 「바꾸지만, 몇 번을 해도 같다」— 멱등이지만 안전하지 않다는 중간 띠에 들어갑니다.
5 가지 메서드를 안전·멱등의 2 축으로 늘어놓은 지도입니다. GET/HEAD 는 두 약속을 모두 지키고, PUT/DELETE 는 멱등만, POST 는 어느 약속도 하지 않습니다. 이 지도가 「재시도해도 되는가」의 판단표가 됩니다.
실무에서의 효과 — 갱신의 재시도는 멱등성을 확인하고 나서
이 2 가지 약속이 실무에서 가장 잘 듣는 곳은, 통신이 실패했을 때의 재전송(재시도) 판단입니다.
리퀘스트를 보내고 타임아웃되었을 때, 사실 2 가지 가능성이 있습니다. 「리퀘스트가 서버에 도착하지 않았다」인가, 「도착해서 처리도 끝났는데, 답장만 사라졌다」인가 — 클라이언트에서는 구별할 수 없습니다. 여기서 멱등성이 듣습니다.
그렇기 때문에 브라우저는 POST 의 결과 화면에서 새로 고침을 하면 「폼을 다시 제출하시겠습니까?」라고 일부러 확인해 옵니다(이 장면은 제 7 장의 케이스에서 다시 등장합니다). 또, 자동 재시도를 내장한 라이브러리의 다수가 기본값으로 GET 만 재전송하는 것도, 이 약속이 근거입니다. 「갱신의 재시도는, 그 메서드가 멱등인지 확인하고 나서」 — 손이 기억할 때까지 되뇌어 주세요.
연습 2-2 — 안전과 멱등의 경계
2 가지 약속을 정확하게 구별해 말하고, 실무의 「재시도해도 되는가」에 연결합니다.
Q4. 메서드가 「안전(safe)」하다는 것은 어떤 약속입니까.
안전 = 「읽기만 하고, 서버의 상태를 바꾸지 않는다」는 약속으로, GET 과 HEAD 가 여기에 해당합니다. 암호화(그것은 TLS 의 일)나 속도·무오류의 보증이 아닙니다. 이 약속이 있기 때문에 검색 엔진의 크롤러는 안심하고 링크를 GET 으로 따라갈 수 있습니다 — GET 으로 상품이 삭제되는 구조는 이 약속을 깬 설계입니다.
Q5. 「멱등이지만 안전하지 않은」 메서드의 조합은 어느 것입니까.
PUT(같은 내용으로의 치환)과 DELETE(같은 것의 삭제)는 서버의 상태를 바꾸므로 안전하지 않지만, 2 번 실행해도 최종 상태는 1 번과 같으므로 멱등입니다. GET 과 HEAD 는 안전(바꾸지 않음)하고, 바꾸지 않으니 당연히 멱등이기도 합니다. POST 는 안전하지도 멱등하지도 않은 대표격입니다. 「안전 ⊂ 멱등」이라는 포함 관계로 기억하면 정리하기 쉽습니다.
Q6. 갱신 계열의 API 리퀘스트를 보냈는데, 응답이 돌아오지 않고 타임아웃되었습니다. 같은 리퀘스트를 다시 보내기 전에 확인해야 할 것으로 가장 적절한 것은 어느 것입니까.
타임아웃이 무서운 점은 「서버에 도착해 처리까지 끝났는데, 답장만 사라졌을」 가능성이 있다는 것입니다. 멱등한 PUT/DELETE 라면, 도착해 있었더라도 재전송의 결과는 변하지 않으므로 안심하고 다시 보낼 수 있습니다. POST 는 재전송하면 주문이나 결제가 이중이 될 수 있으므로, 처리 완료 여부를 확인한 뒤에, 혹은 이중 실행을 막는 구조 위에서 재전송합니다. 「갱신의 재시도는 멱등성을 확인하고 나서」— 이 장에서 실무에 가장 잘 듣는 한 문장입니다.
이 장에서 가져갈 것
- 메서드는 왕복 서신의 동사. 취득은 GET, 본문 없는 확인은 HEAD, 처리 의뢰·신규 작성은 POST, 받는 곳 지정 치환은 PUT, 삭제는 DELETE
- 안전 = 서버의 상태를 바꾸지 않는다(GET/HEAD). 멱등 = 몇 번을 해도 최종 상태가 같다(GET/HEAD/PUT/DELETE). 안전하면 반드시 멱등
- POST 는 안전하지도 멱등하지도 않다. 타임아웃 후의 무조건적인 재시도는 이중 처리의 위험이 있다
- 갱신의 재시도는 멱등성을 확인하고 나서 — 이 한 문장이 실무에서의 효과
부탁을 읽는 법이 몸에 익었으니, 다음 장은 왕복 서신의 반대쪽 — 서버가 보내는 답장입니다. 3 자리 상태 코드를 백의 자리부터 순서대로 읽어냅니다.