이번 게시글에서는 AWS S3를 이용하여 새 파일을 저장하는 과정을 기술하겠다. 본래에는 도커를 이용하여 파일을 관리할 생각이었으나, S3를 사용하면 얻는 장점이 많기도 하고 CloudFront를 적용하여 Cash에 대한 장점도 얻을 수 있어서 S3를 적용하기로 하였다. 이로인해 아래 적용한 내용들은 dev 환경에서 S3를 이용하도록 구성되어 있다.
S3는 AWS(Amazon Web Service)에서 제공하는 인터넷 스토리지 서비스이다.
AWS S3 설정과 관련한 부분은 다른 게시글에 잘 정리되어있으므로 해당 게시글에서는 정리하지 않고 간단하게 게시글을 추천해주고 넘어가겠다.
Spring Boot에서 S3와의 연동을 위해 build.gradle과 application-dev.yml, S3 관련 설정 클래스를 구성한다.
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' //AWS S3
AWS S3와 연동하기 위한 의존성을 추가한다.
cloud:
aws:
region:
static: ${s3.region}
s3:
bucket: ${s3.bucket}
stack:
auto: false
credentials:
access-key: ${s3.access-key}
secret-key: ${s3.secret-key}
AWS 리전, S3 버킷 이름, 액세스 키, 시크릿 키등 을 설정한다.
@Profile("dev")
@Configuration
public class S3Config {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public AmazonS3 s3Builder() {
AWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withRegion(region).build();
}
}
S3Config 클래스는 AWS S3 서비스를 위한 Spring 설정을 제공한다.
- @Profile("dev") 어노테이션을 사용해 개발 환경에서만 적용된다.
- Builder 메서드는 AWS 자격 증명과 리전을 설정해 AmazonS3 클라이언트 객체를 생성하고 반환한다.
@Profile("dev")
@Service
@RequiredArgsConstructor
public class S3FileService implements FileService {
private final AmazonS3 amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucketName;
@Override
public void upload(MultipartFile file, String filename) {
try {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(file.getContentType());
objectMetadata.setContentLength(file.getSize());
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, filename, file.getInputStream(), objectMetadata);
amazonS3Client.putObject(putObjectRequest);
} catch(Exception e) {
throw new FileUploadFailureException(e);
}
}
@Override
public void delete(String filename) {
try {
amazonS3Client.deleteObject(bucketName, filename);
} catch (Exception e) {
throw new FileDeleteFailureException(e);
}
}
}
S3FileService 클래스는 파일 업로드와 삭제 기능을 구현한다.
- @Service 어노테이션으로 서비스 레이어임을 명시한다.
- AWS S3 클라이언트를 주입받아 upload 메서드에서는 파일 메타데이터 설정 후 S3 버킷에 파일을 업로드한다. delete 메서드로 S3 버킷에서 파일을 삭제한다.
@ExtendWith(MockitoExtension.class)
class S3FileServiceTest {
@InjectMocks
private S3FileService s3FileService;
@Mock
private AmazonS3 amazonS3Client;
@BeforeEach
void beforeEach() {
ReflectionTestUtils.setField(s3FileService, "bucketName", "my-bucket");
}
@Test
void uploadTest() throws Exception {
// given
MultipartFile file = new MockMultipartFile("file", "test.txt", "text/plain", "test content".getBytes());
String filename = "testFile.txt";
// when
s3FileService.upload(file, filename);
// then
verify(amazonS3Client).putObject(any(PutObjectRequest.class));
}
@Test
void uploadExceptionByFileUploadFailureException() {
// given
MultipartFile file = new MockMultipartFile("file", "test.txt", "text/plain", new byte[0]);
// then
doThrow(new RuntimeException("S3 Upload Failed")).when(amazonS3Client).putObject(any(PutObjectRequest.class));
// when
assertThrows(FileUploadFailureException.class, () -> {
s3FileService.upload(file, "test.txt");
});
}
@Test
void deleteTest() {
// given
String filename = "testFile.txt";
// when
s3FileService.delete(filename);
// then
verify(amazonS3Client).deleteObject(any(String.class), eq(filename));
}
@Test
void deleteExceptionByFileDeleteFailureException() {
// given
String filename = "nonExistentFile.txt";
// then
doThrow(new RuntimeException("S3 Delete Failed")).when(amazonS3Client).deleteObject(any(DeleteObjectRequest.class));
// when
assertThrows(FileDeleteFailureException.class, () -> {
s3FileService.delete(filename);
});
}
}
S3FileServiceTest 클래스는 S3FileService의 기능을 테스트한다.
- Mockito를 사용해 AmazonS3 클라이언트의 모의 객체를 S3FileService에 주입한다.
- uploadTest에서는 파일 업로드를 시뮬레이션하고 putObject 호출을 검증한다.
- deleteTest에서는 파일 삭제 기능을 검증한다.
S3 버킷에 파일이 잘 업로드된 모습을 확인할 수 있다.
AWS S3를 활용한 파일 저장은 간단하며 효율적이다. 이 과정을 통해 파일을 안전하고 빠르게 관리할 수 있다.
추가로, Spring Boot에서 S3를 활용하는 방법을 잘 설명한 Spring Boot에서 S3에 파일을 업로드하는 세 가지 방법을 추천하며 마무리 하겠다.