조건부 쓰기로 객체 덮어쓰기 방지하기

조건부 쓰기로 객체 덮어쓰기 방지하기

같은 키 이름으로 객체를 다시 업로드하면 기존 객체가 조용히 덮어써져요. 이게 원하지 않는 상황이라면 조건부 쓰기(conditional write) 를 이용할 수 있어요. 조건부 쓰기를 쓰면 WRITE 요청에 추가 헤더를 넣어 S3 작업의 사전 조건을 지정할 수 있어요. 조건부로 객체를 쓰려면 HTTP If-None-Match 또는 If-Match 헤더를 추가하면 돼요.

If-None-Match 헤더는 버킷에 같은 키 이름을 가진 객체가 이미 있는지를 검증해 기존 데이터가 덮어써지는 것을 막아요. 반대로 If-Match 헤더를 쓰면 객체를 쓰기 전에 객체의 엔티티 태그(ETag)를 확인할 수 있어요. 이 헤더를 쓰면 S3가 제공한 ETag 값과 S3 안 객체의 ETag 값을 비교해요. 두 값이 일치하지 않으면 작업이 실패해요.

버킷 소유자는 버킷 정책을 사용해 업로드되는 객체에 조건부 쓰기를 강제할 수도 있어요. 자세한 내용은 S3 버킷에서 조건부 쓰기 강제하기를 참고하세요.

참고

조건부 쓰기를 사용하려면 AWS Signature Version 4로 요청에 서명해야 해요.

출처: 문서

본문

키 이름을 기준으로 객체 덮어쓰기 방지하기

HTTP If-None-Match 조건부 헤더를 쓰면 객체를 만들거나 대상 버킷으로 복사하기 전에, 지정한 버킷에 같은 키 이름의 객체가 이미 있는지를 확인할 수 있어요.

HTTP If-None-Match 헤더를 사용한 조건부 쓰기는 WRITE 작업 중에 객체가 존재하는지 확인해요. 버킷에 동일한 키 이름이 발견되면 작업이 실패해요. 이 헤더가 없다면, 버전 관리가 없는(또는 버전 관리가 중단된) 버킷에서 동일한 키 이름으로 객체를 업로드하거나 복사할 때 객체가 덮어써져요. 키 이름 사용에 대한 자세한 내용은 Amazon S3 객체 이름 짓기를 참고하세요.

참고

HTTP If-None-Match 헤더는 버전 관리 버킷에서 객체의 현재 버전에만 적용돼요.

HTTP If-None-Match 헤더로 조건부 쓰기를 하려면 s3:PutObject 권한이 있어야 해요. 그래야 호출자가 버킷 안 객체 존재 여부를 확인할 수 있어요. If-None-Match 헤더는 *(별표) 값을 기대해요.

If-None-Match 헤더는 다음 API에서 사용할 수 있어요.

다음 put-object 예시 명령은 dir-1/my_images.tar.bz2라는 키 이름의 객체에 대해 조건부 쓰기를 시도해요.

aws s3api put-object --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --body my_images.tar.bz2 --if-none-match "*"

자세한 내용은 _AWS CLI Command Reference_의 put-object를 참고하세요. AWS CLI에 대한 내용은 _AWS Command Line Interface User Guide_의 What is the AWS Command Line Interface?를 참고하세요.

다음 copy-object 예시 명령은 dir-1/my_images.tar.bz2라는 키 이름의 객체를 대상 버킷으로 조건부 쓰기 방식으로 복사하려고 시도해요.

aws s3api copy-object --copy-source amzn-s3-demo-bucket/key --key dir-1/my_images.tar.bz2 --bucket amzn-s3-demo-bucket2 --if-none-match "*"

자세한 내용은 _AWS CLI Command Reference_의 copy-object를 참고하세요. AWS CLI에 대한 내용은 _AWS Command Line Interface User Guide_의 What is the AWS Command Line Interface?를 참고하세요.

다음 complete-multipart-upload 예시 명령은 dir-1/my_images.tar.bz2라는 키 이름의 객체에 대해 멀티파트 업로드를 조건부 쓰기로 완료하려고 시도해요. 이 예시에서 file:// 접두사는 로컬 폴더의 mpustruct라는 파일에서 JSON 구조를 불러오는 데 쓰이고, 그 파일에는 이 특정 멀티파트 업로드를 위해 업로드된 모든 파트 목록이 들어 있어요.

aws s3api complete-multipart-upload --multipart-upload file://mpustruct --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --upload-id upload-id  --if-none-match "*"

자세한 내용은 _AWS CLI Command Reference_의 complete-multipart-upload를 참고하세요. AWS CLI에 대한 내용은 _AWS Command Line Interface User Guide_의 What is the AWS Command Line Interface?를 참고하세요.

객체가 변경되었다면 덮어쓰기 방지하기

객체의 ETag는 객체에 고유하면서 객체 내용의 변경을 반영하는 문자열이에요. If-Match 헤더를 쓰면 S3 버킷 안 객체의 ETag 값을 WRITE 작업 중에 여러분이 제공한 값과 비교할 수 있어요. 두 ETag 값이 일치하지 않으면 작업이 실패해요. ETag에 대한 자세한 내용은 Content-MD5와 ETag를 사용해 업로드 객체 검증하기를 참고하세요.

HTTP If-Match 헤더로 조건부 쓰기를 하려면 s3:PutObject와 s3:GetObject 권한이 있어야 해요. 그래야 호출자가 ETag를 확인하고 버킷 안 객체의 상태를 검증할 수 있어요. If-Match 헤더는 ETag 값을 문자열로 기대해요.

If-Match 헤더는 다음 API에서 사용할 수 있어요.

다음 put-object 예시 명령은 제공한 ETag 값 6805f2cfc46c0f04559748bb039d69ae로 조건부 쓰기를 시도해요.

aws s3api put-object --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --body my_images.tar.bz2 --if-match "6805f2cfc46c0f04559748bb039d69ae"

자세한 내용은 _AWS CLI Command Reference_의 put-object를 참고하세요. AWS CLI에 대한 내용은 _AWS Command Line Interface User Guide_의 What is the AWS Command Line Interface?를 참고하세요.

다음 copy-object 예시 명령은 제공한 ETag 값 6805f2cfc46c0f04559748bb039d69ae로 조건부 쓰기를 시도해요.

aws s3api copy-object --copy-source amzn-s3-demo-bucket/key --key dir-1/my_images.tar.bz2 --bucket amzn-s3-demo-bucket2 --if-match "6805f2cfc46c0f04559748bb039d69ae"

자세한 내용은 _AWS CLI Command Reference_의 copy-object를 참고하세요. AWS CLI에 대한 내용은 _AWS Command Line Interface User Guide_의 What is the AWS Command Line Interface?를 참고하세요.

다음 complete-multipart-upload 예시 명령은 제공한 ETag 값 6805f2cfc46c0f04559748bb039d69ae를 사용해 멀티파트 업로드를 조건부 쓰기로 완료하려고 시도해요. 이 예시에서 file:// 접두사는 로컬 폴더의 mpustruct라는 파일에서 JSON 구조를 불러오는 데 쓰이고, 그 파일에는 이 특정 멀티파트 업로드를 위해 업로드된 모든 파트 목록이 들어 있어요.

aws s3api complete-multipart-upload --multipart-upload file://mpustruct --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --upload-id upload-id --if-match "6805f2cfc46c0f04559748bb039d69ae"

자세한 내용은 _AWS CLI Command Reference_의 complete-multipart-upload를 참고하세요. AWS CLI에 대한 내용은 _AWS Command Line Interface User Guide_의 What is the AWS Command Line Interface?를 참고하세요.

조건부 쓰기 동작

If-None-Match 헤더를 사용한 조건부 쓰기·복사

If-None-Match 헤더를 사용한 조건부 쓰기는 버킷 안 기존 객체를 기준으로 평가해요. 버킷에 같은 키 이름의 기존 객체가 없다면 쓰기 작업이 성공하고 200 OK 응답을 받아요. 기존 객체가 있다면 쓰기 작업이 실패하고 412 Precondition Failed 응답을 받아요.

버전 관리가 활성화된 버킷에서 같은 이름의 현재 객체 버전이 없거나, 현재 객체 버전이 삭제 마커(delete marker)라면 쓰기 작업이 성공해요. 그 외에는 412 Precondition Failed 응답과 함께 쓰기 작업이 실패해요.

같은 객체 이름에 대해 여러 조건부 쓰기·복사가 동시에 발생하면, 먼저 끝나는 첫 번째 쓰기 작업이 성공해요. 그 뒤의 쓰기는 S3가 412 Precondition Failed 응답으로 실패시켜요.

그리고 동시 요청에서 조건부 쓰기 작업이 끝나기 전에 해당 객체에 대한 삭제 요청이 먼저 성공하면 409 Conflict 응답을 받을 수도 있어요. PutObject로 조건부 쓰기를 사용할 때는 409 Conflict 오류를 받은 뒤 업로드를 재시도할 수 있어요. CompleteMultipartUpload를 사용할 때는 409 Conflict 오류를 받은 뒤 객체를 다시 업로드하려면 CreateMultipartUpload로 멀티파트 업로드 전체를 다시 시작해야 해요.

If-Match 헤더를 사용한 조건부 쓰기·복사

If-Match 헤더는 버킷 안 기존 객체를 기준으로 평가해요. 같은 키 이름의 기존 객체가 있고 ETag가 일치하면 쓰기 작업이 성공하고 200 OK 응답을 받아요. ETag가 일치하지 않으면 쓰기 작업이 412 Precondition Failed 응답과 함께 실패해요.

동시 요청의 경우 409 Conflict 응답을 받을 수도 있어요.

만약 조건부 쓰기 작업이 끝나기 전에 동시 삭제 요청이 먼저 성공하면, 객체 키가 더 이상 존재하지 않으므로 404 Not Found 응답을 받아요. 404 Not Found 응답을 받으면 객체를 다시 업로드해야 해요.

같은 이름의 현재 객체 버전이 없거나, 현재 객체 버전이 삭제 마커라면 작업이 404 Not Found 오류와 함께 실패해요.

조건부 쓰기 시나리오

같은 버킷에서 두 클라이언트가 작업을 실행하는 다음 시나리오를 생각해 볼게요.

멀티파트 업로드 중의 조건부 쓰기

조건부 쓰기는 아직 완전히 쓰여지지 않은 객체이기 때문에 진행 중인 멀티파트 업로드 요청은 고려하지 않아요. 다음 예시를 볼게요. 클라이언트 1이 멀티파트 업로드로 객체를 업로드하고 있어요. 멀티파트 업로드가 진행되는 동안 클라이언트 2는 같은 객체를 조건부 쓰기 작업으로 성공적으로 쓸 수 있어요. 그 후 클라이언트 1이 조건부 쓰기를 사용해 멀티파트 업로드를 완료하려고 하면 업로드가 실패해요.

참고

이 시나리오는 If-None-Match와 If-Match 헤더 모두에 대해 412 Precondition Failed 응답이 나와요.

다음 예시는 같은 키 이름으로 항목을 쓰는 두 클라이언트를 보여줘요. 하나는 MPU에 UploadPart를 사용하고, 하나는 조건부 쓰기와 함께 PutObject를 사용해요. 그 뒤에 시작되는 CompleteMultipartUpload 작업은 실패해요.

멀티파트 업로드 중의 동시 삭제

조건부 쓰기 요청이 완료되기 전에 삭제 요청이 성공하면, S3는 쓰기 작업에 대해 409 Conflict 또는 404 Not Found 응답을 반환해요. 이는 먼저 시작된 삭제 요청이 조건부 쓰기 작업보다 우선하기 때문이에요. 이런 경우 새로운 멀티파트 업로드를 시작해야 해요.

참고

이 시나리오는 If-None-Match 헤더에 대해서는 409 Conflict 응답, If-Match 헤더에 대해서는 404 Not Found 응답이 나와요.

다음 예시는 멀티파트 업로드를 사용하는 클라이언트와, MPU가 시작된 뒤 삭제 요청을 보내는 클라이언트를 보여줘요. 삭제 요청은 조건부 쓰기가 시작되기 전에 끝나요.

참고

스토리지 비용을 최소화하려면 AbortIncompleteMultipartUpload 작업을 사용해 지정된 기간(일) 후에 완료되지 않은 멀티파트 업로드를 삭제하는 수명 주기 규칙을 구성하는 것을 권장해요. 완료되지 않은 멀티파트 업로드를 삭제하는 수명 주기 규칙 만들기에 대한 자세한 내용은 완료되지 않은 멀티파트 업로드를 삭제하도록 버킷 수명 주기 구성하기를 참고하세요.

더 알아보기 (Learn more)