[221024] S3로 이미지 업로드

Younseo·2022년 10월 24일
0

TIL Study

목록 보기
4/27

AWS S3로 이미지 업로드 하다..!

aws.. 다양한 서비스를 제공해주지만 잠깐 까먹으면 과금나오는.. 진짜 진짜 조심해야 한다. 성윤민짜짜의 성씨도 오늘 과금 만원이 넘게 나왔다. 모르고 방치했으면 정말 큰일날 뻔. AWS 서비스 사용할 때는 과금진짜 조심하자!!

Amazon S3란 무엇인가?

Amazon Simple Storage Service(S3)는 객체 스토리지 서비스이다. 다양한 사용 사례에서 원하는 만큼의 양의 데이터를 저장하고 보호할 수 있다. 또, 사용자의 요구사항에 맞게 데이터에 대한 엑세스를 조절할 수 있는 관리 기능을 제공한다.

S3의 기능

  1. Storage Class
    Amazon S3는 여러 사용 사례에 맞춰서 설계된 다양한 스토리지를 제공한다. 얼마나 엑세스할 것인가에 따라 나눠서 데이터를 보관할 수 있다.
    자세한 내용은 여기를 참고!
  2. Storage 관리
    비용관리,대기시간 단축, 여러 요구 사항에 맞게 여러개의 개별 데이터 복제본 저장을 수행할 수 있는 스토리지 관리 기능이 포함되어 있다.
    수명주기, 객체잠금, 복제, 배치 작업등의 기능이있다.
  3. 엑세스 관리
    ... 등 많은 기능들이 있다.

S3 설정

1. S3 버킷 생성하기

Aws console ➡️ S3 서비스 ➡️ 버킷 만들기로 이동한다. 버킷 이름을 입력하고, 모든 퍼블릭 엑세스 차단을 풀어준 후 버킷을 생성한다.


여러 조건을 설정할 수 있는데 꼼꼼하게 읽어본 후에 자신에게 맞는 것을 선택한다.

이렇게 생성된다!

2. IAM(Identity and Access Management) 만들기

S3에 접근하기 위해서 IAM 사용자에게 S3 접근 권한을 주어야 한다.
IAM 서비스 ➡️ 사용자 ➡️ 사용자 추가로 이동해서 사용자 이름을 입력하고 엑세스키 - 프로그래밍 방식 엑세스를 눌러준다.

기존 정책 직접 연결을 클릭하고, S3를 검색하여 원하는 접근권한을 선택한다. 나는 여기서 AmazonS3FullAccess를 선택하였다. 다음의 태그는 선택사항이니 입력해도 좋고 입력하지 않아도 좋다.

사용자 생성이 완료되면, Access-key와 Secret-key를 발급해주는데, 생성 직후에만 볼 수 있으니 안전한 곳에 저장해 두어야 한다.

Spring 설정하기

1. application.properties

application.properties에 추가해준다.
💥💥💥github에 올릴 때 진짜 꼭 제발 밑의 accesskey, secretkey 지우고 올리기💥💥💥
💥💥💥gitIgnore 페이지에 application.properties 꼭 꼭 넣고 올리기 💥💥💥
밑의 두 줄은 과금 방지를 위해 올릴 이미지의 파일 크기를 한정지어 준 것이다.

cloud.aws.region.static=ap-northeast-2
cloud.aws.stack.auto=false
cloud.aws.s3.bucket=mysparta4
cloud.aws.credentials.access-key= 발급받은 access키
cloud.aws.credentials.secret-key= 발급받은 secret키

spring.servlet.multipart.max-file-size: 10MB
spring.servlet.multipart.max-request-size: 10MB

2. build.gradle

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

build.gradle에 추가하고, 코끼리 눌러서 install해준다.

3. S3설정 등록

S3config.java

@Configuration
public class AwsS3Config {
    @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. 파일 업로드 class 생성

S3Uploader.java

@Component
@RequiredArgsConstructor
public class S3Uploader {
    private final AmazonS3Client amazonS3Client;
    @Value("${cloud.aws.s3.bucket}")
    private String bucket;

    public String uploadFiles(MultipartFile multipartFile, String dirName) throws IOException {
        File uploadFile = convert(multipartFile)  // 파일 변환할 수 없으면 에러
                .orElseThrow(() -> new IllegalArgumentException("error: MultipartFile -> File convert fail"));
        return upload(uploadFile, dirName);
    }

    public String upload(File uploadFile, String filePath) {
        String fileName = filePath + "/" + UUID.randomUUID() + uploadFile.getName();   // S3에 저장된 파일 이름
        String uploadImageUrl = putS3(uploadFile, fileName); // s3로 업로드
        removeNewFile(uploadFile);
        return uploadImageUrl;
    }

    // S3로 업로드
    private String putS3(File uploadFile, String fileName) {
        amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead));
        return amazonS3Client.getUrl(bucket, fileName).toString();
    }

    // 로컬에 저장된 이미지 지우기
    private void removeNewFile(File targetFile) {
        if (targetFile.delete()) {
            System.out.println("File delete success");
            return;
        }
        System.out.println("File delete fail");
    }

    // 로컬에 파일 업로드 하기
    private Optional<File> convert(MultipartFile file) throws IOException {
        File convertFile = new File(System.getProperty("user.dir") + "/" + file.getOriginalFilename());
        if (convertFile.createNewFile()) { // 바로 위에서 지정한 경로에 File이 생성됨 (경로가 잘못되었다면 생성 불가능)
            try (FileOutputStream fos = new FileOutputStream(convertFile)) { // FileOutputStream 데이터를 파일에 바이트 스트림으로 저장하기 위함
                fos.write(file.getBytes());
            }
            return Optional.of(convertFile);
        }
        return Optional.empty();
    }
}

5. Controller, Service

아래의 controller와 service는 지금 진행중인 프로젝트에서 발췌하였다.

(1) Controller
폴더명에는 버킷에 생성한 폴더의 이름을 넣어주면 된다.

    @Transactional
    public GlobalResponseDto createPost(MultipartFile multipartFile, PostRequestDto postRequestDto, Account account) throws IOException {

        String img = s3Uploader.uploadFiles(multipartFile, "폴더명");

        Post post = new Post(postRequestDto, account, null);
        postRepository.save(post);

        return new GlobalResponseDto("Success Post", HttpStatus.OK.value());
    }

(2) Service

 @Transactional
    public GlobalResponseDto createPost(MultipartFile multipartFile, PostRequestDto postRequestDto, Account account) throws IOException {

        String img = s3Uploader.uploadFiles(multipartFile, "폴더명");

        Post post = new Post(postRequestDto, account, null);
        postRepository.save(post);

        return new GlobalResponseDto("Success Post", HttpStatus.OK.value());
    }

실행해보기


Postman으로 실행해서 확인해보면, post가 파일 변환되어 잘 업로드 되고 게시글 전체조회를 하면 url로 잘 리턴되는 것을 확인할 수 있다!

0개의 댓글