java future로 이미지 병렬적으로 업로드 하려다 실패한 슬픈 이야기..

가영·2021년 12월 5일
1

로지성장일기

목록 보기
2/3

AWS s3 에서는 다음과같이 java에서 비동기적으로 객체를 업로드하는 기능을 제공한다.

default CompletableFuture<PutObjectResponse> putObject(PutObjectRequest putObjectRequest, AsyncRequestBody requestBody) {
	throw new UnsupportedOperationException();
}

나는 다음과 같이 이용할 수 있었다.

fun putObject(key: String, multipartFile: MultipartFile): CompletableFuture<PutObjectResponse> {
	val file = convertMultipartToFile(multipartFile) // 요청으로 들어온 multipartFile을 File로 변환한다.
        val objectRequest = PutObjectRequest
            .builder().bucket(bucketName).key(key).build() // PutObject 메서드에 넣어줄 Request 객체를 만든다.
        return s3AsyncClient.putObject(objectRequest, AsyncRequestBody.fromFile(Path.of(file.path)))
            .whenComplete { _, _ -> removeNewFile(file) }
}

게시글 생성 서비스에서 이용

게시글을 쓸 때 이미지 업로드를 하는 경우 (Request DTO에 이미지 리스트가 비어있지 않은 경우)를 분기처리하여 이 때만 Async Uploader를 사용하게 했다.

if (!postErrandReqDto.images.isNullOrEmpty()) {
    val imageWithKeyList = postErrandReqDto.images.map { image ->
        val fileName = "${errand.id}-${user.id}-${LocalDateTime.now()}"
        val key = s3AsyncUploader.generateKey(fileName) // 키 지어주기. (이전에 올라갔던 같은 키의 객체가 있을 경우 이 파일로 덮어쓰여지기 때문에 유니크하게!)
        imageRepository.save(Image(s3AsyncUploader.generateObjectUrl(key), errand)) // 이미지를 업로드 하기 전에 키를 생성하면서(이미지 url을 얻을 수 있으므로) 같이 이미지 엔티티를 데이터베이스에 생성해주었다.
        ImageFileWithKey(image, key) // map으로 들어가는 요소들의 형태.
    }
    val futures = imageWithKeyList.map { imgWithKey ->
        s3AsyncUploader.putObject(imgWithKey.key, imgWithKey.image)
    }
    CompletableFuture.allOf(*futures.toTypedArray()).handle { _, err -> throw err } // TODO: 확인
}
return errand

고민되었던 부분

  • 키를 지어주면서 데이터베이스에 이미지 엔티티를 생성해주는 것이 맞는지 살짝 걱정스러웠다.
  • 사실 이미지 url은 s3 uploader를 이용해서 업로드 해주고 난 후에 접근할 수 있게 되는 것인데 미리 데이터베이스에 저장을 하는 게 맞긴 한지.
  • 문제가 되는 부분이 있었다. 이건 아직도 미지수인데, 글을 쓰고 난 직후에 클라이언트에서 사진이 바로 뜨지 않았다.
  • CompletableFuture의 allOf를 사용해줬음에도 불구하고 사진 업로드 요청 각각의 병렬적 수행이 완료되고 나서 응답이 가는 것이 아닌 것 같았다..
    😢

결론

글을 올리고 나서 바로 사진을 볼 수 없는 문제 외에도,
다른 이유로 iOS에서는 아예 사진 업로드를 실패했기 때문에
Presigned url 발급으로 클라단에서 사진을 업로드 하고 서버에서는 이미지 url만 받아 글을 생성하는 방법으로 바꾸게 되었다.
그 이야기는 다음 포스팅에서 해야겠당


비동기 업로드를 공부하면서 추가적으로 알게된 것

s3에 같은 객체를 업로드하는 경우 마지막으로 업로드한 애가 덮어쓰인다.

s3에 업로드를 하는 것 자체가 분산 컴퓨팅으로 이뤄지기 때문이라고 한다.

그래서 우리가 lock을 걸고 싶을 경우에는 애플리케이션 단에서 기능을 추가해줘야한다.

1개의 댓글

comment-user-thumbnail
2022년 2월 9일

괜찮아요. 흔히들 실패는 성공의 어머니라고 한답니다 ^^

답글 달기