Post

TIL(20240814) [트러블 슈팅: 중복된 이미지이름일 경우 이미지 덮어쓰기 문제발생]

TIL(20240814) [트러블 슈팅: 중복된 이미지이름일 경우 이미지 덮어쓰기 문제발생]

8월 14일 (수) ~ 8월 18일 (일) 까지 사용자 테스트 기간

그러던 중 팀원분이 인증요청시에 제목 없음.jpg 파일을 여러장 업로드 했을때 S3 버킷에는 해당 파일의 이름으로 계속 덮어쓰여지는 문제가 발생하는 것 같다고 알려주셨다.

그러니까 제목없음.jpg 파일로 3개 올렸을 때 각 다른 이미지 이지만 버킷에는 제목없음.jpg 파일이 한 개만 있는 상태가 되어버린 것이다.

위 문제를 듣고 떠올렸던 건 고유한 식별자를 만들 때 사용하는 UUID이다.

💡 UUID

  • UUID는 ‘Universally Unique Identifier’의 약자로 128-bit의 고유 식별자로, 다른 고유 ID 생성 방법과 다르게 UUID는 중앙 시스템에 등록하고 발급하는 과정이 없어서 상대적으로 더 빠르고 간단하게 만들 수 있다는 장점이 있다.

  • (단점)하지만 완전히 고유하지 않을 확률이 있는데, RFC 4122 문서에 정의된 UUID 버전 4 표준 규약을 따르면 1조 개의 UUID 중에 중복이 일어날 확률은 10억 분의 1이라고 합니다.
  • (장점) UUID의 또 다른 장점은 작은 크기인데, 다른 고유 식별자에 비해 정렬, 차수, 해싱 등 다양한 알고리즘에 사용하기 쉽고 데이터베이스에 보관하기도 용이하다고 합니다.

🔒 S3 버킷 이미지 덮어쓰기 문제

  • 문제: 고유한 이름 없이 이미지 파일의 오리지널 이름으로 S3에 파일을 저장이 되므로써 동일한 이름의 파일을 덮어쓰게 되 잠재적인 데이터 손실이나 잘못된 파일 검색이 발생하는 문제가 발생

  • 문제 해결 프로세스:

  1. 문제 식별: 이름이 중복된 파일은 S3에서 서로 덮어쓰므로 충돌이 발생하고 고유한 파일 스토리지가 손실된다는 점을 인식했습니다.
  2. UUID 구현: 각 파일 이름에 UUID를 추가하여 고유성을 보장하고 덮어쓰기를 방지합니다. UUID는 각 파일 이름을 구별하는 고유 식별자입니다.
  3. 코드 업데이트: 파일 이름에 UUID를 추가하도록 파일 업로드 코드를 수정하고 UUID가 있는 파일을 처리하도록 삭제 로직을 업데이트합니다.
  • 결론: UUID를 파일 이름에 통합함으로써 S3에 저장된 각 파일은 고유하게 식별되어 덮어쓰기 문제를 해결하고 각 파일이 충돌 없이 올바르게 저장되고 검색 가능하도록 보장합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// S3ServiceImpl
@Override
  public String imageUpload(MultipartFile image, String uniqueFileName) {
	if (image.isEmpty()) {
	  throw new GlobalException(ErrorCode.EMPTY_FILE);
	}
	fileSizeExceed(image);
	allowedImageTypes(image);
	try {
	  ObjectMetadata metadata = new ObjectMetadata();
	  metadata.setContentType(image.getContentType());
	  metadata.setContentLength(image.getSize());
	  amazonS3Client.putObject(BUCKET, uniqueFileName, image.getInputStream(), metadata);
	  String imageUrl = amazonS3Client.getResourceUrl(BUCKET, uniqueFileName);
	  return imageUrl;
	} catch (IOException e) {
	  throw new FileUploadFailureException("파일 업로드 실패");
	}
  }

  @Override
  public void deleteVerificationImage(Verification verification) {
	DeleteObjectRequest request = new DeleteObjectRequest(BUCKET, verification.getImageName());
	amazonS3Client.deleteObject(request);
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// VerificationService
@Transactional
  public VerificationResponseDto requestVerification(Long challengeId, MultipartFile image, VerificationRequestDto requestDto, User user) {
      Challenge challenge = challengeService.findById(challengeId);

      duplicateVerification(challengeId, user);

      String key = "verification/";
      String uniqueFileName = key + UUID.randomUUID() + "_" + image.getOriginalFilename();
      String imageUrl = s3Service.imageUpload(image, uniqueFileName);
      Verification verification = new Verification(requestDto.getTitle(), requestDto.getContent(), uniqueFileName, imageUrl, challenge, user);
      verificationRepository.save(verification);
      return new VerificationResponseDto(verification.getId(), verification.getTitle(), verification.getContent(), imageUrl, verification.getStatus());
  }

  @Transactional
  public void cancelVerification(Long verificationId, User user) {
    Verification verification = findVerificationById(verificationId);
    if(verification.getStatus().equals(Status.APPROVE)) {
      throw new GlobalException(ErrorCode.DO_NOT_CANCEL_VERIFICATION);
    }
    verification.checkUser(user);
    s3Service.deleteVerificationImage(verification);
    verificationRepository.delete(verification);
  }
  • cancelVerification함수에서 deleteVerificationImage의 매개변수가 String key도 있었으나 이미지를 저장할 때 키값 같이 저장하는 식으로 구현을 하여 매개변수 한 개를 줄였습니다(?)
    -> requestVerification에서 verification을 생성할때 uniqueFileName변수에 key와 UUID + image의 고유이름을 넣고 저장하였기에 삭제할 때 key값이 필요없어졌습니다. -> requestVerification에서 uniqueFileName을 고유값으로 만든 후 이미지 업로드 기능을 호출하는 형식으로 수정하여 verificationService 이외의 service에서 이미지업로드 기능이 필요할 경우 해당 부분을 공통 기능으로 사용할 수 있게 되었습니다. String imageUrl = s3Service.imageUpload(image, uniqueFileName);
This post is licensed under CC BY 4.0 by the author.