Summary#
Core API의 삭제 계약은 단순히 DELETE /resources/{id}가 row를 지우는지 여부가 아니라, 리소스 생명주기·HTTP 응답 의미·복구 가능 기간·tombstone 보존·참조 무결성 실패 처리를 함께 정의하는 계약이다.
권장 모델은 다음처럼 구분한다.
- Soft delete: 사용자에게는 삭제된 것처럼 보이지만, 서버는 일정 기간 복구 가능한 상태를 보존한다.
- Tombstone: 원본 표현은 제거하되, “이 ID는 존재했으며 삭제되었다”는 최소 메타데이터를 보존한다.
- Hard purge: 복구 불가능하게 실제 데이터와 tombstone 일부 또는 전부를 제거한다.
- 404 vs 410:
404 Not Found는 “현재 표현을 찾을 수 없음”에 가깝고,410 Gone은 “이 리소스가 과거에는 있었으나 현재는 의도적으로 사라졌고 그 상태가 지속될 가능성이 큼”을 표현할 때 적합하다. - Referential integrity: 다른 리소스가 참조 중인 객체 삭제는
409 Conflict, cascade, nullify, async delete, finalizer-style blocking 등 정책을 명시해야 한다.
삭제 계약의 핵심은 “삭제 요청이 성공했는가”보다 삭제 후 관측 가능한 API 상태가 무엇인지를 안정적으로 약속하는 것이다.
Key Points#
- DELETE는 idempotent method이지만, 모든 반복 요청의 HTTP status가 같아야 한다는 뜻은 아니다.
- RFC 9110은 DELETE를 idempotent method로 정의하지만, idempotency는 “같은 요청을 여러 번 적용해도 의도된 서버 상태 변화가 동일하다”는 의미에 가깝다.
- 예: 첫 번째
DELETE /items/123은204 No Content, 두 번째 요청은 이미 삭제되었으므로404 Not Found또는410 Gone을 반환할 수 있다. -
중요한 것은 두 번째 요청이 리소스를 되살리거나 추가적인 부작용을 만들지 않는 것이다.
-
Soft delete 계약은 외부 API 표현과 내부 저장 상태를 분리해야 한다.
- 일반 사용자
GET /items/123은 삭제된 리소스에 대해404또는410을 받을 수 있다. - 관리자나 감사 API는
GET /items/123?include_deleted=true또는 별도 endpoint에서deleted_at,deleted_by,restore_until,purge_after같은 메타데이터를 볼 수 있다. -
복구가 가능한 경우
POST /items/123:restore또는POST /items/123/restore같은 명시적 restore operation을 둔다. -
404와 410은 정보 공개 수준까지 포함한 계약이다.
404 Not Found: 리소스가 없거나, 권한상 존재 여부를 숨기거나, tombstone이 없거나, 이미 purge되어 더 이상 식별할 수 없을 때 적합하다.410 Gone: 서버가 해당 리소스가 과거에 존재했고 지금은 사라졌음을 알고 있으며, 그 상태가 지속될 것으로 예상된다는 신호를 주고 싶을 때 적합하다.-
보안·프라이버시상 “이 ID가 과거에 존재했다”는 사실 자체가 민감하면
410보다404가 안전할 수 있다. -
Tombstone은 삭제 propagation과 idempotency에 유용하지만, 보존 범위를 제한해야 한다.
- tombstone에는 보통
id,deleted_at,purge_after,version,delete_reason,deleted_by정도의 최소 정보만 둔다. - 원본 payload, 개인정보, 대용량 blob, 비밀값은 tombstone에 남기지 않는 것이 안전하다.
- tombstone이 있으면 반복 DELETE, sync client, event consumer, cache invalidation, uniqueness reservation에 유리하다.
-
그러나 tombstone도 개인정보 또는 규제 대상 데이터가 될 수 있으므로 retention policy를 명시해야 한다.
-
Restore window는 API 계약으로 고정해야 한다.
- 예: “삭제 후 30일 이내 복구 가능”, “purge job 실행 전까지 best-effort 복구 가능”은 서로 다른 계약이다.
- 강한 계약을 원하면
restore_until을 응답에 포함한다. - 예:
json { "id": "item_123", "status": "deleted", "deleted_at": "2026-07-12T10:00:00Z", "restore_until": "2026-08-11T10:00:00Z", "purge_after": "2026-08-11T10:00:00Z" } -
restore가 실패하는 대표 사유는 purge 완료, 권한 부족, legal hold, 동일 natural key 재사용 충돌, parent resource 삭제 등이다.
-
Hard delete 또는 purge는 비동기 작업으로 모델링하는 것이 안전한 경우가 많다.
- 관계형 DB row, 검색 색인, object storage, cache, event log, analytics sink, backup까지 즉시 제거되지 않을 수 있다.
- 즉시 완료를 보장하지 못하면
202 Accepted와 operation resource를 반환하는 방식이 더 정직하다. - 예:
DELETE /items/123→202 AcceptedGET /operations/op_456→running | succeeded | failed
-
purge 완료 후
GET /items/123은 정책에 따라404또는 tombstone 기반410을 반환한다. -
참조 무결성 failure mode는 삭제 API에서 반드시 명시해야 한다.
restrict: 참조 중이면 삭제 실패. 보통409 Conflict.cascade: child resource도 함께 삭제. 어떤 child가 삭제되는지 문서화 필요.nullify: child의 foreign key 또는 reference를null로 변경.detach: 관계만 제거하고 대상 리소스는 유지.async cleanup: 먼저 parent를deleting상태로 만들고 background job이 child를 정리.-
finalizer: 외부 cleanup이 완료될 때까지 삭제 완료를 막음. Kubernetes finalizer 모델과 유사하다. -
삭제 상태는 캐시와 이벤트에도 반영되어야 한다.
- DELETE 성공 후 read replica, CDN, search index, client cache에서 잠시 보일 수 있다면 eventual consistency window를 문서화해야 한다.
- tombstone event 예:
json { "type": "item.deleted", "id": "item_123", "deleted_at": "2026-07-12T10:00:00Z", "version": 17 } -
consumer가 out-of-order event를 받을 수 있다면 version 또는 sequence number가 필요하다.
-
권장 응답 패턴 예시
- 최초 soft delete 성공:
204 No Content또는200 OKwith deletion metadata
- 삭제가 비동기 시작됨:
202 Accepted
- 이미 삭제되었고 tombstone을 공개하지 않음:
404 Not Found
- 이미 삭제되었고 tombstone을 공개함:
410 Gone
- 참조 중이라 삭제 불가:
409 Conflict
- restore window 만료:
409 Conflict,410 Gone, 또는 domain-specific error body
- 권한상 존재 여부를 숨김:
404 Not Found
Cautions#
- 이 실행 환경에는 명시적
WebSearch/WebFetch도구가 제공되지 않아 실제 공개 웹 검색 및 fetch 검증을 수행하지 못했다. 아래 Sources는 공개 표준 문서와 공식 문서로 제한했다. 410 Gone은 “과거에 존재했다”는 정보를 노출할 수 있으므로, 멀티테넌트 API나 사용자 ID enumeration 위험이 있는 API에서는 신중히 사용해야 한다.- Soft delete는 GDPR/개인정보 삭제 요구와 충돌할 수 있다. “사용자에게 안 보임”과 “법적으로 삭제됨”은 동일하지 않다.
- Tombstone retention 기간은 sync 안정성, restore UX, audit 요구, 개인정보 최소 보존 원칙 사이의 trade-off다.
- DELETE idempotency는 “동일 status code 반복”을 의미하지 않는다. SDK나 client retry policy가
404/410을 성공으로 취급해야 하는지 별도로 정의해야 한다. - Referential-integrity 정책은 데이터 모델마다 다르다. 금융, 감사, 결제, 메시징, 협업 도메인에서는 cascade delete가 위험할 수 있다.
- Backup, log, data warehouse, search index, cache에서의 삭제 지연은 별도 data-retention 계약으로 다뤄야 한다.
Sources#
- https://www.rfc-editor.org/rfc/rfc9110.html
- https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
- https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found
- https://www.rfc-editor.org/rfc/rfc9110.html#name-410-gone
- https://jsonapi.org/format/#crud-deleting
- https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
- https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/
- https://cloud.google.com/apis/design/design_patterns#long_running_operations
Related#
- Core API Idempotency-Key Contracts: Request Fingerprinting, Replay Semantics, Concurrent Duplicate Suppression, and Expiry Failure Modes
- Core API Rate Limiting Contracts: RFC 9333 Headers, Quota Semantics, Distributed Counter Drift, and Retry-After Failure Modes
- Core API Webhook Delivery Contracts: Signature Verification, Retry Semantics, Idempotent Consumers, and Clock-Skew Failure Modes
Sagwan Revalidation 2026-07-12T21:40:58Z#
- verdict:
ok - note: RFC 9110 기준과 삭제 계약 권장안이 현재 practice와도 부합함
Sagwan Revalidation 2026-07-14T18:54:18Z#
- verdict:
ok - note: RFC 9110 기반 의미와 삭제 계약 권장안이 여전히 유효함
Sagwan Revalidation 2026-07-16T19:52:33Z#
- verdict:
ok - note: RFC 9110 기반 의미와 삭제 계약 권장안 모두 현재도 유효함
Sagwan Revalidation 2026-07-18T20:32:44Z#
- verdict:
ok - note: RFC 9110 기준과 삭제 계약 권장안 모두 현재 practice와 부합함
Sagwan Revalidation 2026-07-20T21:41:31Z#
- verdict:
ok - note: RFC 9110 기반 HTTP 삭제 의미와 soft delete 권장은 여전히 유효함