S3 폴더 및 파일명
- S3 폴더 구조는
{개발환경}/{멤버ID}/{yyyy-MM-dd}
형태로 가져감
- S3에 업로드 되는 이미지의 파일명은 UUID를 사용
- ex)
e2eb545f-6bc7-4219-bb52-f72e951c5ae8.jpg
동작 방식
- S3에 이미지를 업로드해 url을 내려주는 방식 사용
- 다중 이미지 업로드 시 비동기 처리 구현
- CompleatbleFuture 사용
- 비동기 처리 시 디폴드 스레드 풀 사이즈는 5로 구성
- 디폴트 스레드 풀 사이즈 2로 구성해서 테스트해보기도 했지만 응답 시간이 둘다 비슷함
- 한번에 최대 5개의 이미지 업로드 요청이 올 수 있으므로 5개의 스레드를 미리 만들어놓는 방식 사용
- 단일 이미지 업로드는 동기 처리
구현 코드
@Override public List<String> uploadImages(Long id, List<MultipartFile> files) { List<CompletableFuture<String>> imageUrlFutures = files.stream() .map(file -> CompletableFuture.supplyAsync(() -> uploadImage(id, file), executor) .orTimeout(3, TimeUnit.SECONDS)) .collect(Collectors.toList()); return imageUrlFutures.stream( .map(CompletableFuture::join) .collect(Collectors.toList()); }
- 비동기로 S3에 이미지 업로드 요청을 전부 보낸 뒤 (
supplyAsync
) - 비동기 처리
CompletableFuture.join()
을 통해 업로드 요청이 전부 끝나길 기다린다 - 블로킹
타임아웃 설정 이유
- 비동기로 동작하던 와중에 에러 발생하게 된다면 해당 스레드에서 리턴값을 주지못하므로
join()
메소드에서 영원히 기다릴 수 있음 (블록 문제)
- 이를 방지하고자 비동기 요청에 대한 timeout 3초를 걸어서 해당 시간내에 요청을 처리하지 못하면
TimeoutException
을 던짐
비동기 처리 방식 선택 이유
- 자바에서 비동기 처리를 구현하는 방법으로는 여러 방법이 있음 그 중 2가지의 방식 중 고민
parallelStream
(병렬 스트림)- I/O가 포함되지 않은 계산 중심의 동작을 실행할 때는 스트림 인터페이스가 구현하기 간단하며 효율적
CompletableFuture
- I/O를 기다리는 작업을 병렬로 실행할때 많은 유연성을 제공. 대기/계산(W/C)의 비율에 적합한 스레드 수를 설정할 수 있음
이미지 업로드는 I/O 작업이므로
CompletableFuture
를 선택응답 시간 비교
동기, 비동기로 5개의 이미지 요청을 보냈을때의 시간을 비교합니다.
동기 처리 시
- 코드
@Override public List<String> uploadImages(Long id, List<MultipartFile> files) { return files.stream() .map(file -> uploadImage(id, file)) .collect(Collectors.toList()); }
- 응답 시간: 568 ms


비동기 처리 시
- 코드
@Override public List<String> uploadImages(Long id, List<MultipartFile> files) { List<CompletableFuture<String>> imageUrlFutures = files.stream() .map(file -> CompletableFuture.supplyAsync(() -> uploadImage(id, file), executor) .orTimeout(3, TimeUnit.SECONDS)) .collect(Collectors.toList()); return imageUrlFutures.stream( .map(CompletableFuture::join) .collect(Collectors.toList()); }
- 응답 시간: 201 ms

