HTTP 상태 코드 428: 전제 조건 필요? 업데이트 손실 방지

INEZA Felin-Michel

INEZA Felin-Michel

21 October 2025

HTTP 상태 코드 428: 전제 조건 필요? 업데이트 손실 방지

Apidog 엔터프라이즈

온프레미스 배포

SSO & RBAC

SOC 2 준수

Apidog Enterprise 살펴보기

웹 기반 편집기를 사용하여 동료와 중요한 문서를 공동 작업하고 있습니다. 두 사람 모두 동시에 같은 문서를 엽니다. 동료가 결론을 작업하는 동안 당신은 30분 동안 서론을 신중하게 다시 작성합니다. 당신이 먼저 "저장"을 클릭하면 변경 사항이 적용됩니다. 그러고 나서 동료가 "저장"을 클릭하면, 아무런 경고 없이 동료의 버전이 당신의 훌륭한 새 서론을 완전히 덮어씁니다. 당신의 작업은 방금 "잃어버린 업데이트 문제(lost update problem)"의 희생양이 된 것입니다.

이 답답한 시나리오는 바로 428 Precondition Required HTTP 상태 코드가 방지하기 위해 설계된 것입니다. 이 코드는 HTTP 사양에서 더 정교하고 사전 예방적인 상태 코드 중 하나로, 여러 사용자가 동시에 수정할 수 있는 리소스를 위한 보호 메커니즘 역할을 합니다.

이는 흔히 접하는 상태 코드는 아니지만, 안전하고 신뢰할 수 있으며 동시적인 API 통신에서 매우 중요한 역할을 합니다.

그렇다면 HTTP 상태 코드 428 Precondition Required는 정확히 무엇을 의미할까요? 언제 나타나며, 어떻게 적절하게 처리할 수 있을까요?

마치 어떤 판본을 업데이트하는지 확인하기 전에는 책을 빌려주지 않는 신중한 사서라고 생각해보세요. 이것은 서버가 "변경을 허용하기 전에 이 리소스의 최신 버전으로 작업하고 있음을 증명해야 합니다"라고 말하는 방식입니다.

공동 작업 애플리케이션, 동시 업데이트를 처리하는 API, 또는 데이터 일관성이 중요한 모든 시스템을 구축하고 있다면, 428을 이해하는 것이 필수적입니다.

바로 이것이 이번 심층 분석에서 다룰 내용이며, 이를 통해 428이 무엇을 의미하는지뿐만 아니라 존재하며 어떻게 API를 더 좋게 만들 수 있는지 이해할 수 있을 것입니다.

💡
동시 접근을 안전하게 처리해야 하는 API를 구축하거나 테스트하고 있다면, 이러한 복잡한 시나리오를 시뮬레이션하는 데 도움이 되는 도구가 필요합니다. Apidog를 무료로 다운로드하세요. Apidog는 ETags 및 헤더를 사용한 조건부 요청을 쉽게 테스트하여 애플리케이션이 428 응답을 올바르게 처리할 수 있도록 하는 올인원 API 플랫폼입니다.
버튼

이제 HTTP 428 Precondition Required가 충돌하는 업데이트 문제를 어떻게 해결하는지 살펴보겠습니다.

문제: 끔찍한 잃어버린 업데이트

428이 왜 존재하는지 이해하려면, 이 코드가 해결하는 문제를 알아야 합니다. 다중 사용자 시스템에서 두 명 이상의 사용자가 거의 동시에 동일한 리소스를 업데이트하려고 할 때, 다음과 같은 몇 가지 문제가 발생할 수 있습니다.

  1. 잃어버린 업데이트: 두 번째 쓰기가 첫 번째 쓰기의 변경 사항을 통합하지 않고 덮어쓰는 고전적인 문제입니다.
  2. 충돌하는 변경: 두 사용자가 동일한 리소스의 다른 부분에 서로 다른 변경 사항을 만듭니다.
  3. 오래된 데이터 업데이트: 사용자가 오래된 정보에 기반하여 변경 사항을 만듭니다.

전통적인 접근 방식은 종종 클라이언트가 조건부 헤더를 포함하여 "올바른 일을 하는" 것에 의존합니다. 하지만 클라이언트가 이를 잊어버리면 어떻게 될까요? 428 코드는 서버가 올바른 동작을 강제할 수 있도록 합니다.

HTTP 428 Precondition Required는 실제로 무엇을 의미할까요?

428 Precondition Required 상태 코드는 원본 서버가 요청이 조건부여야 한다고 요구함을 나타냅니다. 이는 클라이언트가 최신 데이터로 작업하고 있음을 증명하기 위해 조건부 헤더(예: If-Match 또는 If-Unmodified-Since)를 포함해야 한다고 서버가 지시하는 방식입니다.

응답은 어떤 전제 조건이 필요한지에 대한 유용한 설명을 포함해야 합니다. 일반적인 428 응답은 다음과 같습니다.

HTTP/1.1 428 Precondition RequiredContent-Type: application/problem+json
{
  "type": "<https://example.com/probs/conditional-required>",
  "title": "Precondition Required",
  "detail": "This resource requires conditional requests. Please include an If-Match or If-None-Match header.",
  "instance": "/articles/123"
}

서버는 본질적으로 이렇게 말하고 있는 것입니다. "이 특정 리소스에 대해서는 맹목적인 업데이트를 허용하지 않겠습니다. 어떤 버전을 수정하려고 하는지 알고 있음을 보여주셔야 합니다."

더 간단히 말해, 서버는 요청을 처리하기 전에 클라이언트가 전제 조건 헤더(예: If-Match 또는 If-Unmodified-Since)를 포함하기를 기대합니다.

만약 그 전제 조건이 포함되지 않으면, 서버는 요청을 거부하고 428 Precondition Required 오류로 응답할 것입니다.

공식 RFC 정의

428 상태 코드는 웹 통신 및 신뢰성을 향상시키기 위해 여러 추가 HTTP 상태 코드를 도입한 RFC 6585에 정의되어 있습니다.

내용은 다음과 같습니다.

“428 (Precondition Required) 상태 코드는 원본 서버가 요청이 조건부여야 한다고 요구함을 나타냅니다. 이 코드의 목적은 클라이언트가 리소스의 상태를 GET하고, 이를 수정하여 서버에 PUT하는 동안, 제3자가 그 사이에 리소스를 수정하여 발생하는 '잃어버린 업데이트' 문제를 방지하는 것입니다.”

많은 기술 용어처럼 보이지만, 본질은 간단합니다. 여러 클라이언트가 동시에 동일한 리소스를 수정할 때 **데이터 무결성**을 유지하고 **덮어쓰기를 방지**하는 것에 관한 것입니다.

쉽게 설명하기

다음 시나리오를 상상해 보세요.

팀원들과 함께 Google Docs에서 문서를 편집하고 있습니다. 문서를 열고 일부를 편집한 다음 저장을 클릭했는데, 그 사이에 팀원도 변경 사항을 만들고 당신보다 먼저 자신의 버전을 저장했습니다.

이제 버전 관리가 없다면, 당신의 변경 사항이 팀원의 변경 사항을 덮어쓸 것입니다. 이것이 바로 428 Precondition Required 상태 코드가 API에서 방지하는 것입니다.

이 코드는 클라이언트에게 이렇게 말합니다.

“이 리소스를 수정하기 전에, 당신이 최신 버전으로 작업하고 있음을 내게 증명하세요.”

428은 왜 도입되었을까요?

RESTful API 및 일반적인 HTTP 작업에서 클라이언트는 리소스를 읽고, 로컬에서 일부를 수정한 다음, 업데이트 요청을 보낼 수 있습니다. 그러나 그 사이에 리소스가 변경되었다면, 업데이트를 맹목적으로 적용하는 것은 더 새로운 변경 사항을 덮어쓸 위험이 있습니다.

클라이언트가 전제 조건을 지정하도록 요구함으로써, 서버는 다음을 보장합니다.

이는 동시 작업 또는 다중 사용자를 지원하는 API에 매우 중요합니다.

작동 방식: 조건부 요청 흐름

공동 편집 시나리오에서 428이 잃어버린 업데이트를 방지하는 방법에 대한 전체 예시를 살펴보겠습니다.

1단계: 사용자 A가 리소스를 가져옵니다.

사용자 A가 현재 문서를 검색합니다.

GET /documents/123 HTTP/1.1

서버는 문서로 응답하고, 이 특정 리소스 버전에 대한 고유 식별자인 ETag 헤더를 포함합니다.

HTTP/1.1 200 OKContent-Type: application/jsonETag: "abc123"
{
  "id": 123,
  "title": "Project Proposal",
  "content": "Original content...",
  "version": "abc123"
}

2단계: 사용자 B가 동일한 리소스를 가져옵니다.

거의 동시에 사용자 B도 문서를 요청하고 동일한 ETag를 받습니다.

3단계: 사용자 A가 업데이트를 시도합니다 (조건 없이).

사용자 A가 문서를 업데이트하려고 시도하지만, 조건부 헤더를 포함하는 것을 잊습니다.

PUT /documents/123 HTTP/1.1Content-Type: application/json
{
  "id": 123,
  "title": "Project Proposal",
  "content": "User A's updated content...",
  "version": "abc123"
}

4단계: 서버의 428 응답

이 엔드포인트는 전제 조건을 요구하도록 구성되어 있으므로, 서버는 다음과 같이 응답합니다.

HTTP/1.1 428 Precondition RequiredContent-Type: application/json
{
  "error": "precondition_required",
  "message": "This resource requires conditional updates. Please include an If-Match header with the current ETag."
}

5단계: 사용자 A가 올바른 헤더로 재시도합니다.

사용자 A의 애플리케이션은 428 응답을 보고 적절한 조건부 헤더로 자동으로 재시도합니다.

PUT /documents/123 HTTP/1.1Content-Type: application/jsonIf-Match: "abc123"
{
  "id": 123,
  "title": "Project Proposal",
  "content": "User A's updated content...",
  "version": "abc123"
}

서버는 이 조건부 요청을 성공적으로 처리하고 새로운 ETag와 함께 200 OK를 반환합니다.

6단계: 사용자 B가 업데이트를 시도합니다.

사용자 B가 오래된 ETag로 업데이트를 시도하면, 서버는 이제 412 Precondition Failed로 이를 거부하여 잃어버린 업데이트를 방지할 수 있습니다.

428 대 412 Precondition Failed: 차이점 이해하기

이는 조건부 요청의 세계에서 중요한 차이점입니다.

비유:

428 Precondition Required가 존재하는 이유

언뜻 보기에는 번거롭게 느껴질 수 있습니다. 왜 클라이언트가 자유롭게 업데이트하도록 두지 않을까요?

글쎄요, 428 상태 코드는 분산 시스템에서 **데이터 손실을 방지**하고 **일관성을 보장**하기 위한 좋은 이유로 존재합니다.

그 목적을 더 자세히 살펴보겠습니다.

1. 잃어버린 업데이트 방지

"잃어버린 업데이트" 문제는 여러 클라이언트가 동일한 리소스를 가져와 독립적으로 업데이트할 때 발생합니다. 전제 조건이 없으면 한 클라이언트의 업데이트가 다른 클라이언트의 업데이트를 조용히 덮어쓸 수 있습니다.

428은 모든 수정 작업이 리소스가 가져온 이후 변경되었는지 확인하도록 하여, 조용한 데이터 손실을 방지합니다.

2. 데이터 무결성 보장

If-Match와 같은 전제 조건을 요구함으로써, 서버는 업데이트가 리소스의 올바른 버전에만 적용되도록 보장합니다. 이는 데이터에 안전 잠금 장치를 거는 것과 같습니다.

3. 안전한 동시성 증진

공동 편집, API 통합 또는 RESTful 서비스와 같이 많은 사용자가 공유 리소스와 상호 작용하는 시스템에서 428은 동시성 관리를 더 예측 가능하고 안전하게 만듭니다.

4. 모범 사례 장려

조건부 요청을 강제함으로써, 서버는 개발자들이 **ETag**, **조건부 GET**, **버전 확인**과 같은 **RESTful 설계 모범 사례**를 따르도록 유도합니다.

428 Precondition Required를 사용해야 할 때

다음 시나리오에서 428 사용을 고려해야 합니다.

1. 공동 편집 애플리케이션

여러 사용자가 동시에 동일한 문서를 편집할 수 있는 Google Docs 스타일 애플리케이션.

2. 경합이 심한 리소스

여러 소스에서 빈번한 업데이트가 발생하는 모든 리소스, 예를 들면 다음과 같습니다.

3. 민감한 데이터 업데이트

재정 기록이나 의료 데이터처럼 실수로 덮어쓰면 심각한 결과를 초래할 수 있는 리소스.

4. 안전을 위한 API 설계

좋은 클라이언트 동작을 강제하고 일반적인 동시성 문제를 방지하고자 할 때.

428 Precondition Required의 실제 시나리오

1. 동시 API 편집

여러 클라이언트가 동시에 동일한 레코드를 수정할 때, 428은 업데이트가 서로 덮어쓰지 않도록 보장합니다.

2. 버전 관리 API

시간이 지남에 따라 발전하는 API는 클라이언트가 호환되는 버전을 사용하고 있음을 보장하기 위해 전제 조건을 강제할 수 있습니다.

3. 낙관적 잠금 시스템

낙관적 동시성 제어를 위해 **ETag**를 사용하는 데이터베이스 또는 REST API는 충돌을 감지하기 위해 전제 조건에 의존합니다.

4. 파일 또는 객체 저장 API

S3와 같은 클라우드 저장 시스템은 조건부 요청을 많이 사용하며, 428은 이러한 규칙을 강제하는 데 자연스럽게 적합할 것입니다.

Apidog로 API 테스트하기

동시성 제어를 다룰 때, **Apidog**는 당신의 비밀 병기가 됩니다. 조건부 요청 흐름을 테스트하려면 신중한 설정과 여러 단계가 필요합니다. Apidog는 이러한 유형의 테스트에 완벽하게 적합합니다.

Apidog를 사용하면 다음을 수행할 수 있습니다.

1. 테스트 시나리오 생성: 다음을 포함하는 완전한 테스트 흐름을 구축합니다.

2. 헤더 관리 자동화: Apidog의 환경 변수를 사용하여 요청 전반에 걸쳐 ETag 값을 자동으로 저장하고 재사용합니다.

3. 경쟁 조건 시뮬레이션: 다른 ETag를 사용하여 병렬 요청을 보내 여러 사용자가 동일한 리소스를 업데이트하는 것을 시뮬레이션하는 테스트 스위트를 생성합니다.

4. 오류 응답 유효성 검사: 428 응답에 클라이언트가 다르게 수행해야 할 사항을 안내하는 유용한 오류 메시지가 포함되어 있는지 확인합니다.

5. 클라이언트 복원력 테스트: 클라이언트 애플리케이션이 적절한 조건부 헤더로 재시도하여 428 응답을 올바르게 처리하는지 확인합니다.

버튼

구현 모범 사례

서버 개발자를 위한:

클라이언트 개발자를 위한:

더 큰 그림: 견고한 API 구축

428 Precondition Required 상태 코드는 더 견고하고 자체 문서화된 API로의 전환을 나타냅니다. 클라이언트가 조건부 요청을 사용하도록 요구함으로써 다음을 수행하는 것입니다.

  1. 데이터 손실 방지: 전체 범주의 동시성 버그를 제거합니다.
  2. API 안전성 향상: 클라이언트가 실수로 데이터를 손상시키기 어렵게 만듭니다.
  3. 모범 사례 강제: 클라이언트가 올바른 사용 패턴을 따르도록 안내합니다.
  4. 더 나은 진단 제공: 클라이언트가 실수를 했을 때 명확한 피드백을 제공합니다.

결론: 반응적 오류 처리에서 사전 예방적 오류 처리로

HTTP 428 Precondition Required 상태 코드는 동시성 제어를 선택적 모범 사례에서 강제 가능한 요구 사항으로 전환합니다. 이는 오류 처리를 반응적("당신의 업데이트가 다른 사람의 업데이트와 충돌했습니다")에서 사전 예방적("업데이트를 고려하기 전에 현재 데이터로 작업하고 있음을 증명해야 합니다")으로 바꿉니다.

추가적인 단계처럼 보일 수 있지만, 이 접근 방식은 궁극적으로 더 안정적인 애플리케이션과 조용한 데이터 손상으로 인해 작업을 잃지 않는 더 행복한 사용자들을 만듭니다.

최신 웹 애플리케이션을 구축하는 개발자에게 428을 이해하고 구현하는 것은 API 설계의 정교함을 나타내는 표시입니다. 이는 API가 무엇을 하는지뿐만 아니라, 여러 사용자와 함께 실제 환경에서 어떻게 작동하는지에 대해서도 생각하고 있음을 보여줍니다.

그리고 이러한 정교한 동시성 제어를 구현하고 테스트할 준비가 되면, **Apidog**와 같은 강력한 도구가 조건부 요청 로직이 완벽하게 작동하여 사용자 데이터를 보호하고 안정성을 확보하는 데 필요한 테스트 환경을 제공합니다.

버튼

Apidog에서 API 설계-첫 번째 연습

API를 더 쉽게 구축하고 사용하는 방법을 발견하세요