본문으로 건너뛰기
개발 머꼬
개발 노트API 설계
hohyeon.dev26

PATCH로 필드 하나만 보냈는데 나머지가 지워진 이유

  • #API 설계
  • #Engineering Note

문제 발생

프로필 수정 API에서 닉네임만 바꾸려고 보냈습니다.

PATCH /api/profile
{ "nickname": "머꼬" }

응답은 200이었는데 소개글과 링크가 전부 비워졌습니다. 서버가 요청 본문을 그대로 저장하고 있었기 때문입니다. 반대로 "소개글을 지우고 싶다"는 요구는 표현할 방법이 없었습니다.

원인 분석

PATCH의 의미는 본문 형식이 정합니다. HTTP는 PATCH를 "부분 수정"이라고만 정의하고, 어떻게 적용할지는 미디어 타입이 결정합니다. 형식을 정하지 않으면 "보낸 필드만 바꾼다"인지 "보낸 것으로 전체를 대체한다"인지 서버 구현에 달리게 됩니다.

RFC 7386의 JSON Merge Patch가 이 자리를 위한 표준입니다 — merge patch 형식은 주로 HTTP PATCH 메서드와 함께, 대상 리소스 내용에 대한 수정 집합을 기술하는 수단으로 쓰이도록 의도되었고, 미디어 타입은 application/merge-patch+json입니다.

규칙의 핵심은 null입니다 — merge patch의 null 값은 대상의 기존 값을 제거하라는 의미로 특별하게 취급됩니다. 그래서 "지우기"를 표현할 수 있습니다.

그리고 알아둬야 할 한계가 있습니다 — 객체가 아닌 대상의 일부를 패치하는 것, 예를 들어 배열의 일부 값만 교체하는 것은 불가능합니다. 배열은 통째로 교체됩니다. 같은 이유로 필드 값을 null로 "설정"하는 것도 표현할 수 없습니다 — 그 자리는 삭제의 의미로 이미 쓰였기 때문입니다.

해결 방안

  1. 형식을 정하고 문서에 적습니다.
PATCH /api/profile
Content-Type: application/merge-patch+json

{ "nickname": "머꼬", "bio": null }     // 닉네임 변경, 소개글 삭제
  1. 서버 구현을 규칙에 맞춥니다. 본문에 있는 키만 반영하고, 값이 null이면 삭제(또는 컬럼을 NULL로), 없는 키는 그대로 둡니다. MEOKKO의 데이터 접근 함수들이 쓰는 tri-state 규약(생략=유지, null=비움)이 정확히 이 의미입니다.

  2. 배열의 부분 수정이 필요하면 다른 도구를 씁니다. JSON Patch(RFC 6902)의 연산 목록을 쓰거나, POST /posts/1/tags처럼 하위 리소스로 모델링하는 편이 대개 더 이해하기 쉽습니다.

  3. null을 값으로 저장해야 하는 필드가 있으면 형식을 바꿉니다. merge patch로는 표현할 수 없습니다. 그런 필드가 하나라도 있으면 JSON Patch를 검토합니다.

  4. 전체 교체가 의도라면 PUT을 씁니다. 부분 수정과 전체 교체를 같은 엔드포인트에 섞지 않습니다.

  5. 동시 수정 보호를 함께 붙입니다. PATCH는 "지금 상태"를 전제로 하므로 If-Match와 ETag를 함께 쓰면 다른 사람의 수정을 덮어쓰는 문제를 막을 수 있습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.