HTTP 메서드의 안전과 멱등: POST 재시도는 위험하고 PUT은 괜찮은 이유
RFC 9110 기준으로 안전한 메서드와 멱등 메서드의 차이를 정리했습니다. 연결이 끊겼을 때 GET·PUT·DELETE는 다시 보내도 되고 POST·PATCH는 왜 조심해야 하는지, If-Match로 막는 방법까지 다룹니다.
응답을 받기 전에 연결이 끊기면, 클라이언트는 요청이 서버에 반영됐는지 알 수 없습니다. 이때 같은 요청을 다시 보내도 되는지는 메서드가 정합니다. HTTP 의미론 명세 RFC 9110은 메서드의 성질을 “안전(safe)“과 “멱등(idempotent)” 두 가지로 나눕니다.
안전: 읽기만 요청한다
안전한 메서드는 정의된 의미가 사실상 읽기 전용인 메서드입니다. 클라이언트가 서버 상태의 변경을 요청하지도, 기대하지도 않습니다(9.2.1절). RFC 9110이 정의한 메서드 중에서는 GET, HEAD, OPTIONS, TRACE가 안전합니다.
부수 효과가 전혀 없다는 뜻은 아닙니다. 명세는 요청마다 접근 로그를 남기는 서버나, 광고를 누르면 광고 계정에 과금되는 경우도 안전하다고 봅니다. 중요한 것은 클라이언트가 그 동작을 요청하지 않았다는 점입니다. 반대로 page?do=delete처럼 GET으로 삭제가 일어나게 만들었다면, 리소스 소유자는 안전한 메서드로 접근할 때 그 동작을 막아야 합니다(MUST). 링크 점검이나 프리페치, 검색 색인처럼 모든 URI에 GET을 보내는 자동화된 처리가 있기 때문입니다.
멱등: 여러 번 보내도 의도한 효과는 한 번
멱등 메서드는 같은 요청을 여러 번 보냈을 때 서버에 의도한 효과가 한 번 보낸 것과 같은 메서드입니다(9.2.2절). PUT, DELETE와 안전한 메서드가 여기에 속합니다. PUT은 대상의 상태를 보낸 표현으로 바꾸라는 요청이라, 같은 내용으로 두 번 보내도 결과 상태가 같습니다. POST는 리소스가 자기 방식대로 처리하라는 요청이라 두 번 보내면 주문이 두 건 생길 수 있습니다.
같은 응답을 보장한다는 뜻은 아닙니다. 명세는 원래 요청이 성공했더라도 반복하면 응답은 달라질 수 있다고 적습니다. 예를 들어 같은 DELETE를 다시 보내면 두 번째에는 404가 올 수 있지만, 지워진 상태라는 효과는 같습니다. 서버가 요청마다 로그나 변경 이력을 따로 남기는 것도 허용됩니다.
| 메서드 | 안전 | 멱등 |
|---|---|---|
| GET, HEAD, OPTIONS, TRACE | 예 | 예 |
| PUT, DELETE | 아니요 | 예 |
| POST, PATCH | 아니요 | 아니요 |
재시도 규칙
이 구분이 실제로 쓰이는 곳은 자동 재시도입니다. 클라이언트는 응답을 읽기 전에 연결이 끊긴 멱등 요청을 새 연결로 다시 보낼 수 있습니다. 멱등이 아닌 요청은 실제로 멱등이라는 것을 알거나 원래 요청이 반영되지 않았음을 알아낼 방법이 없다면 자동으로 다시 보내지 않아야 합니다(SHOULD NOT). 프록시는 멱등이 아닌 요청을 자동으로 재시도하면 안 되고(MUST NOT), 자동 재시도가 실패했을 때 그것을 또 자동으로 재시도해서도 안 됩니다(SHOULD NOT). 명세는 일부 클라이언트가 유휴 상태였던 지속 연결이 끊기면 POST를 다시 보내는 위험한 추측을 한다는 점도 함께 적고 있습니다.
PATCH와 If-Match
PATCH를 정의한 RFC 5789는 PATCH가 안전하지도 멱등하지도 않다고 명시합니다. 다만 멱등하게 보낼 수는 있습니다. 특정 기준 상태를 전제로 하는 패치 형식이라면 클라이언트는 조건부 요청을 써야 합니다(SHOULD). 예를 들어 마지막으로 받은 강한 ETag를 If-Match에 넣으면, 그 사이에 리소스가 바뀌었을 때 요청이 실패합니다. If-Match 조건이 거짓이면 서버는 메서드를 실행하지 않고(MUST NOT) 412 Precondition Failed로 답할 수 있습니다. 같은 변경이 이미 반영된 것으로 보이면 2xx로 답할 수도 있습니다(RFC 9110 13.1.1절). ETag 자체는 no-cache와 no-store 차이에서 다뤘습니다.
POST를 다시 보내야 한다면
명세는 설계나 설정을 통해 그 요청의 의미가 실제로 멱등하다는 것을 아는 경우라면 POST도 자동으로 반복할 수 있다고 봅니다. 결제나 주문 API에서 쓰는 한 가지 방법은 요청마다 고유 키를 받아 같은 요청의 재시도를 식별하고, 이미 끝난 요청이면 이전 처리 결과를 돌려주는 것입니다. IETF에는 이를 위한 Idempotency-Key 헤더 초안이 있지만, 2026년 10월 8일 확인 시점에 RFC가 아니며 초안도 만료된 상태입니다. 폼을 POST한 뒤 결과 페이지로 보내는 303 응답은 301, 302, 307, 308 리다이렉트의 차이에 정리했습니다.
본문이 있는 안전한 요청: QUERY
2026년 6월 발행된 RFC 10008은 QUERY 메서드를 정의합니다. POST처럼 요청 본문을 보내지만 안전하고 멱등하므로 연결이 끊긴 뒤에도 다시 보낼 수 있습니다. 브라우저에서 교차 출처로 QUERY 요청을 보내면 QUERY는 CORS 허용 목록 메서드가 아니므로 사전 요청(preflight)이 필요합니다. 새로 나온 명세라서, 쓰려는 클라이언트·서버·프록시가 지원하는지는 따로 확인해야 합니다.
참고한 문서
- RFC 9110 HTTP Semantics: 9.2.1, 9.2.2, 9.3.3–9.3.5, 13.1.1절
- RFC 5789 PATCH Method for HTTP: 2절
- RFC 10008 The HTTP QUERY Method: 2절, 4절
- IANA HTTP Method Registry
- IETF draft-ietf-httpapi-idempotency-key-header
링크와 내용은 2026년 10월 8일에 확인했습니다.