S3
- AWS의 스토리지 서비스 중 하나
- 여러가지 용도로 사용 가능한 범용적인 스토리지 서비스
- 여러 데이터를 보관할 수 있고 정적 웹사이트 호스팅 및 다양한 형태의 클라우드 서비스로 활용 가능한 만능 스토리지 서비스
사진이나 동영상 같은 파일을 업로드 할 때, S3를 사용할 수 있다.
프론트엔드 → 백엔드 → S3
- 사용자는 application 서버에 파일을 업로드한다.
- application 서버는 처리를 위해 임시 공간에 파일을 저장한다.
- application은 영구적으로 저장하기 위해 파일을 S3 버킷에 전송한다.
- 프로세스는 간단하지만 사용량이 많은 application에서는 웹 서버의 성능에 문제를 일으킬 수 있다.
- 파일 업로드는 일반적으로 크기가 크기 때문에 이를 전송하는 것은 네트워크 I/O 및 서버 CPU 시간을 많이 잡아먹는다.
공통 프로젝트
- 프론트엔드 → 백엔드 → S3로의 업로드를 사용해 S3 버킷에 파일을 업로드하였다.
- 사용자가 프론트엔드에 파일을 업로드한다.
- 프론트엔드에서 백엔드 서버로 파일을 전송한다.
- 백엔드 서버에서 local 저장소에 파일을 저장한 후, S3 버킷에 저장하여, 저장된 link를 얻는다.
- 프론트엔드에서 백엔드로 이미지 파일을 보내고 백엔드에서 이미지 파일을 받는 것을 코드로 구현하는데 오래 걸렸다. (request 데이터 설정에 시간이 많이 걸렸다.)
- 이미지 파일을 request로 주고 받았기 때문에, request의 크기가 커졌고 그에 따라 처리 속도가 느려졌다.
@Slf4j
@RequiredArgsConstructor
@Component
public class AwsS3Uploader {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
public String bucket;
public String upload(MultipartFile multipartFile, String dirName) throws IOException {
File uploadFile = convert(multipartFile) // 파일 생성
.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File convert fail"));
return upload(uploadFile, dirName);
}
private String upload(File uploadFile, String dirName) {
String fileName = dirName + "/" + UUID.randomUUID() + uploadFile.getName();
String uploadImageUrl = putS3(uploadFile, fileName); // s3로 업로드
removeNewFile(uploadFile);
return uploadImageUrl;
}
// 1. 로컬에 파일생성
private Optional<File> convert(MultipartFile file) throws IOException {
File convertFile = new File(file.getOriginalFilename());
if (convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) {
fos.write(file.getBytes());
}
return Optional.of(convertFile);
}
return Optional.empty();
}
// 2. S3에 파일업로드
private String putS3(File uploadFile, String fileName) {
amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead));
log.info("File Upload : " + fileName);
return amazonS3Client.getUrl(bucket, fileName).toString();
}
// 3. 로컬에 생성된 파일삭제
private void removeNewFile(File targetFile) {
if (targetFile.delete()) {
log.info("File delete success");
return;
}
log.info("File delete fail");
}
}
@ApiOperation(value = "방송 생성하기")
@PostMapping(value = "/broadcasts", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<?> createBroadcast(@RequestPart CreateBroadcastDto broadcastInfo, @RequestPart MultipartFile img) throws OpenViduJavaClientException, OpenViduHttpException {
// 방송 생성
Long broadcastId = broadcastService.createBroadcast(broadcastInfo.getTitle(), broadcastInfo.getDescription(),
session.getSessionId(), broadcastInfo.getAnimalStoreId(), broadcastInfo.getAnimalIdList(), img);
}
/**
* broadcast 생성
* @param title : 방송 제목 description : 방송설명
* @return boolean :
*/
@Transactional
public Long createBroadcast(
String title, String description, String sessionId, Long animalStoreId,
List<Long> animalIdList, MultipartFile img
){
if(img.isEmpty()) {
throw new RuntimeException("썸네일을 설정하지 않았습니다.");
}
String imgUrl = null;
try {
imgUrl = s3Uploader.upload(img, dirName);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
프론트엔드 → S3
- 프론트엔드에서 구현하여 간단하게 처리할 수 있다. 프론트엔드에서 직접 처리하기 때문에 파일 업로드 진행상황을 쉽게 표시할 수 있다.
- 파일 업로드에 필요한 권한이 프론트엔드에 있어 보안에 취약할 수 있다.
- 크기가 큰 파일을 업로드할 때, 프론트엔드에서 오래 작업을 수행하게 되면, 성능에 영향을 미칠 수 있다.
특화 프로젝트
- 프론트엔드 → S3로의 업로드를 사용해 S3 버킷에 파일을 업로드하였다.
- 사용자가 프론트엔드에 파일을 업로드한다.
- 프론트엔드에서 S3 access key와 secret key를 이용해 S3 버킷에 저장하고, 저장된 link를 백엔드에 보낸다.
- 특화 프로젝트에서 업로드하는 이미지의 개수에 제한을 두지 않았었다.
- 그런데 이미지를 1개 올리는 경우와 이미지를 여러 개 올리는 경우에 프론트엔드에서 작업 속도가 확연히 다른 것을 볼 수 있었다. (이미지를 여러 개 올리는 경우에 엄청 오래 걸림!)
presigned URL 사용
- application 서버를 통하지 않고 S3에 직접 업로드 하는 것
- 네트워크 트래픽과 서버 CPU 사용량이 많이 줄어든다.
presigned URL
- presigned URL을 사용해 S3 버킷에 객체를 업로드하도록 허용할 수 있다.
- presigned URL을 사용하면 보안 자격 증명이나 권한이 없이 업로드할 수 있다.
- presigned URL은 만료 날짜 및 시간을 지정해야 하며, 만료 시간 전까지 여러 번 사용할 수 있다.
- HTTP 메소드 중 GET은 객체를 다운로드할 때 사용하고 PUT은 객체를 업로드할 때 사용한다.
- S3 버킷에서 signed URL을 가져온다.
- Amazon API Gateway엔드 포인트에서 getSignedURL Lambda 함수를 호출해서 얻을 수 있다.
- 프론트엔드 application에서 S3 버킷으로 파일을 직접 업로드 한다.
- presigned URL을 사용하여 제한된 시간동안만 업로드가 가능하도록 설정하기 때문에 보안성을 강화할 수 있다.
- 업로드는 프론트엔드에서 진행되지만, 업로드 권한은 백엔드에서 관리하여 업로드 권한이 분리된다. → 보안 관리가 용이하다.
- 크기가 큰 파일을 업로드할 때도, 성능이 개선된다.
- AWS 내부 네트워크를 이용해 데이터를 전송하기 때문에 빠르다.
- presigned URL을 사용하면 여러 사용자가 동시에 파일을 업로드하거나 다운로드할 수 있다. → 병렬 작업이 가능하다.
- 많은 파일 업로드가 예상되는 경우에는 서버리스 업로드와 같이 백엔드에서 멀리 떨어져있는 S3로 대량의 네트워크 트래픽을 offload 할 수 있다.
- → 네트워크 부하를 서비스에서 멀리 이동시킨다.
- 결과적으로 application의 확장성을 높일 수 있고, 트래픽이 급증했을 때를 대비할 수 있다.
적용 방법
- 백엔드 서버에서 presigned URL을 생성한다.
- presigned URL에는 버킷 정보, 파일 경로 및 url의 만료 시간, 유효한 권한 정보가 포함된다.
- 백엔드 서버에서 생성한 presigned URL을 프론트엔드에게 전달한다.
- 백엔드 서버에서 받은 presigned URL을 이용해 프론트엔드에서 파일을 S3 버킷에 직접 업로드한다.
- 프론트엔드와 S3 사이에서 직접 주고 받는 것이므로 업로드 파일은 백엔드를 거치지 않는다.
참고
Uploading to Amazon S3 directly from a web or mobile application | Amazon Web Services
This blog post walks through a sample application repo and explains the process for retrieving a signed URL from S3. It explains how to the test the URLs in both Postman and in a web application. Finally, I explain how to add authentication and make upload
aws.amazon.com
728x90
'Team' 카테고리의 다른 글
[CS 스터디] Spring 의존성 주입 (0) | 2023.08.30 |
---|---|
[CS 스터디] Annotation (0) | 2023.08.24 |
[CS 스터디] Spring Security (0) | 2023.08.02 |
[CS 스터디] AI, 메타버스, 블록체인 (0) | 2023.07.26 |
[CS 스터디] 클라우드와 분산환경 (0) | 2023.07.19 |
댓글