← 전체 글

JSON Patch vs JSON Merge Patch: RFC 6902 vs 7396

JSON Patch(RFC 6902)는 명시적 작업을 보낸다; JSON Merge Patch(RFC 7396)는 부분 객체를 덮어쓴다. 예시로 비교하고 맞는 것을 고른다.

HTTP API 가 리소스의 일부 만 업데이트해야 할 때, 두 IETF 표준이 그 일을 두고 경쟁합니다: JSON Merge Patch(RFC 7396) 와 JSON Patch(RFC 6902). 비슷해 보이지만 동작은 매우 다릅니다 —— Merge Patch 는 부분 객체를 덮어쓰고, JSON Patch 는 명시적인 작업 목록을 보냅니다. 이 가이드는 각각의 동작, 정면 비교, 그리고 어느 쪽을 고를지를 보여줍니다.

문제: 부분 업데이트

PUT 요청은 리소스 전체를 교체해서, 한 필드만 바꾸고 싶을 때는 낭비이고 위험합니다. HTTP PATCH 메서드가 부분 업데이트를 위해 존재하지만 —— PATCH 는 body 형식을 정의하지 않습니다. 이 두 표준이 그것을 제공합니다.

JSON Merge Patch (RFC 7396)

Merge Patch 는 대상 문서의 부분 버전입니다. 서버가 그걸 덮어씁니다: 패치의 키는 설정되고, null 로 설정된 키는 삭제 되며, 언급되지 않은 키는 그대로.

// 원본
{ "title": "Hello", "author": "Ada", "tags": ["a", "b"], "draft": true }

// Merge Patch body (Content-Type: application/merge-patch+json)
{ "title": "Hello World", "draft": null }

// 결과
{ "title": "Hello World", "author": "Ada", "tags": ["a", "b"] }
//   ^ title 업데이트       ^ author 그대로         ^ draft 삭제 (null 이었음)

직관적이고 간결합니다. 함정: null 이 "삭제" 를 의미하므로 Merge Patch 로 값을 null 로 설정할 수 없습니다, 그리고 배열은 통째로만 교체 가능 —— 단일 배열 요소를 바꿀 방법이 없습니다.

JSON Patch (RFC 6902)

JSON Patch 는 순서 있는 작업 배열이고, 각 작업은 JSON Pointer 경로로 위치를 지정합니다. 명시적이고 정확:

// JSON Patch body (Content-Type: application/json-patch+json)
[
  { "op": "replace", "path": "/title", "value": "Hello World" },
  { "op": "remove",  "path": "/draft" },
  { "op": "add",     "path": "/tags/-", "value": "c" },
  { "op": "test",    "path": "/author", "value": "Ada" }
]

여섯 가지 작업:

op효과
add값 추가 (또는 /- 로 배열 끝에 append)
remove경로의 값 삭제
replace경로의 값 교체
move값을 한 경로에서 다른 경로로 이동
copy값을 다른 경로로 복사
test값을 단언 (실패 시 전체 패치 중단)

test 작업은 안전한 원자적 업데이트를 가능하게 합니다: 문서가 예상 상태가 아니면 전체 패치가 거부됩니다 —— 낙관적 동시성 제어에 유용.

정면 비교

JSON Merge PatchJSON Patch
RFC73966902
Content-Typeapplication/merge-patch+jsonapplication/json-patch+json
가독성높음 —— 데이터처럼 보임낮음 —— 작업 목록
값을 null 로 설정 가능불가 (null 은 삭제 의미)가능
단일 배열 요소 수정불가 (배열 전체 교체)가능 (인덱스로)
조건부 / 원자적 업데이트불가가능 (test 작업)
Move / copy불가가능
최적 용도간단한 객체 필드 업데이트정밀한, 배열 인식의, 원자적 편집

어느 걸 언제 쓸지

  • JSON Merge Patch —— 클라이언트가 주로 최상위 객체 필드를 업데이트하고, body 가 리소스처럼 읽히길 원할 때. 간단한 설정과 프로필 업데이트에 좋음. 다만 필드를 null 로 설정하거나 배열 항목을 편집할 수 없다는 점을 기억하세요.
  • JSON Patch —— 배열 요소 편집, 값 이동/복사, 필드를 null 로 설정, 또는 전제 조건으로 원자적 변경 적용이 필요할 때. 협업 편집과 감사 친화적 변경 집합의 표준 선택.

HTTP PATCH 시맨틱: 멱등성과 헤더

PATCH 엔드포인트를 출시하는 팀이 자주 걸리는 두 HTTP 디테일:

  • PATCH 는 멱등일 필요 없음 (PUT 과 달리). {"op":"add","path":"/items/-","value":...} 가 있는 JSON Patch 는 append —— 두 번 적용하면 두 번 추가됩니다. 안전한 재시도가 필요하면 test 작업으로 먼저 상태를 단언하거나, 재적용이 no-op 가 되도록 작업을 설계하세요 (/items/-add 대신 고정 경로에 replace).
  • 올바른 Content-Type 사용. RFC 6902 = application/json-patch+json; RFC 7396 = application/merge-patch+json. 서버가 보통 이걸로 분기합니다.
  • 낙관적 동시성. JSON Patch 의 test 작업 (또는 If-Match ETag 헤더) 을 결합해 동시 쓰기 주체들이 서로를 조용히 덮어쓰지 못하게 합니다.

알아 둘 만한 라이브러리

JSON Patch 의 경우 fast-json-patch 가 사실상의 npm 패키지 —— 적용, 생성 (두 문서 diff 해서 패치 생성), 관찰 (감시 대상 객체 변화 추적) 가능. JSON Merge Patch 규칙은 인라인으로 작성할 만큼 작지만, json-merge-patch 가 null 삭제 엣지 케이스를 처리해 구현합니다.

코드에서 패치 적용

// JavaScript —— fast-json-patch 가 RFC 6902 를 구현
import { applyPatch } from 'fast-json-patch';
const updated = applyPatch(document, patchOps).newDocument;

// JSON Merge Patch 는 재귀로 적용하기 쉽지만, 라이브러리
// (예: json-merge-patch) 가 null 삭제 규칙을 정확히 처리합니다.

패치 적용 후, before/after 를 diff 해서 변경이 의도한 그대로인지 확인할 수 있습니다 —— 두 JSON 파일 비교하는 법 참고, 또는 둘 다 JSON Diff 에 붙여 넣기.

자주 묻는 질문

JSON Patch 와 JSON Merge Patch 의 차이는?

JSON Merge Patch (RFC 7396) 는 문서의 부분 복사본으로, null 이 키를 삭제. JSON Patch (RFC 6902) 는 JSON Pointer 경로를 대상으로 하는 명시적 작업 배열 (add, remove, replace, move, copy, test). Merge Patch 가 더 단순; JSON Patch 가 더 강력.

왜 JSON Merge Patch 는 필드를 null 로 설정 못 하나요?

Merge Patch 에서 null 은 키를 삭제 하라는 신호이기 때문입니다. 실제 null 값을 저장해야 한다면 JSON Patch 의 replace 작업을 쓰세요.

어떤 Content-Type 을 써야 하나요?

application/merge-patch+json 은 Merge Patch, application/json-patch+json 은 JSON Patch, 둘 다 HTTP PATCH 메서드로 전송.

배열의 한 요소를 어떻게 편집하나요?

/items/2 같은 경로로 JSON Patch 를 쓰거나 /items/- 로 append. Merge Patch 는 배열 전체만 교체할 수 있습니다.

관련 도구 & 읽을거리