팀에서 음원 파일이나 폴더를 R2에 올리는 공용 명령을 만들었습니다. 폴더를 올리다 중간에 연결이 끊기면 같은 명령을 다시 실행하고 싶습니다. 그런데 같은 키에 일반 PUT을 보내면, 이미 올라간 파일이 지금 로컬에 있는 내용으로 바뀔 수 있습니다. 기본 실행은 빈 키만 채우고, 기존 파일을 바꿀 때는 교체 옵션을 명시하도록 했습니다.
비어 있는 키에만 쓰기
먼저 HEAD로 키가 비어 있는지 확인하고 뒤이어 PUT을 보내면, 두 요청 사이에 다른 작업자가 같은 키에 파일을 올릴 수 있습니다. 저는 기본 PUT에 If-None-Match: *를 넣었습니다. R2는 이 조건을 객체 쓰기와 함께 판단합니다. 키가 이미 있으면 412 PreconditionFailed가 돌아오고, 도구는 그 파일을 건너뛰었다고 표시한 뒤 다음 파일로 넘어갑니다. --overwrite를 준 경우에는 이 조건을 붙이지 않습니다. Cloudflare R2의 조건부 PUT 문서
파일마다 PUT을 보내므로 중간에 실패하기 전에 올린 파일은 남습니다. 다시 실행하면 그 키를 건너뛰고 나머지 파일을 올릴 수 있습니다. 건너뛰었다는 결과는 해당 키가 이미 차 있었다는 뜻입니다. 내용까지 같은지 확인해야 한다면 바이트나 신뢰할 수 있는 해시를 따로 비교해야 합니다.
재실행할 때 어떤 버킷을 쓰는지도 분명해야 했습니다. 환경파일을 지정하면 그 파일에서 필요한 키만 읽고, 빠진 값은 오류로 처리합니다. 파일의 따옴표와 주석은 문자열 파서로 읽으며, 명령 치환처럼 보이는 테스트 값도 문자열 그대로 들어오는지 확인했습니다. 이렇게 두면 설정파일을 읽는 동안 셸이 명령을 실행하거나, 누락된 값이 다른 환경에서 조용히 섞이지 않습니다.
같은 키로 PUT 세 번 검증
검증에서는 R2 대신 로컬 HTTP 서버가 객체를 보관하게 했습니다. 업로드 도구는 실제 boto3 클라이언트로 서명한 요청을 보냈습니다. 같은 파일 경로에서 내용을 바꿔 가며 명령을 세 번 실행했습니다.
| 실행 | 저장소 대역의 응답 | 요청 뒤 같은 키의 내용 |
|---|---|---|
| 원본을 처음 업로드 | HTTP 200 | 원본 |
| 로컬 파일을 바꾼 뒤 기본 옵션으로 재실행 | HTTP 412 | 원본 유지 |
| 바뀐 파일에 교체 옵션을 명시 | HTTP 200 | 새 파일로 교체 |
서버에 기록된 PUT은 3건이었습니다. 두 번째 실행 뒤에는 저장소 대역의 바이트가 원본 그대로였고, 세 번째 실행에서만 바뀌었습니다. 요청 헤더의 서명 형식과 본문의 MD5, 메타데이터에 담긴 SHA-256도 서버가 받은 바이트와 대조했습니다. 조건부 요청과 교체 요청의 차이를 CLI 출력과 저장된 값 양쪽에서 확인한 셈입니다.
이 검증에서 직접 확인한 범위는 실제 SDK가 만든 HTTP 요청과 도구의 응답 처리입니다. 운영 버킷의 접근 권한과 실제 업로드는 당시 실행 범위에 포함하지 않았습니다.

댓글
댓글과 반응은 GitHub 계정으로 로그인해 남길 수 있어요.