개발하는 두부

AWS S3 - SpringBoot 연동 및 업로더 구현하기 (feat. 테스트 코드)

by 뚜부니
SpringBoot에서 S3 사용 시 Spring Boot는 2.x.x 버전이어야 합니다. 2023년 1월 8일 기준, Spring Boot 3.x.x 버전에 대한 정식 Release가 출시 되지 않았기 때문입니다. (참고 : https://github.com/awspring/spring-cloud-aws)
여기서는 S3 개념에 대해 자세하게 다루지 않으며, 자세한 설명을 원한다면 아래 블로그를 추천드립니다.
https://inpa.tistory.com/entry/AWS-📚-S3-버킷-생성-사용법-실전-구축

 

AWS Key 발급

AWS 서비스에 접근하기 위해서는 권한을 가진 Key를 생성해야 합니다. Key 발급은 AWS IAM 서비스를 이용하면 됩니다. 먼저, AWS IAM의 사용자에 접속하여 우측에 있는 사용자 추가 버튼을 클릭합니다.

생성할 사용자 이름을 입력하고, 액세스 유형은 프로그래밍 방식 으로 선택합니다.

사용자 이름은 영문자와 숫자, _+=,.@- 를 포함할 수 있습니다.

참고로, 저는 넘블 당근마켓챌린지 ver.2 참여 중이라서 numble-daangn-market-ver2로 생성했습니다 :D

그 다음 AmazonS3FullAccess 권한을 부여합니다.

태그는 필요에 따라 입력하면 됩니다. 여기서는 IAM 구분을 위해 Key를 Name으로 주고 설정했습니다.

최종 생성이 완료되면 다음과 같이 액세스 키 ID와 비밀 액세스 키가 생성됩니다. 해당 키는 SpringBoot 프로젝트, Github Actions 등 다양하게 사용될 수 있는 키이므로 저장해두세요!

.csv를 다운로드 받아두거나, 액세스 키 ID와 비밀 액세스 키를 저장해두는 방법이 있겠지요..ㅎㅎ

 

S3 Bucket 생성

온라인 오브젝트 스토리지 서비스인 S3를 사용하기 위해서는 Bucket 이라는 객체를 저장하는 공간을 생성해야 합니다. 버킷 생성을 위해 AWS S3의 우측에 있는 버킷 만들기 버튼을 클릭합니다.

버킷 이름과 AWS 리전, 모든 퍼블릭 액세스 차단을 비활성화로 설정합니다. 나머지는 기본 설정 그대로 두었습니다.

생성이 완료되면 아래와 같이 대시보드에서 확인할 수 있습니다.

객체를 퍼블릭으로 설정할 수 있음이 아닌 액세스를 퍼블릭으로 변경하기 위해서는 권한 추가가 필요합니다.

해당 버킷의 버킷 정책 에 아래 내용을 추가합니다.

{
"Version": "2012-10-17",
"Id": "NumbleDaangnMarketVer2ImageBucket",
"Statement": [
{
"Sid": "ModifyBucketPolicy",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::{버킷 이름}/*"
}
]
}

다시 대시보드로 돌아가보면 액세스 퍼블릭으로 변경된 것을 확인할 수 있습니다.

 

Spring Boot 내에 S3 설정하기

먼저 Gralde에 AWS 관련 dependency를 추가합니다. spring-cloud-starter-aws는 현재 가장 최신 버전인 2.2.6.RELEASE로 설정했습니다.

👉 spring-cloud-start-aws 최신 버전 확인하러 가기

dependencies {
// ...
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
// ...
}

AWS S3에 필요한 정보를 application.yml에 추가합니다.

spring:
config:
activate:
on-profile: s3
cloud:
aws:
s3:
bucket: numble-daangn-market-ver2-image
region:
static: ap-northeast-2
stack:
auto: false
credentials:
access-key: {access key 입력}
secret-key: {secret key 입력}

cloud.aws.stack.auto: false 는 EC2에서 Spring Cloud 프로젝트를 실행시키면 기본으로 CloudFormation 구성을 시작합니다. 설정한 CloudFormation이 없으면 프로젝트 시작이 안되니, 해당 내용은 사용하지 않도록 false를 등록합니다.

그 다음 1MB 이상 파일을 업로드 할 수 있도록 application.yml에 max-file-size를 설정합니다. 해당 설정을 하지 않으면, max-file-size의 기본 설정이 1MB이기 때문에 1MB 이상의 파일을 업로드할 수 없습니다.

spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB # default 10MB

 

S3 업로더 구현하기

클라이언트에서 API를 통해 MultipartFile을 전달하면, 해당 파일을 S3에 업로드 하는 기능을 구현했습니다.

@Slf4j
@Service
@RequiredArgsConstructor
public class S3Service {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
public String upload(MultipartFile multipartFile) throws IOException {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(multipartFile.getContentType());
metadata.setContentLength(multipartFile.getSize());
amazonS3Client.putObject(bucket, multipartFile.getName(), multipartFile.getInputStream(), metadata);
return amazonS3Client.getUrl(bucket, multipartFile.getName()).toString();
}
}

실제 동작하는 지 확인하기 위해 아래와 같이 테스트 코드를 작성하여 실행시켜봅니다.

@SpringBootTest
public class S3ServiceTest {
@Autowired
private S3Service s3Service;
@Test
@DisplayName("S3 업로드 테스트")
public void upload() throws Exception {
// given
String fileName = "default-profile";
String contentType = "svg";
String filePath = "src/test/resources/static/image/default-profile.png";
MockMultipartFile multipartFile = getMockMultipartFile(fileName, contentType, filePath);
// when
String[] s3Urls = s3Service.upload(multipartFile).split("/");
String s3FileName = s3Urls[s3Urls.length - 1];
// then
assertThat(fileName).isEqualTo(s3FileName);
}
private MockMultipartFile getMockMultipartFile(String fileName, String contentType, String path) throws IOException {
FileInputStream fileInputStream = new FileInputStream(path);
return new MockMultipartFile(fileName, fileName + "." + contentType, contentType, fileInputStream);
}
}

실행 결과, 테스트 코드는 정상적으로 수행함을 확인할 수 있습니다.

혹시 모르니 진짜 업로드 되었는지도 확인해보겠습니다.

S3 버킷을 확인해보면, 테스트 코드 실행을 통해 올린 이미지가 객체로 버킷 내에 존재함을 확인할 수 있습니다.

해당 객체의 객체 URL을 클릭해보면 이미지가 표출되는 것도 확인할 수 있습니다.

 

참고

블로그의 프로필 사진

블로그의 정보

개발하는 두부

뚜부니

활동하기