SpringBoot에 AWS S3 적용하기

꺄악 운석이다·2023년 9월 10일

사실 이전에도 똑같은 주제로 글을 올린 적이 있었는데

io.awspring.cloud:spring-cloud-aws-s3:2.xx 버전에서
io.awspring.cloud:spring-cloud-aws-s3:3.xx 에서 코드가 바뀐 부분이 있어 다시 올리게 되었다.

※ 이 글은 S3 bucket 까지 설정했다는 가정 하에 쓴 글입니다.

1. build.gradle

//aws s3 연동
	implementation 'io.awspring.cloud:spring-cloud-aws-s3:3.0.2'

dependencies에 추가해주면 된다.

2. yml 설정

spring:
  cloud:
    aws:
      credentials:
        accessKey: <YOUR_ACCESS_KEY>
        secretKey: <YOUR_SECRET_KEY>
      s3:
        bucket: <BUCKET_NAME>
      region:
        static: <REGION>
      stack:
        auto: false

accessKey, secretKey, bucketName, region 을 입력해주면 된다.

🚨🚨🚨 주의사항 🚨🚨🚨
Github 업로드 시 절대 public 한 repository에 올리면 안된다.

.gitignore에 application.yml을 추가하고 업로드를 하여 accessKey secretKey 가 노출되지 않도록 해야 한다.

3. AWSConfig

S3Client에 accessKey, bucketName, region을 주입시켜야 한다.

@Configuration
public class AWSConfig {
    @Value("${spring.cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${spring.cloud.aws.credentials.secretKey}")
    private String accessSecret;

    @Value("${spring.cloud.aws.region.static}")
    private String region;

    @Bean
    public S3Client s3Client() {
        return S3Client.builder()
                .credentialsProvider(this::awsCredentials)
                .region(Region.of(region))
                .build();
    }

    private AwsCredentials awsCredentials() {
        return new AwsCredentials() {
            @Override
            public String accessKeyId() {
                return accessKey;
            }

            @Override
            public String secretAccessKey() {
                return accessSecret;
            }
        };
    }
}

4. CommonUtils

파일 이름을 설정해주는 클래스이다.

public class CommonUtils {
    public static final String FILE_EXTENSION_SEPARATOR = ".";

    public static String getFileName(String originalFileName) {
        int fileExtensionIndex = originalFileName.lastIndexOf(FILE_EXTENSION_SEPARATOR);
        return originalFileName.substring(0, fileExtensionIndex); //파일 이름
    }

    public static String buildFileName(String originalFileName) {
        int fileExtensionIndex = originalFileName.lastIndexOf(FILE_EXTENSION_SEPARATOR); //파일 확장자 구분선
        String fileExtension = originalFileName.substring(fileExtensionIndex); //파일 확장자
        String fileName = originalFileName.substring(0, fileExtensionIndex); //파일 이름
        String now = String.valueOf(System.currentTimeMillis()); //파일 업로드 시간

        return fileName + "_" + now + fileExtension;
    }
}

5. AWSService

@Slf4j
@RequiredArgsConstructor
@Component
public class AwsS3Service {
    private final S3Client s3Client;

    @Value("${spring.cloud.aws.s3.bucket}")
    private String bucketName;

    public String uploadFile(MultipartFile multipartFile) {

        if(multipartFile.isEmpty()) {
            log.info("image is null");
            return "";
        }

        String fileName = getFileName(multipartFile);

        try {
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .contentType(multipartFile.getContentType())
                    .contentLength(multipartFile.getSize())
                    .key(fileName)
                    .build();
            RequestBody requestBody = RequestBody.fromBytes(multipartFile.getBytes());
            s3Client.putObject(putObjectRequest, requestBody);
        } catch (IOException e) {
            log.error("cannot upload image",e);
            throw new RuntimeException(e);
        }
        GetUrlRequest getUrlRequest = GetUrlRequest.builder()
                .bucket(bucketName)
                .key(fileName)
                .build();

        return s3Client.utilities().getUrl(getUrlRequest).toString();
    }

    public String getFileName(MultipartFile multipartFile) {
        if(multipartFile.isEmpty()) return "";
        return CommonUtils.buildFileName(multipartFile.getOriginalFilename());
    }
}

Postman으로 테스트한 결과 성공적으로 업로드 된 것을 볼 수 있었다.

profile
멸종은 면하자

0개의 댓글