1. 라이브러리
  2. HTTP와 웹
  3. HTTP 메서드

업데이트됨 1개월 전

모든 개발자는 결국 같은 질문을 하게 됩니다: 모든 필드를 포함하지 않고 업데이트를 보내면, 빠진 필드들은 어떻게 될까요?

그 답은 전적으로 PUT을 사용하느냐 PATCH를 사용하느냐에 달려 있습니다.

PUT은 "이것이 지금의 전체 진실입니다"라고 말합니다. 보내는 내용이 기존 내용을 대체합니다. 빠진 필드는 사라지거나 기본값으로 초기화됩니다. 업데이트가 아니라 덮어쓰기입니다.

PATCH는 "이것들만 바꾸고 나머지는 그대로 두세요"라고 말합니다. 포함한 필드만 수정됩니다. 나머지는 있는 그대로 유지됩니다.

이 구분은 단순해 보이지만, 요청을 구조화하는 방식, 서버가 처리하는 방식, 그리고 깜빡 잊은 필드 하나가 운영 장애를 일으킬 수 있는지를 결정합니다.

PUT: 완전한 교체

PUT은 리소스 전체를 전송합니다. 서버는 병합하지 않고 교체합니다.

예를 들어 사용자 프로필이 있다고 가정해 보겠습니다:

{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "bio": "Software developer",
  "location": "New York"
}

PUT으로 이메일을 업데이트하려면:

PUT /users/123 HTTP/1.1
Content-Type: application/json

{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com",
  "bio": "Software developer",
  "location": "New York"
}

변경되지 않은 필드까지 모두 포함해야 합니다. 만약 다음과 같이만 보낸다면:

{
  "id": 123,
  "email": "john.doe@example.com"
}

name, bio, location 필드는 모두 지워집니다. 이것은 버그가 아닙니다. PUT이 갖는 의미가 바로 이것입니다: 보내는 내용이 곧 새로운 완전한 상태입니다.

PATCH: 정밀 수정

PATCH는 변경이 필요한 것만 전송합니다:

PATCH /users/123 HTTP/1.1
Content-Type: application/json

{
  "email": "john.doe@example.com"
}

서버는 이메일만 수정하고 나머지는 그대로 유지합니다. 결과:

{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com",
  "bio": "Software developer",
  "location": "New York"
}

PATCH는 의도를 더 정확하게 표현합니다: "이 특정 항목을 변경하세요." 요청 크기가 작고, 유지하려던 필드를 실수로 지울 위험도 없습니다.

멱등성 문제

PUT은 항상 멱등적입니다. 동일한 PUT 요청을 열 번 보내도 결과는 같습니다—리소스는 지정한 상태 그대로 유지됩니다. 덕분에 네트워크 요청이 실패했을 때 PUT을 안전하게 재시도할 수 있습니다.

PATCH는 좀 더 까다롭습니다. 특정 값으로 필드를 설정하는 것은 멱등적입니다:

{"email": "new@example.com"}

이것을 백 번 적용해도 이메일은 여전히 new@example.com입니다.

하지만 상대적인 변경은 멱등적이지 않습니다:

{"balance": {"increment": 10}}

이것을 두 번 적용하면 10이 아니라 20이 더해집니다. PATCH의 멱등성 여부는 무엇을 어떻게 패치하느냐에 따라 달라집니다.

각각 언제 사용할까

PUT을 사용할 때:

  • 리소스 전체를 교체해야 하는 경우
  • 클라이언트가 현재 완전한 상태를 불러와서 수정 사항을 다시 보내는 경우
  • 멱등성이 보장되어야 하는 경우
  • 설정 값처럼 전체가 한 단위인 객체의 경우

PATCH를 사용할 때:

  • 클라이언트가 일반적으로 한두 개의 필드만 수정하는 경우
  • 리소스가 크고 전체를 보내는 것이 대역폭 낭비인 경우
  • 여러 클라이언트가 동시에 다른 필드를 업데이트할 수 있는 경우
  • 무엇이 변경되는지를 요청 자체가 정확하게 표현하길 원하는 경우

PATCH 형식

리소스의 표준 형태를 사용하는 PUT과 달리, PATCH는 다양한 방식으로 변경 사항을 기술할 수 있습니다.

JSON Merge Patch (RFC 7396)가 가장 단순합니다: 업데이트하려는 필드가 담긴 객체를 보냅니다. 필드를 null로 설정하면 삭제됩니다. 필드를 생략하면 변경되지 않습니다.

{
  "email": "new@example.com",
  "bio": null
}

이렇게 하면 이메일이 업데이트되고 bio가 삭제됩니다. 단순하지만, "이 필드를 null로 설정"과 "이 필드를 삭제"를 구분할 수 없습니다—둘 다 null을 사용합니다.

JSON Patch (RFC 6902)는 명시적인 작업의 배열을 사용합니다:

[
  {"op": "replace", "path": "/email", "value": "new@example.com"},
  {"op": "remove", "path": "/bio"},
  {"op": "add", "path": "/tags/-", "value": "verified"}
]

더 강력합니다—배열에 항목을 추가하거나, 값을 이동하거나, 변경을 적용하기 전에 조건을 테스트할 수도 있습니다—하지만 구현이 더 복잡합니다.

대부분의 API는 단순함 때문에 JSON Merge Patch를 사용합니다. 배열에 대한 정밀한 제어나 조건부 업데이트가 필요할 때 JSON Patch를 선택하세요.

동시 수정 처리

PATCH는 경쟁 조건 위험을 만들어냅니다. 두 클라이언트가 같은 리소스를 읽고, 둘 다 패치를 보내면, 두 번째가 첫 번째의 변경 사항을 그 존재조차 모른 채 덮어쓰게 됩니다.

해결책은 ETag를 이용한 낙관적 잠금입니다:

PATCH /users/123 HTTP/1.1
If-Match: "abc123"
Content-Type: application/json

{"email": "new@example.com"}

서버는 리소스의 ETag가 여전히 일치하는 경우에만 패치를 적용합니다. 다른 누군가가 먼저 수정했다면 412 Precondition Failed를 받게 되고, 다시 불러와서 병합한 뒤 재시도할 수 있습니다.

PUT도 같은 문제를 가지고 있지만, 완전한 상태를 보내기 때문에 상대적으로 덜 심각합니다. 누군가의 변경 사항을 덮어쓸 수도 있지만, 적어도 리소스가 예상치 못한 불완전한 상태로 남지는 않습니다.

리소스 생성

PUT은 클라이언트가 지정한 URL에 리소스를 생성할 수 있습니다:

PUT /users/johndoe HTTP/1.1

{"name": "John Doe", "email": "john@example.com"}

/users/johndoe가 존재하지 않으면 생성됩니다. 존재하면 교체됩니다. 이 멱등적 생성은 클라이언트가 사용자 이름, SKU, UUID 같은 의미 있는 식별자를 직접 지정할 수 있을 때 유용합니다.

PATCH는 일반적으로 기존 리소스만 수정합니다. 존재하지 않는 리소스를 패치하려 하면 404를 반환합니다.

응답 코드

PUT과 PATCH 모두 일반적으로 다음을 반환합니다:

  • 200 OK: 성공, 응답에 업데이트된 리소스 포함
  • 204 No Content: 성공, 응답 본문 없음 (클라이언트가 새 상태를 추론)
  • 400 Bad Request: 잘못된 형식의 요청
  • 404 Not Found: 리소스가 존재하지 않음
  • 409 Conflict: 업데이트가 현재 상태와 충돌
  • 412 Precondition Failed: ETag 불일치

PUT은 새 리소스를 생성할 때 201 Created를 반환하기도 합니다.

응답에 업데이트된 리소스를 포함하면 이후의 GET 요청을 절약할 수 있습니다. 204를 반환하면 대역폭을 절약할 수 있습니다. 어느 방식이든 작동하지만, 일관성을 유지하세요.

PUT 대 PATCH에 관해 자주 묻는 질문

PUT을 사용하다가 필드를 빠뜨리면 어떻게 되나요?

해당 필드가 제거되거나 기본값으로 초기화됩니다. PUT은 교체를 의미합니다—요청에 필드가 없으면 이후의 리소스에도 없습니다. 이것이 개발자들이 부분 업데이트에 PUT을 사용할 때 가장 흔히 하는 실수입니다.

PATCH도 PUT처럼 새 리소스를 생성할 수 있나요?

일반적으로 아닙니다. PATCH는 기존 리소스를 수정하기 위해 설계되었습니다. 대부분의 API는 존재하지 않는 리소스를 PATCH하려 하면 404를 반환합니다. 생성에는 PUT이나 POST를 사용하세요.

API가 둘 다 지원해야 하나요?

네. 전체 리소스를 교체하려는 클라이언트를 위한 PUT, 정밀 업데이트를 원하는 클라이언트를 위한 PATCH. 하나만 선택해야 한다면 PATCH가 보통 더 실용적입니다—부분 업데이트가 일반적인 경우이기 때문입니다.

PATCH가 항상 PUT보다 효율적인가요?

변경이 작은 큰 리소스의 경우 그렇습니다. 리소스가 작거나 대부분의 필드를 업데이트하는 경우에는 차이가 미미합니다. 복잡한 변경의 경우 JSON Patch는 오히려 리소스 전체를 보내는 것보다 더 장황해질 수 있습니다.

이 페이지가 도움이 되었나요?

😔
🤨
😃
PUT 대 PATCH • 라이브러리 • Connected