Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'

// S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.683'
implementation 'io.awspring.cloud:spring-cloud-starter-aws:3.3.0'
implementation 'software.amazon.awssdk:s3:2.20.14'
// Resilience4j
implementation 'io.github.resilience4j:resilience4j-spring-boot2:1.7.1'
implementation 'io.github.resilience4j:resilience4j-bulkhead:1.7.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ public ResponseEntity<?> uploadStoreImage(
@RequestParam("files") List<MultipartFile> files,
@RequestParam(value = "types") List<String> types
) {
// TODO 관련 정책 확정되면 메서드로 분리 예정
// 파일 개수 제한 검증
if (files.isEmpty() || files.size() > 10) {
throw new IllegalArgumentException("파일은 1개 이상 10개 이하로 업로드해 주세요.");
}
// 파일 크기 검증
for (MultipartFile file : files) {
if (file.isEmpty()) {
throw new IllegalArgumentException("빈 파일은 업로드할 수 없습니다.");
}
if (file.getSize() > 10 * 1024 * 1024) { // 10MB 제한
throw new IllegalArgumentException("파일 크기는 10MB를 초과할 수 없습니다.");
}
}

List<StoreImageUploadResponse> response = storeImageService.saveAll(storeId, files, types);
return ResponseEntity
.status(HttpStatus.CREATED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -34,15 +35,26 @@ public List<StoreImageUploadResponse> saveAll(Long storeId, List<MultipartFile>
Store store = storeRepository.findById(storeId)
.orElseThrow(() -> new EntityNotFoundException("Store not found with id: " + storeId));

List<StoreImageUploadResponse> imageUploadResponses = new ArrayList<>();
for (int i = 0; i < files.size(); i++) {
S3Service.S3UploadResult uploadResult;
try {
uploadResult = s3Service.upload(storeId, files.get(i)).get();
} catch (Exception e) {
throw new RuntimeException("S3 업로드 실패", e);
}
// 모든 파일을 비동기로 업로드
List<CompletableFuture<S3Service.S3UploadResult>> uploadFutures = new ArrayList<>();
for (MultipartFile file : files) {
uploadFutures.add(s3Service.upload(storeId, file));
}

// 모든 업로드 완료 대기
List<S3Service.S3UploadResult> uploadResults;
try {
uploadResults = uploadFutures.stream()
.map(CompletableFuture::join)
.toList();
} catch (Exception e) {
throw new RuntimeException("S3 업로드 실패", e);
}

// DB 저장은 모든 S3 업로드 성공 후 수행
List<StoreImageUploadResponse> imageUploadResponses = new ArrayList<>();
for (int i = 0; i < uploadResults.size(); i++) {
S3Service.S3UploadResult uploadResult = uploadResults.get(i);
StoreImage storeImage = StoreImage.builder()
.store(store)
.imageUrl(uploadResult.url())
Expand Down
Loading