본문 바로가기
Team

[CS 스터디] S3에 파일을 업로드 하는 방법

by seungh2 2023. 8. 17.

S3

  • AWS의 스토리지 서비스 중 하나
  • 여러가지 용도로 사용 가능한 범용적인 스토리지 서비스
  • 여러 데이터를 보관할 수 있고 정적 웹사이트 호스팅 및 다양한 형태의 클라우드 서비스로 활용 가능한 만능 스토리지 서비스
사진이나 동영상 같은 파일을 업로드 할 때, S3를 사용할 수 있다.

프론트엔드 → 백엔드 → S3

  1. 사용자는 application 서버에 파일을 업로드한다.
  2. application 서버는 처리를 위해 임시 공간에 파일을 저장한다.
  3. application은 영구적으로 저장하기 위해 파일을 S3 버킷에 전송한다.
  • 프로세스는 간단하지만 사용량이 많은 application에서는 웹 서버의 성능에 문제를 일으킬 수 있다.
  • 파일 업로드는 일반적으로 크기가 크기 때문에 이를 전송하는 것은 네트워크 I/O  및 서버 CPU 시간을 많이 잡아먹는다.
공통 프로젝트
  • 프론트엔드 → 백엔드 → S3로의 업로드를 사용해 S3 버킷에 파일을 업로드하였다.
  1. 사용자가 프론트엔드에 파일을 업로드한다.
  2. 프론트엔드에서 백엔드 서버로 파일을 전송한다.
  3. 백엔드 서버에서 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 버킷에 파일을 업로드하였다.
  1. 사용자가 프론트엔드에 파일을 업로드한다.
  2. 프론트엔드에서 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은 객체를 업로드할 때 사용한다.

 

  1. S3 버킷에서 signed URL을 가져온다.
  2. Amazon API Gateway엔드 포인트에서 getSignedURL Lambda 함수를 호출해서 얻을 수 있다.
  3. 프론트엔드 application에서 S3 버킷으로 파일을 직접 업로드 한다.
  • presigned URL을 사용하여 제한된 시간동안만 업로드가 가능하도록 설정하기 때문에 보안성을 강화할 수 있다.
  • 업로드는 프론트엔드에서 진행되지만, 업로드 권한은 백엔드에서 관리하여 업로드 권한이 분리된다. → 보안 관리가 용이하다.
  • 크기가 큰 파일을 업로드할 때도, 성능이 개선된다.
    • AWS 내부 네트워크를 이용해 데이터를 전송하기 때문에 빠르다.
    • presigned URL을 사용하면 여러 사용자가 동시에 파일을 업로드하거나 다운로드할 수 있다.  → 병렬 작업이 가능하다.
  • 많은 파일 업로드가 예상되는 경우에는 서버리스 업로드와 같이 백엔드에서 멀리 떨어져있는 S3로 대량의 네트워크 트래픽을 offload 할 수 있다.
    • → 네트워크 부하를 서비스에서 멀리 이동시킨다.
  • 결과적으로 application의 확장성을 높일 수 있고, 트래픽이 급증했을 때를 대비할 수 있다.

 

적용 방법

  1. 백엔드 서버에서 presigned URL을 생성한다.
    • presigned URL에는 버킷 정보, 파일 경로 및 url의 만료 시간, 유효한 권한 정보가 포함된다.
  2. 백엔드 서버에서 생성한 presigned URL을 프론트엔드에게 전달한다.
  3. 백엔드 서버에서 받은 presigned URL을 이용해 프론트엔드에서 파일을 S3 버킷에 직접 업로드한다.
    • 프론트엔드와 S3 사이에서 직접 주고 받는 것이므로 업로드 파일은 백엔드를 거치지 않는다.

 


참고

https://aws.amazon.com/ko/blogs/compute/uploading-to-amazon-s3-directly-from-a-web-or-mobile-application/?_encoding=UTF8&tag=clien0a-20&linkCode=ur2&linkId=4ef926e41eaa57b3dcfc48b2660f5024&camp=1789&creative=9325 

 

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

댓글