REST API를 설계하다 보면 잠깐 멈칫하게 되는 메서드가 있는 것 같습니다.
바로 PATCH인데요.👀
GET/POST/PUT/DELETE와 달리 “부분 수정”이라는 설명만으로는 실제 사용 기준이 모호하고, 여러 표준이 공존해 선택지가 많습니다.
이 글에서는 PATCH가 왜 애매하게 느껴지는지, PUT과 무엇이 다른지, 그리고 실무에서 어떤 방식으로 선택해 사용했는지까지 정리해 보려고 합니다.
PATCH가 애매한 이유
- PATCH는 HTTP/1.1(RFC 5789)에 정의된 부분 수정(partial update) 용 메서드입니다.
- “부분 수정”이라는 말이 열려 있어 다음과 같은 질문이 생깁니다.
- 어떤 필드까지 수정 가능해야 하는가?
- 없는 필드가 오면 어떻게 처리해야 하는가?
null은 변경인가 삭제인가? - PUT과의 경계는 어디까지로 볼 것인가?
- 스펙이 구현 방법을 강제하지 않아 팀마다 사용 방식이 달라지기 쉽습니다.
- 특히 다음 세 가지가 애매함을 키웁니다.
- 표현 방식의 다중 표준: JSON Merge Patch, JSON Patch 등 Content-Type별 규칙이 달라 어떤 포맷을 선택할 지부터 정해야 합니다.
- 부분 수정 범위의 불명확성: 누락 필드는 유지인지 삭제인지,
null은 값 설정인지 제거인지 같은 해석을 팀이 합의해야 합니다. - 멱등성/원자성 해석 차이: PATCH는 멱등성이 구현에 따라 달라지고, 여러 연산을 한 요청에 묶을 때 실패 시 롤백 여부 등 원자성 정책도 명확히 정해야 합니다.
PUT vs PATCH
| 구분 | PUT | PATCH |
|---|---|---|
| 목적 | 리소스 전체 교체 | 리소스 부분 수정 |
| 요청 바디 | 전체 리소스 표현 | 변경하려는 필드만 |
| 누락 필드 | 제거될 수 있음 | 그대로 유지 |
| 멱등성 | O | 구현에 따라 다름 |
- PUT은 “이 리소스를 이 상태로 만들어라” 라는 의미에 가깝습니다.
- PATCH는 “이 리소스에 이 변경을 적용해라” 라는 의미에 가깝습니다.
- PATCH에서 변경을 어떻게 표현할지에 대한 명확한 기준이 없다는 점이 애매함의 핵심이라고 생각합니다.
PATCH 표현 방식 두 가지
(표현 방식에 맞춰 Content-Type을 명시하는 게 헷갈림을 줄입니다. 대부분 application/json으로 받기도 하지만, 표준은 merge patch → application/merge-patch+json, json patch → application/json-patch+json입니다.)
1) JSON Merge Patch (application/merge-patch+json)
{
"name": "new-name",
"email": null
}
- 동작: 요청에 포함된 필드만 기존 리소스에 병합합니다.
포함되지 않은 필드는 그대로 유지합니다.null은 보통 해당 필드를 제거(또는 비활성화)로 해석합니다. - 강점: 구조가 단순하고 DTO 기반으로 쉽게 구현할 수 있어 애플리케이션 레벨에서 빠르게 적용 가능합니다.
- 한계: 깊은 중첩/배열을 세밀하게 조작하기엔 표현력이 부족합니다.
`null`의 의미를 팀이 명확히 합의해야 합니다.
2) JSON Patch (application/json-patch+json)
[
{ "op": "replace", "path": "/name", "value": "new-name" },
{ "op": "remove", "path": "/email" }
]
- 동작:
op/path/value로 변경 작업을 순서대로 명시합니다.add,remove,replace,move,copy,test등의 연산을 지원합니다. - 강점: 배열·중첩 구조까지 세밀하게 지정할 수 있고, 한 요청에 여러 연산을 묶어 순차 적용할 수 있습니다.
- 한계: path 검증, 파싱, 예외 처리, 실패 시 롤백 정책 등을 별도로 설계해야 하며, 클라이언트·서버 모두 동일한 규칙을 이해해야 합니다.
주로 사용하게 된 Merge Patch
- 실무에서는 요구사항이 단순했기 때문에 Merge Patch로 충분했습니다.
주요 패턴은 필드 값 변경, on/off,null을 통한 비활성화였습니다. - 일반 DTO로 처리할 수 있어 구현과 유지보수 비용이 낮았습니다.
@PatchMapping("/users/{id}") public void updateUser(@RequestBody UpdateUserRequest request) { ... } - JSON Patch는 별도의 파서, path 유효성 검증, 실패 시 에러 규칙을 설계해야 해서 팀 단위 유지보수 비용이 커집니다.
- 표준을 모두 아는 것과 서비스에 맞게 선택하는 것은 다릅니다. 우리는 “명확하고 예측 가능한 API”를 우선해 Merge Patch를 채택했습니다.
- 다만 팀 컨벤션과 요구사항 복잡도에 따라 달라질 수 있습니다. 깊은 중첩/배열을 세밀하게 다루거나 여러 연산을 한 번에 표현해야 하면 JSON Patch가 더 적합할 수 있습니다.
언제 JSON Patch를 고려할까?
- 배열이나 중첩 구조를 정밀하게 조작해야 하는 경우: 특정 index 삽입/삭제, 깊은 path 수정 등.
ex) TODO 리스트에서 3번째 항목을 1번째로 이동, 중첩 설정 객체의 일부만 제거. - 한 요청에 여러 연산을 순서대로 묶어야 하는 경우: add → replace → remove처럼 순차적 패치를 명시해야 할 때.
(Merge Patch에서도 add/modify 필드를 나눠 흉내 낼 수 있지만, JSON Patch는 그 흐름이 표준화되어 있고 클라이언트·서버가 동일한 표현을 공유하기 쉽습니다.) - 변경 전 상태를 확인하는 조건부 업데이트가 필요한 경우:
test연산으로 사전 검증 후 적용.
ex) 특정 버전/etag일 때만 replace, 값이 X일 때만 remove. - 외부에 공개되는 범용/플러그인형 API처럼 표준화된 패치 포맷을 요구하는 경우.
- 문서 기반 리소스(설정/정책 문서 등)처럼 구조가 자주 변하고 호환성을 유지해야 하는 경우.
ex) 사용자 정의 스키마를 가진 설정 문서, 정책 JSON을 여러 클라이언트가 패치해야 하는 경우.
PATCH가 애매한 이유는 메서드가 모호해서라기보다, 구현 방식이 너무 열려 있기 때문입니다.
결국 팀이 무엇을 우선순위로 두느냐가 선택을 가릅니다.
우리처럼 단순한 요구라면 Merge Patch 하나로 충분히 깔끔하게 갈 수 있고, 구조가 복잡하거나 표준화된 패치 표현을 외부에 열어야 한다면 JSON Patch가 힘을 발휘합니다.
중요한 건 두 표준을 모두 아는 것보다, 서비스에 맞춰 고르게 쓸 수 있어야 할 것 같습니다!
'Web > HTTP' 카테고리의 다른 글
| 쿠키(Cookie)와 세션(Session) (0) | 2024.11.03 |
|---|---|
| Header Referer를 아시나요? (0) | 2024.07.30 |
| HTTP Method (GET, POST, PUT, DELETE, ...) (0) | 2024.01.19 |
| REST, RESTful 그리고 REST API, RESTful API (3) | 2024.01.03 |