[AWS] S3 생성 및 Spring Boot 연동

2023. 3. 16. 02:19기술 창고/AWS

728x90
SMALL

아마존 S3는 클라우드 형식으로 이미지나 동영상같은 미디어 자원들을 저장하고 관리할 수 있는 도구이다.

어플리케이션을 개발할 때 이미지를 업로드 하거나 다운받을 수 있도록 쉽게 관리할 수 있다.

Spring Boot 와 연동하는 방법을 알아보자.

 

 

(1) 아마존 계정 로그인

https://aws.amazon.com/ko/console/

 

AWS Management Console

AWS Support 플랜은 AWS로 성공하는 데 도움이 되는 다양한 도구, 프로그램 및 전문 지식에 대한 액세스의 조합을 제공합니다.

aws.amazon.com

아마존 사이트에 들어가서 로그인한다.

계정이 없다면 회원가입은 필수이다.

 

 

 

(2) S3 버킷 생성

S3 항목에 진입한다.

  • 버킷 이름 : 사용할 버킷의 이름을 지정한다.
  • AWS 리전 : AWS 사용 국가 지역을 설정해준다.
    서울에 살기 때문에 아시아 태평양(서울) ap-northeast-2 로 지정해준다.

 

  • ACL 활성화 체크 : 이후에 정책 생성을 할 때 혹시나 편집이 필요한 경우 해당 옵션을 활성화하지 않으면 진행되지 않기 때문에 활성화 체크가 필요하다.

 

 

  • 모든 퍼블릭 액세스 차단을 전부 해제한다.
  • 밑에 퍼블릭 상태가 될 수 있음을 확인하는 부분도 체크해준다.
  • 버킷을 생성해준다.

 

 

 

(3) 버킷 정책 생성

  • 생성한 버킷의 이름을 눌러 진입한다.

 

  • 진입한 버킷 안에서 권한 탭을 누른다.

 

  • 버킷 정책 항목에 우리의 버킷 사용 정책을 넣어줄 것이다.
    편집 버튼을 눌러준다.

 

  • 버킷 ARN이 현재 나의 s3 공식 버킷 명이다. 복사해주자.
  • 정책 생성기를 눌러 생성 페이지로 진입한다.

 

  • Select Type of Policy 에 S3 Bucket Policy로 지정한다.
  • Effect 는 Allow(허용)을 선택한다.
  • Principal은 arn:aws:iam:: 으로 특정 계정을 지정해줄 수 있다.
    앞서 Effect에서 허용을 선택하고 arn:aws:iam:: 으로 특정 계정을 지정해주었다면 해당 계정에 대해서 접근 허용을 설정해준 것이다.
    Deny는 반대로 허용하지 않겠다는 의미이다.
    arn:aws:iam:: 대신에 *을 입력하면 모든 계정에 대해서 적용된다.
  • All Actions를 체크해준다.
  • Amazon Resource Name (ARN)에 앞서 복사했던 나의 s3 공식 버킷명을 넣어주고 그 뒤에 /* 를 붙여준다.
  • 마지막으로 Add Statement 버튼을 눌러 정책을 생성한다.
  • 생성을 하게되면 json 형식으로 생성된 정책 화면이 보이게 될 것이다.

 

  • 생성된 json 형식의 정책을 복사하여 앞선 정책 생성기 버튼을 눌렀던 페이지의 버킷 정책 항목의 편집으로 들어가 붙여넣기 해주고 변경 사항 저장을 눌러준다.

 

 

(4) IAM 추가 및 설정

  • IAM에 들어와서 사용자 항목에 사용자 추가를 눌러준다.

 

  • 사용자 이름을 입력하고 생성해준다.

 

  • 직접 정책 연결을 눌러주고
  • 밑에 생긴 권한 정책 항목에 S3F 를 검색하여 AmazonS3FullAccess를 선택한다.

 

 

  • 이때까지 설정한 정보들을 확인하고 사용자를 생성한다.

 

 

  • 다시 IAM의 사용자 항목으로 돌아와서 생성한 사용자를 눌러 진입한다.

 

  • 보안 자격 증명 항목을 선택한다.

 

 

  • 보안 자격 증명 항목의 액세스 키 만들기 버튼을 누른다.
    해당 액세스 키가 있어야지만 aws s3 기능에 액세스하여 사용할 수 있다.

 

 

  • Command Line Interface(CLI) 를 선택하고 권장 사항 확인을 체크해준다.

 

 

  • 설명 태그 값에 자기가 원하는 태그 값을 아무거나 입력한다.

 

 

  • 이제 csv 파일 다운로드를 받아 accesskey 와 secretkey 를 확인한다.
  • 반드시 파일 다운로드를 눌러줘야한다. 그냥 완료를 누르게 되면 다시는 키를 확인하지 못하고 다시 생성해주어야 한다.

이제 AWS 에서의 설정은 마무리 되었다.

Spring Boot 설정을 진행해보자.

 

 

 

(5) Spring Boot 에서 설정

application.yml

cloud:
  aws:
    region:
      static: {앞서 aws를 사용하는 국가의 리전. 나의 경우 ap-northeast-2}
    s3:
      bucket: {생성한 버킷명. arn:aws:s3는 빼고 버킷명만 입력}
    stack:
      auto: false
    credentials:
      secret-key: {방금 생성한 secret key}
      access-key: {방금 생성한 access key}

application.yml 파일을 만들어주어 AWS에서 생성한 일부 설정값들을 관리해준다.

 

 

AwsConfig

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 AwsS3Config {

    // S3를 등록한 사람이 전달받은 접속하기 위한 key 값
    @Value("${cloud.aws.credentials.access-key}")
    private String accessKey;

    // S3를 등록한 사람이 전달받은 접속하기 위한 secret key 값
    @Value("${cloud.aws.credentials.secret-key}")
    private String secretKey;

    // S3를 등록한 사람이 S3를 사용할 지역
    @Value("${cloud.aws.region.static}")
    private String region;

    // 전달받은 Accesskey 와 SecretKey 로 아마존 서비스 실행 준비
    @Bean
    public AmazonS3Client amazonS3Client() {
        BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
        return (AmazonS3Client) AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                .build();
    }
}

aws 설정 클래스를 만들어준다.

accesskey와 secretkey, region 정보를 통해 aws s3 서비스를 실행할 준비를 한다.

 

 

MediaUpload

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

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

@RequiredArgsConstructor
@Service
public class MediaUpload implements MediaUploadInterface {

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

    private final AmazonS3 amazonS3;

    private final MediaRepository mediaRepository;

    @Override
    public Media uploadMedia(MultipartFile media, String mediaPurpose) {

        String mediaName = createFileName(media.getOriginalFilename()); // 각 파일의 이름을 저장

        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(media.getSize());
        objectMetadata.setContentType(media.getContentType());

        System.out.println("for each 진입 : " + mediaName);

        try (InputStream inputStream = media.getInputStream()) {
            // S3에 업로드 및 저장
            amazonS3.putObject(new PutObjectRequest(bucket, media.getOriginalFilename(), inputStream, objectMetadata)
                    .withCannedAcl(CannedAccessControlList.PublicRead));
        } catch (IOException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드에 실패했습니다.");
        }

        // 접근가능한 URL 가져오기
        String mediaUrl = amazonS3.getUrl(bucket, mediaName).toString();

        // 동시에 해당 미디어 파일들을 미디어 DB에 이름과 Url 정보 저장.
        // 게시글마다 어떤 미디어 파일들을 포함하고 있는지 파악하기 위함 또는 활용하기 위함.
        Media uploadMedia = Media.builder()
                .mediaRealName(media.getOriginalFilename())
                .mediaUidName(mediaName)
                .mediaType(media.getContentType())
                .mediaUrl(mediaUrl)
                .mediaPurpose(mediaPurpose)
                .build();

        mediaRepository.save(uploadMedia);

        return uploadMedia;
    }

    // S3에 저장되어있는 미디어 파일 삭제
    @Override
    public void deleteFile(String fileName) {
        amazonS3.deleteObject(new DeleteObjectRequest(bucket, fileName));
    }


    // 파일 업로드 시, 파일명을 난수화하기 위해 random으로 돌린다. (현재는 굳이 난수화할 필요가 없어보여 사용하지 않음)
    @Override
    public String createFileName(String fileName) {
        return UUID.randomUUID().toString().concat(getFileExtension(fileName));
    }


    // file 형식이 잘못된 경우를 확인하기 위해 만들어진 로직이며, 파일 타입과 상관없이 업로드할 수 있게 하기 위해 .의 존재 유무만 판단하였습니다.
    @Override
    public String getFileExtension(String fileName) {
        try {
            return fileName.substring(fileName.lastIndexOf("."));
        } catch (StringIndexOutOfBoundsException e) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "잘못된 형식의 파일(" + fileName + ") 입니다.");
        }
    }
}

s3를 사용하기 위한 설정은 앞에서 다 완료하였고 MediaUpload 클래스의 경우에는 내가 만든 미디어 파일 업로드 서비스이다.

따라서 내가 지금 만든 것처럼 굳이 따라할 필요는 없을 것이다.

중점은 uploadMedia 메소드이다.

application.yml에서 설정한 bucket 정보를 여기서 사용하고, amazons3.putObject 를 이용하여 s3 에 파일을 업로드하고, 동시에 DB에도 파일을 저장한다.

어플리케이션을 실행하고 서비스를 실행해보면,

 

 

버킷에 파일이 무사히 들어온 것을 확인할 수 있다.

DB에도 저장이 되어있고, s3에 업로드된 이미지의 url을 같이 저장해두었기 때문에 프론트 단에서 해당 url을 src 속성을 이용해 구현하면 해당 이미지가 나올 것이다.

728x90
반응형
LIST