- Spring Boot 애플리케이션에서 AWS SDK를 활용하여 S3에 이미지를 업로드하는 메서드와, CloudFront를 통해 각 S3 버킷의 섬네일 파일에 접근할 수 있는 URL을 생성하는 메서드를 생성하였다.
- 생성된 URL은 소설의 상세 정보를 전송할 때 포함되어 클라이언트가 해당 URL을 통해 이미지를 다운로드하고 렌더링할 수 있도록 구성하였다.
1. 프로젝트 설정
build.gradle
- AWS SDK를 사용하기 위해 의존성을 설정한다.
//AWS S3 SDK, versionCheck 2024-08-19
implementation 'software.amazon.awssdk:s3:2.27.7'
//AWS Cloud Front SDK, versionCheck 2024-08-19
implementation 'software.amazon.awssdk:cloudfront:2.27.7'
application.properties
- S3와 CloudFront 설정을 application.properties 파일에 추가한다,
- 업로드 용량은 10MB로 제한하였다.
# AWS S3 config
aws.s3.accessKey=<KEY>
aws.s3.secretKey=<SECRETKEY>
aws.s3.thumbnail.bucket=<BUCKETNAME>
# AWS Cloud Front config
aws.cloudfront.thumbnail.domain=<URL>.cloudfront.net
aws.cloudfront.mini-thumbnail.domain=<URL>.cloudfront.net
# Upload file size setting
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
2. AWS S3 연결 설정
- AWS S3와 상호작용할 수 있는 S3Client 객체를 Spring Bean으로 등록한다.
- AWS accessKey 와 secretKey 는 위에서 설정한 값으로 propertiy binding 하여 값을 가져와 등록하였다.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
@Configuration
public class AWSConfig {
@Value("${aws.s3.accessKey}")
private String accessKey;
@Value("${aws.s3.secretKey}")
private String secretKey;
//AWS S3 접속 클라이언트 객체 이미지 업로드시 사용
@Bean
public S3Client s3Client() {
return S3Client.builder()
.region(Region.AP_NORTHEAST_2)//지역설정(한국 서울)
.credentialsProvider(StaticCredentialsProvider
.create(AwsBasicCredentials.create(accessKey, secretKey)))//key설정
.build();
}
3. S3 관련 Service 계층 구현
1) 업로드 메서드
- S3 버킷에 파일 업로드시, 파일명이 중복될 수 있으므로, 현재시간과 파일명 조합으로 고유한 파일명을 갖도록 구현하였다.
- PutObjectRequest로 S3 버킷에 이미지 파일을 바이트 형태로 업로드하고, 성공 시 파일 이름을 반환(DB에 저장하기 위함) 실패하면 예외를 던지도록 하였다.
@Override
public String uploadFileToS3(MultipartFile file) {
//현재시간과 파일이름으 조합으로 고유한 파일명 생성
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
try {
//S3에 Put 요청을 하기위한 Request 객체 생성
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(THUMBNAIL_BUKET_NAME)//S3에서 사용될 버킷 이름
.key(FOLDER_NAME + "/" + fileName)//버킷 안에 폴더 이름과 파일 명으로 엔드포인트 설정
.build();
//업로드할 파일을 바이트로 변환하여 S3 버킷에 저장후, 반환된 응답 객체에 저장
PutObjectResponse response = s3Client.putObject(putObjectRequest,
RequestBody.fromBytes(file.getBytes()));
//업로드 결과 출력, 업로드 실패시 예외로 던져짐
log.info("S3 파일 업로드 결과 ={}", response.toString());
return fileName;
} catch (Exception ex) {
throw new ServiceMethodException("uploadFileToS3 메서드 에러, S3 파일 업로드 실패" + ex + ex.getMessage());
}
}
2) CloudFront URL 생성 메서드
- 소설의 상세 정보를 보낼때 섬네일 생성용 메서드이다.
- 이미지 종류(원본/축소)에 따라 CloudFront URL을 생성하여 클라이언트에서 이미지에 접근할 수 있도록 하였다.
@Override
public String generateCloudFrontUrl(String fileName, String thumbnailType) {
try {
//작은 썸네일 요청 URL 생성(랭킹 등 이미지가 작아도 되는경우)
if (thumbnailType.equals("mini")) {
return String.format("https://%s/%s/%s", MINI_THUMBNAIL_DOMAIN_NAME, FOLDER_NAME, "mini-"+fileName); //생성된 URL 반환
}
//원본 썸네일 요청 URL, Novel 상세페이지 등에 이용
return String.format("https://%s/%s/%s", THUMBNAIL_DOMAIN_NAME, FOLDER_NAME, fileName); //생성된 URL 반환
//cloudfront 도메인으로 S3 이미지 접근, URL은 https://{cloudfront도메인이름}/{폴더이름}/{파일이름}
} catch (Exception ex) {
throw new ServiceMethodException("generateCloudFrontUrl 메서드 에러 cloudfront URL 생성실패" + ex + ex.getMessage());
}
}
전체코드
import com.ham.netnovel.common.exception.ServiceMethodException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
@Service
@Slf4j
public class S3ServiceImpl implements S3Service {
private final S3Client s3Client;
private final String FOLDER_NAME = "thumbnail";//S3 버킷에서 접근할 폴더 이름
@Value("${aws.s3.thumbnail.bucket}")
private String THUMBNAIL_BUKET_NAME;//S3 버킷이름
@Value("${aws.cloudfront.thumbnail.domain}")
private String THUMBNAIL_DOMAIN_NAME;//원본 사이즈 섬네일cloud front 가상 도메인 이름
@Value("${aws.cloudfront.mini-thumbnail.domain}")
private String MINI_THUMBNAIL_DOMAIN_NAME;//작은 사이즈 섬네일 cloud front 가상 도메인 이름
public S3ServiceImpl(S3Client s3Client) {
this.s3Client = s3Client;
}
@Override
public String uploadFileToS3(MultipartFile file) {
//현재시간과 파일이름으 조합으로 고유한 파일명 생성
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
try {
//S3에 Put 요청을 하기위한 Request 객체 생성
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(THUMBNAIL_BUKET_NAME)//S3에서 사용될 버킷 이름
.key(FOLDER_NAME + "/" + fileName)//버킷 안에 폴더 이름과 파일 명으로 엔드포인트 설정
.build();
//업로드할 파일을 바이트로 변환하여 S3 버킷에 저장후, 반환된 응답 객체에 저장
PutObjectResponse response = s3Client.putObject(putObjectRequest,
RequestBody.fromBytes(file.getBytes()));
//업로드 결과 출력, 업로드 실패시 예외로 던져짐
log.info("S3 파일 업로드 결과 ={}", response.toString());
return fileName;
} catch (Exception ex) {
throw new ServiceMethodException("uploadFileToS3 메서드 에러, S3 파일 업로드 실패" + ex + ex.getMessage());
}
}
@Override
public String generateCloudFrontUrl(String fileName, String thumbnailType) {
try {
//작은 썸네일 요청 URL 생성(랭킹 등 이미지가 작아도 되는경우)
if (thumbnailType.equals("mini")) {
return String.format("https://%s/%s/%s", MINI_THUMBNAIL_DOMAIN_NAME, FOLDER_NAME, "mini-"+fileName); //생성된 URL 반환
}
//원본 썸네일 요청 URL, Novel 상세페이지 등에 이용
return String.format("https://%s/%s/%s", THUMBNAIL_DOMAIN_NAME, FOLDER_NAME, fileName); //생성된 URL 반환
//cloudfront 도메인으로 S3 이미지 접근, URL은 https://{cloudfront도메인이름}/{폴더이름}/{파일이름}
} catch (Exception ex) {
throw new ServiceMethodException("generateCloudFrontUrl 메서드 에러 cloudfront URL 생성실패" + ex + ex.getMessage());
}
}
}