달팽로그

PATCH, 부분 수정 API를 안전하게 설계하는 법

2026-07-24 · 기타

PATCH, 부분 수정 API를 안전하게 설계하는 법

PATCH 문서 형식과 ETag를 활용해 안전한 부분 수정 API를 설계하는 흐름

PATCH는 이미 존재하는 리소스에서 필요한 부분만 수정할 때 사용하는 HTTP 메서드입니다. 사용자 프로필에서 닉네임만 바꾸거나, 주문 정보의 배송 메모만 고치는 경우가 대표적입니다.

다만 PATCH API는 단순히 JSON 일부를 보내는 기능으로 끝나지 않습니다. 어떤 형식으로 변경 내용을 표현할지, 동시에 수정한 요청이 충돌하면 어떻게 처리할지, 누가 어떤 필드를 바꿀 수 있는지까지 함께 정해야 합니다. 이 기준이 정리되어 있어야 PATCH API를 오래 안정적으로 운영할 수 있습니다.

핵심: PATCH의 목적은 “적은 데이터를 보낸다”는 데만 있지 않습니다. 변경하려는 범위와 의도를 API에 분명하게 담는 데 의미가 있습니다.

PATCH와 PUT, POST는 무엇이 다를까요?

리소스를 수정한다는 점에서는 비슷해 보여도 PATCH, PUT, POST는 전달하려는 의미가 다릅니다. 엔드포인트를 설계할 때는 데이터의 변화인지, 업무 동작의 실행인지를 먼저 구분하는 편이 좋습니다.

메서드 주된 용도 핵심 특징
PATCH 리소스 일부 수정 변경할 부분 또는 변경 명령을 전달
PUT 리소스 전체 교체 요청 본문을 리소스의 새 전체 상태로 취급
POST 생성·명령 실행 업무 동작 자체를 표현할 때 적합

예를 들어 사용자 정보가 다음과 같다고 가정해 보겠습니다.

{ "name": "민지", "email": "[email protected]", "marketingConsent": false }

여기서 닉네임만 변경한다면 전체 객체를 전달하는 PUT보다 PATCH가 변경 의도를 더 명확하게 보여줄 수 있습니다. 반대로 수정할 때마다 리소스 전체 상태를 확실하게 보낼 수 있고, 리소스 자체도 작다면 PUT이 더 단순할 수 있습니다.

비밀번호 변경, 결제 승인처럼 단순한 필드 수정으로 보기 어려운 업무 동작도 있습니다. 이런 경우에는 PATCH /users/1보다 POST /users/1/password-change처럼 명령의 성격을 드러내는 URL이 읽기 쉬울 때가 많습니다.

PATCH API에서 많이 쓰는 두 가지 문서 형식

HTTP PATCH 자체는 기본 JSON 형식을 정하지 않습니다. 따라서 서버는 어떤 패치 형식을 지원하는지 Content-Type으로 명확히 받아야 하며, 클라이언트도 그 규칙에 맞춰 요청을 구성해야 합니다.

1. JSON Merge Patch: 간단한 객체 수정

JSON Merge Patch는 application/merge-patch+json을 사용합니다.

PATCH /users/42 Content-Type: application/merge-patch+json { "name": "민지", "marketingConsent": true }

위 요청에서는 포함하지 않은 email은 그대로 두고, 요청 본문에 담긴 필드만 변경합니다. 프로필이나 설정값처럼 일반적인 객체의 일부 값을 수정하는 PATCH API에 잘 어울립니다.

다만 null의 의미에는 주의해야 합니다. JSON Merge Patch에서 null은 보통 “null 값을 저장한다”는 뜻이 아니라 해당 멤버의 삭제를 의미합니다. 또한 배열을 보내면 배열 전체를 교체하므로, 태그 하나를 바꾸려는 상황에서는 의도하지 않은 값까지 덮어쓰지 않도록 확인해야 합니다.

2. JSON Patch: 배열과 세밀한 변경

JSON Patch는 application/json-patch+json을 사용합니다.

PATCH /users/42 Content-Type: application/json-patch+json [ { "op": "replace", "path": "/name", "value": "민지" }, { "op": "add", "path": "/tags/-", "value": "seoul" } ]

JSON Patch는 add, remove, replace, move, copy, test 같은 작업을 배열로 표현합니다. 배열 끝에 새 값을 넣거나 특정 경로만 정확하게 바꿔야 할 때 특히 유용합니다.

간단한 객체 수정에 적합한 JSON Merge Patch와 배열 변경에 적합한 JSON Patch 비교

대신 JSON Patch는 경로 표기와 작업 순서를 엄격하게 검증해야 합니다. 클라이언트 역시 패치 명령을 직접 만들어야 하므로, 단순한 폼 수정이라면 JSON Merge Patch가 더 실용적인 선택일 수 있습니다.

상황 검토할 형식
프로필·설정의 일부 필드 수정 JSON Merge Patch
배열의 특정 항목 추가·삭제·이동 JSON Patch
전체 리소스 상태를 교체 PUT
승인·변경 요청 같은 업무 동작 POST 기반 명령 엔드포인트

PATCH는 멱등일까요?

PATCH는 HTTP 표준상 기본적으로 안전한 메서드도, 멱등 메서드도 아닙니다. 같은 요청을 두 번 전송했을 때 결과가 달라질 수 있기 때문입니다.

예를 들어 아래 JSON Patch 요청은 배열 끝에 태그를 추가합니다.

[ { "op": "add", "path": "/tags/-", "value": "seoul" } ]

네트워크 문제로 요청을 재시도하면 seoul 태그가 한 번 더 추가될 수 있습니다. 사용자는 한 번만 눌렀는데 서버 상태는 두 번 변경되는 상황이 생길 수 있는 것입니다.

가능하다면 “값을 10으로 설정한다”처럼 같은 요청을 반복해도 결과가 유지되는 방식으로 PATCH API를 설계하는 것이 좋습니다. 포인트 증감, 결제, 재고처럼 누적 효과가 있는 작업은 별도의 명령 엔드포인트와 멱등성 키를 검토하는 편이 안전합니다.

네트워크 재시도 시 중복 변경을 막기 위한 멱등성 설계 비교

동시 수정 충돌은 ETag와 If-Match로 방어합니다

두 사람이 같은 프로필 화면을 열고 수정하는 경우를 생각해 보겠습니다. 먼저 저장한 사람의 변경 내용이 있어도, 나중에 도착한 요청이 이를 덮어쓸 수 있습니다. PATCH API에서 이런 문제를 줄이려면 조회 응답의 ETag를 받고 수정 요청에 If-Match를 포함하는 낙관적 잠금 방식을 사용할 수 있습니다.

PATCH /users/42 If-Match: "v17" Content-Type: application/merge-patch+json

서버의 현재 버전이 v17이 아니라면 요청을 412 Precondition Failed로 거절할 수 있습니다. 클라이언트는 최신 데이터를 다시 조회하고, 사용자에게 충돌 사실을 안내할 수 있습니다.

현재 상태를 전제로 변경하는 PATCH API라면 ETag와 If-Match는 오래된 요청이 최신 데이터를 덮어쓰는 일을 막는 유용한 방어 장치입니다.

ETag와 If-Match를 비교해 오래된 수정 요청을 412 오류로 차단하는 동시 수정 방어

PATCH 서버 구현에서 놓치기 쉬운 점

PATCH API는 적용할 필드가 적더라도 검증해야 할 조건이 적지 않습니다. 특히 외부에서 들어온 패치 문서를 그대로 데이터 모델에 반영하지 않도록 주의해야 합니다.

  • 패치 전체를 적용할 수 없다면 일부만 반영하지 않도록 원자적으로 처리합니다.
  • 수정 가능한 필드는 허용 목록으로 제한합니다. role, isAdmin, price 같은 민감 필드는 임의 변경을 막아야 합니다.
  • 형식이 틀리면 400, 지원하지 않는 패치 형식이면 415를 사용합니다.
  • 상태 충돌은 상황에 따라 409 또는 412를 일관되게 사용합니다.
  • 검증 실패 이유는 구조화된 응답으로 제공해 프런트엔드가 처리하기 쉽게 만듭니다.
  • 필요하다면 OPTIONS 응답의 Accept-Patch 헤더로 지원하는 패치 형식을 알립니다.

실무 팁: PATCH 요청을 처리할 때는 “전달된 필드가 수정 가능한가”, “값의 형식이 맞는가”, “현재 리소스 상태와 충돌하지 않는가”를 순서대로 점검하면 구현과 테스트 기준을 세우기 편합니다.

PATCH를 선택하기 전 체크리스트

리소스의 일부 필드만 변경하고, 변경 대상 URL도 분명하다면 PATCH를 우선 검토할 수 있습니다. 다만 PATCH라는 이름만 보고 선택하기보다 실제 변경 방식과 재시도 가능성, 동시 수정 가능성을 함께 살펴야 합니다.

  1. 전체 리소스가 아니라 일부 필드만 수정하는가?
  2. 객체의 일부 값을 바꾸는 일인가, 배열 요소를 세밀하게 조작하는 일인가?
  3. 같은 요청이 재시도되어도 안전한가?
  4. 여러 사용자가 같은 리소스를 수정할 가능성이 있는가?
  5. 수정할 수 없는 민감 필드를 명확히 막았는가?

객체의 몇 가지 필드를 간단히 수정한다면 JSON Merge Patch가 잘 맞습니다. 배열 요소의 이동·삭제처럼 정교한 변경이 필요하면 JSON Patch를 검토할 수 있습니다.

반대로 전체 상태를 매번 명확하게 전달할 수 있다면 PUT이 단순합니다. “승인하기”, “발송 요청하기”, “비밀번호 변경하기”처럼 업무 동작 자체가 핵심이라면 POST 기반 명령 엔드포인트가 더 읽기 좋을 수 있습니다.

PATCH API 자주 묻는 질문

PATCH는 무조건 PUT보다 효율적인가요?

아닙니다. 전송량이 줄 수는 있지만 패치 형식 해석과 충돌 처리 비용이 생깁니다. 전체 리소스가 작거나 패치 표현이 더 길다면 PUT이 더 단순할 수 있습니다.

PATCH 요청에 application/json만 쓰면 안 되나요?

서버와 클라이언트가 자체 규칙을 명확히 합의했다면 가능합니다. 다만 JSON Merge Patch나 JSON Patch처럼 의미와 미디어 타입이 표준화된 형식을 사용하면 다른 개발자가 동작을 이해하고 연동하기 쉬워집니다.

PATCH 성공 응답은 꼭 200이어야 하나요?

아닙니다. 수정된 리소스를 응답 본문에 반환하면 200 OK, 본문 없이 성공만 알리면 204 No Content를 자주 사용합니다. 팀의 API 규칙에 맞춰 일관되게 정하는 것이 중요합니다.