코드의 여백

Spring Boot와 LocalStack으로 AWS S3 테스트 환경 구성하기

by rowing0328

Intro

이번 포스팅에서는 Spring Boot와 LocalStack을 활용해 AWS S3 테스트 환경을 구축하는 방법을 소개한다.

 

LocalStack을 사용해 S3 테스트 환경을 구성하고, 파일 업로드와 이동 같은 주요 기능을 검증하는 테스트 코드를 작성한다.

 

 

Docker Compose로 LocalStack 실행

LocalStack을 사용하려면 Docker가 필요하다. 먼저 docker-compose.yml 파일을 생성해 아래와 같이 작성한다.

 

docker-compose.yml

services:
  localstack:
    image: localstack/localstack:latest
    container_name: localstack
    ports:
      - "4566:4566"
    environment:
      - SERVICES=s3
      - AWS_ACCESS_KEY_ID=test
      - AWS_SECRET_ACCESS_KEY=test
      - TZ=Asia/Seoul

 

 

Spring Boot에서 LocalStack 테스트 환경 설정

Spring Boot 프로젝트에서 LocalStack을 활용하려면 S3 클라이언트를 LocalStack과 연동하도록 설정해야 한다.

아래는 LocalStack을 테스트 환경에서 사용할 수 있도록 구성한 설정 클래스의 예제다.

 

TestLocalStackConfig.class

package com.realworld.common.localstack;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.validation.annotation.Validated;

@Validated
@Profile("test")
@TestConfiguration
public class TestLocalStackConfig {

    private final String region;
    private final String bucket;
    private final String endpoint;
    private final String accessKey;
    private final String secretKey;

    public TestLocalStackConfig(
            @NotNull @Value("${localstack.region.static}") String region,
            @NotNull @Value("${localstack.s3.bucket}") String bucket,
            @NotNull @Value("${localstack.s3.endpoint}") String endpoint,
            @NotNull @Value("${localstack.credentials.access-key}") String accessKey,
            @NotNull @Value("${localstack.credentials.secret-key}") String secretKey
    ) {
        this.region = region;
        this.bucket = bucket;
        this.endpoint = endpoint;
        this.accessKey = accessKey;
        this.secretKey = secretKey;
    }

    @Bean
    public AmazonS3 amazonS3() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withPathStyleAccessEnabled(true)
                .build();

        if (!amazonS3.doesBucketExistV2(bucket)) {
            amazonS3.createBucket(bucket);
        }

        return amazonS3;
    }

}

 

 

LocalStack 테스트 환경 구성 시 주의사항

테스트에서 @TestConfiguration을 활용해 LocalStack 환경을 설정하면,

해당 설정이 테스트 컨텍스트에 포함되어야 테스트가 정상적으로 동작한다.

 

이를 위해 @SpringBootTest와 함께 @ContextConfiguration을 사용해,

TestLocalStackConfig 클래스를 테스트 컨텍스트에 명시적으로 등록해야 한다.

 

주의사항을 적용한 테스트 클래스

@ActiveProfiles("test")
@ContextConfiguration(classes = TestLocalStackConfig.class) // 테스트 설정 추가
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class AwsS3HandlerMockTest {
	// 테스트 코드 생략...
}

 

 

Spring Boot에서 LocalStack 통합 테스트 시나리오 작성

테스트 코드에는 다음과 같이 아래 나열된 주요 시나리오를 다룬다.

 

1. S3에 파일 업로드

  • 이 테스트는 파일을 업로드한 후, 반환된 URL과 업로드된 파일의 존재 여부를 검증한다.
@Test
void 파일을_S3에_업로드하면_정상적으로_업로드된_URL을_반환한다() {
    // given
    FileMetaData metaData = MockFileData.create(TEST_DIRECTORY);

    // when
    String result = awsS3Handler.save(metaData, inputStream);

    // then
    assertThat(result).isNotNull();
    assertThat(awsS3Handler.isFileExist(getBucketPath(TEST_DIRECTORY), metaData.getDetails().getName())).isTrue();
}

 

2. S3 파일 이동

  • 파일 이동 후 새 위치에 파일이 존재하는지 확인한다.
@Test
void S3_파일을_다른_디렉토리로_이동하면_정상적으로_이동된다() {
    // given
    FileMetaData metaData = MockFileData.create(TEST_DIRECTORY);
    String savedFileUrl = awsS3Handler.save(metaData, inputStream);

    // when
    String result = awsS3Handler.move(savedFileUrl, TEST_DIRECTORY);

    // then
    assertThat(result).isNotNull();
    assertThat(awsS3Handler.isFileExist(getBucketPath(TEST_DIRECTORY), metaData.getDetails().getName())).isTrue();
}

 

3. 예외 처리

  • 존재하지 않는 파일 삭제 시 예외 발생 여부를 검증한다.
@Test
void AWS_S3_존재하지_않는_파일_삭제_시_예외_발생() {
    //given
    String nonExistentFileUrl = "https://xxxxxxxxxxxxxx.cloudfront.net/test/test.jpeg";

    // when & then
    assertThatThrownBy(() -> awsS3Handler.delete(nonExistentFileUrl))
            .isInstanceOf(CustomFileExceptionHandler.class)
            .hasMessageContaining(
                    ErrorCode.FILE_NOT_FOUND_ERROR.getMessage()
            );
}

 

4. 테스트 결과 확인하기 (EC2 Free tier 기준)

 

 

마무리

이번 포스팅에서는 LocalStack을 활용해 Spring Boot에서 AWS S3 테스트 환경을 구성하고,

파일 업로드, 이동, 삭제 등을 검증하는 테스트 코드를 작성해 보았다.

 

이번 테스트는 laaS 서버를 기준으로 진행되었으며, 다음과 같은 두 가지 관리 포인트가 발생했다.

  • 외부 서버의 docker-compose.yml 파일 관리
  • 테스트 환경 설정을 구성하기 위한 클래스 작성

 

또한, 외부 서버에 설정된 LocalStack에는 테스트에서 사용된 파일들이 업로드되거나 삭제되어 있을 가능성이 있다.

만약 다른 사용자가 같은 S3 테스트를 실행한다면, 테스트 결과의 멱등성은 보장될 수 있는가

 

다음 포스팅에서는 Testcontainers 방식의 LocalStack 테스트 구현을 통해,

laaS 기반 테스트와 비교하여 장단점을 살펴볼 예정이다.

 

 

참고 자료 : 

로컬 스택(LocalStack) 알아보기

 

로컬 스택(LocalStack) 알아보기

Intro이번 포스팅에서는 LocalStack에 대해 간단히 알아보려고 한다. LocalStack은 클라우드 애플리케이션의 개발과 테스트를 더 쉽고 빠르게 만들어주는 플랫폼으로, 최근 주목받고 있는 도구다.  Lo

dev-rowing.tistory.com

 

LocalStack Official Docs - Installation

 

Installation

Basic installation guide to get started with LocalStack on your local machine.

docs.localstack.cloud

 

LocalStack Official Organization - localstack-aws-sdk-examples

 

localstack-aws-sdk-examples/java/java-sdk-examples/java-sdk-v1/src/main/java/v1/s3/S3Service.java at main · localstack/localsta

Test repo with sample apps & tests for various different languages/SDKs using LocalStack - localstack/localstack-aws-sdk-examples

github.com

블로그의 정보

코드의 여백

rowing0328

활동하기