
회원가입 기능을 구현할 때, 들어가는 정보로 이메일, 비밀번호, 닉네임 그리고 프로필 사진을 넣기로 했다. 이미지 storage는 AWS S3을 사용하기로 했고, 이에 맞는 ImageService를 만들어 회원가입 기능을 구현했다.
API를 구현하고 Swagger에서 이미지 파일이 S3 버킷에 잘 저장되나 테스트하고 있었는데, 같은 이미지 파일로 두 번 회원가입을 했는데도 버킷에는 이미지 파일이 하나만 있었다.



Amazon S3의 PutObject API 문서를 보면 아래 내용을 찾을 수 있다.
Amazon S3 is a distributed system. If it receives multiple write requests for the same object simultaneously, it overwrites all but the last object written.
Amazon S3는 분산 시스템입니다. 동일한 객체에 대해 여러 쓰기 요청을 동시에 받으면 마지막으로 작성된 객체를 제외한 모든 객체를 덮어씁니다.
User Guide 문서에서도 동일한 내용이 있다.
If an object with the same object key already exists in the bucket, Amazon S3 replaces the existing object with the newly uploaded object.
미리 서명된 URL에 지정된 것과 동일한 키를 가진 객체가 버킷에 이미 있는 경우, Amazon S3는 기존 객체를 업로드된 객체로 대체합니다.
파일 구분을 key로 하는데, 이 key가 결국 object 저장 시 지정하는 이름이었다.
그리고 같은 key에 대해 여러 번 업로드 요청이 발생할 경우, 가장 최신 요청으로 대체된다는 것이다.
덮어씌워진다는 의미이다.
기존에 S3로 파일을 업로드 하는 방법은 아래와 같았다.
public String saveImage(MultipartFile multipartFile){
if(multipartFile == null) return null;
String fileName = multipartFile.getOriginalFilename(); // 이미지 파일 이름 그대로 지정
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(multipartFile.getSize());
metadata.setContentType(multipartFile.getContentType());
try {
amazonS3.putObject(bucket, fileName, multipartFile.getInputStream(), metadata);
} catch (IOException e) {
throw new RuntimeException(e);
}
return amazonS3.getUrl(bucket, fileName).toString();
}
만약 'profileImage.png'를 업로드하면 AWS S3에 그대로 'profileImage.png'라는 key로 이미지 object가 저장되는 것이다.
지금도 'profileImage.png'라는 key로 두 번 업로드 요청을 해서 덮어씌워진 것이었다.
지금 상태로는 두 회원이 하나의 프로필 사진 파일을 공유하게 되는 것이므로,
만약 한 쪽에서 프로필 사진을 삭제하게 된다면 나머지 한 회원도 영향을 받게 된다.
절대 있으면 안되는 일이므로! 같은 이미지 파일이어도 중복 저장 가능하게 만들 방법이 필요하다.
생각한 방법으로는 기존의 파일명에 겹치지 않을 법한 string을 붙여서 저장하는 것이다.
처음엔 모든 유저가 겹칠 수 없는 정보로 email을 이미지 파일명 앞에 붙일까 했는데, 이보단 random UUID를 사용해서 중복되지 않는 key를 만드는게 확실한 방법일 것이라 판단했다.
public String saveImage(MultipartFile multipartFile){
if(multipartFile == null) return null;
String originalImageName = multipartFile.getOriginalFilename();
String fileName = UUID.randomUUID().toString().concat(Objects.requireNonNull(originalImageName));
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(multipartFile.getSize());
metadata.setContentType(multipartFile.getContentType());
try {
amazonS3.putObject(bucket, fileName, multipartFile.getInputStream(), metadata);
} catch (IOException e) {
throw new RuntimeException(e);
}
return amazonS3.getUrl(bucket, fileName).toString();
}
랜덤한 UUID를 기존 파일명 앞에 붙여서 업로드 하기로 했다. 저장된 프로필 이미지 url을 회원 DB에 올려두면 끝!


이제 같은 profileImage.png로 회원가입을 진행해도 두 개의 파일이 중복 저장 가능하게 됐다!
참고
https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html?utm_source=chatgpt.com
https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html?utm_source=chatgpt.com