428 Precondition Required
사용법
428 Precondition Required 상태 코드는 서버가 이런 유형의 HTTP 요청은 조건부여야 하며 If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since 같은 적절한 HTTP 헤더를 포함해야 한다고 기대함을 알립니다.
이러한 HTTP 헤더는 서버가 지원할 때 클라이언트가 선택적으로 사용하는 것이지만, 이 경우에는 서버가 그 사용을 요구합니다. 사용 사례에 따라, 이는 “업데이트 손실” 같은 데이터 손실 사례를 줄이기 위한 것입니다.
업데이트 손실은 클라이언트가 GET으로 리소스를 가져와 수정한 뒤 PUT으로 수정본을 서버에 돌려보낼 때 발생합니다. 제3자의 간섭이 없다면 문제가 없습니다. 최초 GET 이후 제3자가 서버상 리소스의 상태를 수정했다면 충돌이 생깁니다. 클라이언트가 리소스 상태를 먼저 확인하지 않고 PUT을 사용하면 제3자의 갱신이 덮어써집니다(업데이트 손실).
ETag와 날짜를 이용한 조건부 검사를 강제하면 리소스 상태에 대한 가정이 줄어들기 때문에 클라이언트가 다른 갱신을 덮어쓸 여지가 줄어듭니다.
이는 조건 중 하나 이상이 통과하지 못했을 때 서버가 보내는 412 Precondition Failed 응답과 관련이 있습니다.
SEO 영향
Google 같은 검색 엔진은 428 응답 상태를 가진 URL을 색인하지 않습니다. 이전에 색인되었다가 이 상태 코드를 반환하는 URL은 검색 결과에서 제거됩니다.
예시
클라이언트는 앞서 리소스의 사본을 가져왔고 그 이후 로컬 사본을 수정했습니다. 클라이언트가 리소스를 PUT으로 보내 서버를 갱신하려 하지만, HTTP 헤더에 조건부 검사가 포함되지 않았으므로 서버는 428 Precondition Required로 응답합니다.
요청
PUT /reservations.txt HTTP/1.1
Host: example.com
응답
HTTP/1.1 428 Precondition Required
Content-Type: text/html
Content-Length: 193
<html>
<head>
<title>Conditional Update Required</title>
</head>
<body>
<p>This PUT request must be done conditionally.
Try again using If-Unmodified-Since.</p>
</body>
</html>
해결 방법
먼저 GET 요청으로 리소스를 가져와 응답 헤더에서 ETag 값을 추출하세요. 그런 다음 가져온 ETag 값을 If-Match 헤더에 설정해 수정 요청(PUT, PATCH, DELETE)을 다시 보내세요.
또는 최초 GET 응답의 Last-Modified 날짜를 If-Unmodified-Since 헤더에 사용하세요. 서버는 날짜를 비교해 그 이후 리소스가 변경되지 않았을 때만 진행합니다.
ETag 방식이 더 정밀합니다. 타임스탬프는 초 단위 정밀도라 짧은 간격의 연속 편집을 놓칩니다. ETag는 시점과 무관하게 수정될 때마다 바뀝니다.
ETag를 가져온 GET이 이후의 수정 요청과 같은 리소스를 대상으로 하는지 확인하세요. 다른 레코드의 ETag를 사용하면 412 Precondition Failed 응답이 발생합니다.
SAP, Salesforce 및 유사한 플랫폼 위에 구축된 API는 갱신과 삭제 작업에 조건부 요청을 강제하는 경우가 많습니다. 어떤 엔드포인트가 전제 조건을 요구하고 어떤 검증자 헤더를 받아들이는지 API 문서에서 확인하세요.
서버 측에서 428 응답은 같은 리소스를 편집하는 두 클라이언트가 서로를 덮어쓰는 “업데이트 손실” 문제를 막는 데 도움이 됩니다. 변경 가능한 모든 리소스에 ETag 헤더를 포함하면 클라이언트가 조건부 요청에 필요한 값을 확보할 수 있습니다.
코드 참고
.NET
HttpStatusCode.PreconditionRequired
Rust
http::StatusCode::PRECONDITION_REQUIRED
Rails
:precondition_required
Go
http.StatusPreconditionRequired
Symfony
Response::HTTP_PRECONDITION_REQUIRED
Python3.5+
http.HTTPStatus.PRECONDITION_REQUIRED
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_PRECONDITION_REQUIRED
Angular
@angular/common/http/HttpStatusCode.PreconditionRequired