프로젝트

[AWS/S3] Spring에서 S3에 데이터 저장하기

SY 키키 2024. 5. 19. 00:02

 

프로젝트를 진행하면 이미지를 서버에 저장해야 하는 경우가 많은데, 이럴 때 많은 사람들이 AWS의 S3를 이용하여 데이터를 저장하고, 이 링크를 DB에 저장하여 사용하는 경우가 많습니다.

왜냐하면 프로젝트 서버 내에 직접 파일을 적재하면 용량이 많이 필요하기 때문입니다. 

 

그래서 이번 포스팅에서는 AWS S3 버킷을 생성한 후, Spring을 이용해 데이터를 저장해보도록 하겠습니다.

 

s3 버킷 생성

1. AWS > S3 > 버킷 > 버킷 만들기 클릭

또는 다음 주소에 접속합니다.

https://ap-northeast-2.console.aws.amazon.com/s3/bucket/create?region=ap-northeast-2

 

2. 버킷 생성

버킷을 생성하기 위해 이름을 입력합니다.

 

액세스 차단 설정의 경우에는 모든 퍼블릭 액세스 차단을 해제해주세요.

 

3. 버킷 정책 설정

퍼블릭IP에서 데이터를 조회할 수 있도록 정책을 설정해주어야 합니다.

그러기 위해 S3 > 버킷 > 권한 > 버킷 정책 > 편집을 클릭합니다.

 

정책 설정을 입력합니다. 저는 다음과 같이 입력해주었고, 입력시 모든 주석을 삭제해야합니다.

{
    "Version": "2012-10-17",
    "Id": "Policy1464968545158",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow", // 허용
            "Principal": "*",
            "Action": "s3:GetObject", // 객체 읽기 권한
            "Resource": "arn:aws:s3:::<버킷명>/*"
        },
        {
            "Sid": "DenyOtherAccess",
            "Effect": "Deny", // 차단
            "Principal": "*",
            "Action": "s3:PutObject", // 객체 업로드 권한
            "NotResource": [
                "arn:aws:s3:::<버킷명>/*.jpg",
                "arn:aws:s3:::<버킷명>/*.png",
                "arn:aws:s3:::<버킷명>/*.jpeg",
                "arn:aws:s3:::<버킷명>/*.gif"
            ] // 해당 확장자를 가지지 않은 객체
        }
    ]
}

 

 

사용자(I AM 자격증명) 설정

1. AWS > IAM > 엑세스 관리 > 사용자 > 사용자 추가를 클릭합니다.

 

2. 사용자 이름 설정

3. 권한 설정

직접 정책 연결 > AmazonS3FullAccess을 설정합니다.

 

4. 사용자 생성 누르기

 

액세스 키 생성

1. AWS > IAM > 사용자 > 액세스 키 만들기를 선택합니다.

 

2. 액세스 키 모범 사례 및 대안

아무거나 선택 후 계속해도 됩니다. 저는 로컬 코드를 선택해주었습니다.

 

3.액세스 키, 비밀 액세스키 확인

사용자가 생성이 되었으면, 액세스키를 생성할 수 있습니다. 여기서 비밀 액세스키는 다시 볼 수 없기 때문에 csv 파일로 받아두는 것을 추천합니다.

 

spring 설정

1. 의존성 추가

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

 

2. application.yml

cloud:
  aws:
    credentials:
      accessKey: ${AWS_ACCESS_KEY_ID}       # AWS IAM AccessKey 적기
      secretKey: ${AWS_SECRET_ACCESS_KEY}   # AWS IAM SecretKey 적기
    s3:
      bucket: 버킷명  # 예) myawsbucket
      dir: S3 디렉토리 이름 # 예) /home
    region:
      static: ap-northeast-2
    stack:
      auto: false

 

3. S3Config

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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 AmazonS3Client amazonS3Client() {
        BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey,secretKey);
        return (AmazonS3Client) AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                .build();
    }
}

 

4. S3Service

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ObjectMetadata;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.UUID;

@RequiredArgsConstructor
@Service
public class S3ServiceImpl implements S3Service {

    private final AmazonS3 amazonS3;

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

    // 파일 업로드
    @Override
    public String uploadFile(MultipartFile multipartFile, String url) throws IOException {
        String fileName = url+"/"+UUID.randomUUID().toString() + "_" + multipartFile.getOriginalFilename();
        ObjectMetadata objMeta = new ObjectMetadata();
        objMeta.setContentLength(multipartFile.getSize());
        amazonS3.putObject(bucket, fileName, multipartFile.getInputStream(), objMeta);

        return amazonS3.getUrl(bucket, fileName).toString();
    }

    // 파일 삭제
    @Override
    public boolean delete(String fileUrl) {
        try {
            String[] temp = fileUrl.split("/");
            String fileKey = temp[temp.length-1];
            amazonS3.deleteObject(bucket, fileKey);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

 

5. controller

데이터를 받을 때에는 @RequestPart로 받으면 됩니다.

import com.restgram.domain.feed.dto.request.AddFeedRequest;
import com.restgram.domain.feed.service.FeedService;
import com.restgram.global.exception.entity.CommonResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
@RequestMapping("/feed")
@Slf4j
public class FeedController {

    private final FeedService feedService;

    @PostMapping
    public CommonResponse postFeed(Authentication authentication, @RequestPart AddFeedRequest req, @RequestPart("images") List<MultipartFile> images)) {
        ...
    }

}

 


POSTMAN을 이용하여 테스트를 해본 결과 S3에 데이터가 잘 저장됨을 확인할 수 있습니다.

 

 

 

참고

https://velog.io/@shwj203/Spring-AWS-S3-%EC%83%9D%EC%84%B1-SpringBoot-%EC%97%B0%EB%8F%99