Project

[SpringBoot(Java), AWS] 이미지 로딩 속도 개선 (CloudFront, S3)

y-seo 2024. 3. 31. 17:38

 


포토 캘린더 서비스 (Cookiee-) 를 진행하며 이미지 로딩 속도 개선을 한 경험을 적어보려 합니다.


 

문제 상황

진행 중인 프로젝트에서 이미지를 로딩하는 모든 API 호출에 있어서 이미지 로딩이 너무.. 느린 것이 문제 상황이었다. WiFi가 불안정한 곳에서 사용자에게 서비스를 설명할 때에는 체감 5초까지 걸릴 정도로 이미지 로딩이 오래 걸리었다. 하지만 우리 서비스는 이미지 관련 서비스이기 때문에 꼭 이 부분을 고쳐야 했다.

본래 서비스는 S3만을 이용하여 이미지 작업을 처리하였다. SpringBoot에서 이미지를 S3 버킷에 저장하고 S3 버킷 내 객체 URL을 DB에 저장하였다. 프론트에서 이미지 로딩 API를 호출할 때에도 S3 버킷 내 객체 주소를 통해 화면에 띄웠다.

 

해결 아이디어

이를 해결하기 위해 캐시 작업의 필요성을 느끼었다. 이후 AWS의 CloudFront 서비스를 이용하게 되었다. 

AWS의 CloudFront는 CDN(콘텐츠 전송 네트워크)으로 캐싱을 통한 짧은 지연 시간과 빠른 전송 속도로 안전하게 콘텐츠를 전송하는 서비스이다. 클라이언트의  요청으로 서버에서 받아온 이미지를 캐싱해두고 이후 같은 요청 시, 캐싱해두었던 것을 제공하여 전송 속도를 높이는 원리이다. 따라서 본래 매번 S3에 접속하여 이미지를 받아오는 것보다 훨씬 더 빠른 속도를 기대할 수 있다.

 

1. CloudFront 생성

1. CloudFront 배포를 생성한다.

2. 아래와 같이 설정하여 생성한다.

2. 이후 오류페이지를 다음과 같이 설정한다.

3. CloudFront 도메인 이름을 통해 이미지 파일을 로딩해본다.

"CloutFront 배포 도메인 이름 + S3 폴더 구조 + 파일명" 으로 브라우저 접속 시 이미지를 볼 수 있다. 

여기까지 완료되면 CloudFront와 S3의 연결이 성공한 것이다.

 

2. SpringBoot 개발

기존 S3 사용으로 작업한 코드와 yml 파일들이 이미 존재한다는 가정 하에 수정해야 할 부분을 기술하였다.

실제 이미지 파일을 S3 버킷에 저장시켜주는 코드의 딱 한 줄만 수정하였다.

@Slf4j
@RequiredArgsConstructor
@Component
@Service
public class S3Uploader {

    @Value("${cloud.aws.s3.domain}")
    private String CLOUD_FRONT_DOMAIN_NAME;

    private final AmazonS3 amazonS3Client;

    @Value("cookiee-s3")
    private String bucket;

    public String saveFile(MultipartFile multipartFile, String userId, String dirName) {
        String originalFilename = multipartFile.getOriginalFilename();
        String newFilename = userId + "/" + dirName + "/" + UUID.randomUUID() + originalFilename;

        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(multipartFile.getSize());
        metadata.setContentType(multipartFile.getContentType());

        try {
            amazonS3Client.putObject(bucket, newFilename, multipartFile.getInputStream(), metadata);
        } catch (IOException e) {
            throw new GeneralException(S3_UPLOAD_ERROR);
        }

        //return amazonS3Client.getUrl(bucket, newFilename).toString();
        return CLOUD_FRONT_DOMAIN_NAME+"/"+newFilename;
    }
}
        //return amazonS3Client.getUrl(bucket, newFilename).toString();
        return CLOUD_FRONT_DOMAIN_NAME+"/"+newFilename;

가장 밑에 주석처리된 부분인데 본래는 S3에 저장된 이름 그대로 반환하여 DB에 저장하였다. 하지만 CloudFront를 적용한 후에는 이를 CloudFront 도메인 주소로 변환하여 DB에 저장하여야 했기에 return 부분만 바꾸어 코드를 작성하였다. 덧붙여 CLOUD_FRONT_DOMAIN_NAME은 관련 yml 파일에 key 값을 저장하고 @Value 어노테이션으로 불러오는 식으로 사용하였다.

 

결과

실제 Front 쪽에 이미지 로딩 속도를 Javascript의 time 함수를 사용하여 CloudFront 적용 전, 후의 이미지 로딩 시간을 측정하였다. 

CloudFront 도입 전 vs 후

이전에는 이미지 로드에 395ms가 걸린 반면, 이후에는 이미지 로드에 282ms가 걸리었다. 약 30% 성능이 되었다. 😇