본문 바로가기
TIL : Today I learned (or Week)

TIL 230817 : AWS S3 (Simple Storage Service) 1 - 세팅, 객체업로드

by 우인입니다 2023. 8. 22.

AWS에서 제공하는 여러가지 서비스가 있다라는 걸 이제야 파악이 된다.

그 중에서 Simple Storage Service. 줄여서 S3서비스를 해보려한다.

기본 세팅부터 파일 업로드 하는 용량 설정 까지 해본다.

 

 


1. 버킷 생성 및 권한 설정

기본적으로 버킷을 생성해야 한다.

클라우드에 있는 외장하드 개념이라고 생각하면 쉽다.

정확히는 최상위의 컨테이너.

 

자세한 과정은 해당 블로그 참고

https://gaeggu.tistory.com/33

 

 

 

2. Spring 의존성 추가

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

 

 

3. properties 파일 추가

 

application.properties

기본 application.properties파일에 해당 코드를 추가해준다. (여기서는 s3가 해당)

 

그리고 위처럼 application-s3.properties 이름을 지어주면 안에 있는 내용이 포함되어 적용된다.

그러고 gitignore에 추가해주면 안에 시크릿 키나 기타 정보들을 공유하지 않을 수 있다.

 

application-s3.properties

해당 파일에 위처럼 세팅해주면 해당 세팅값으로 연동된다.

 

 

 

cloud.aws.stack.auto=false

EC2에서 Spring Cloud 프로젝트를 실행시키면 기본으로 CloudFormation 구성을 시작하기 때문에 설정한 CloudFormation이 없으면 프로젝트 실행이 되지 않는다. 해당 기능을 사용하지 않도록 false로 설정.

 

 

4. S3Config.java 생성

@Configuration
public class AmazonS3Config {

  @Value("${cloud.aws.credentials.accessKey}")
  private String accessKey;

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

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

  @Bean
  public AmazonS3Client amazonS3Client() {
    BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

    return (AmazonS3Client) AmazonS3ClientBuilder.standard()
        .withRegion(region)
        .withCredentials(new AWSStaticCredentialsProvider(credentials))
        .build();
  }
}

이건 거의 고정인 것 같다.

 

5. Upload 클래스 생성

이제 실제로 S3 서버에 요청을 할 수 있는 서비스 클래스를 생성한다.

파일을 업로드하거나 수정, 삭제 등을 구현해볼 수 있다.

 

@Service
@RequiredArgsConstructor
public class S3UploadService {

    // S3 연동하기 위한 주입받아오기
    private final AmazonS3 amazonS3;

    //버킷이름 받아오기
    @Value("${cloud.aws.s3.bucket}")
    private String bucket;
    ...
}

의존성을 통해 추가한 AmazonS3를 주입받아온다.

(다른 코드에서는 직접 AmazonS3Clinet를 주입받아오기도 했는데, 무슨 차이인지는 공부가 더 필요하다)

 

그리고 버킷의 이름도 String값으로 담아온다.

위에서는 properties파일로 따로 보관하여 사용할 수 있게 처리했다.

 

 

5-1. 파일업로드 메소드 (putObject)

아까 주입받아온 amazonS3가 가지고 있는 메소드들을 대략 훑어보자.

기본적으로 put, copy, delete Object기능부터 버킷에 대한 정보를 가져오는 등 S3 서버에 연동할 수 있는 대부분의 동작들이 있다.

(편하게 Open API를 쓸 수 있는 기분..?)

 

이 중에서 우선 업로드를 위해 putObject를 사용해 볼 거고, 파라미터를 어떻게 받는 지 살펴보면 아래와 같다.

 

1. PutObjectRequest

2. 버킷이름, 파일이름, 파일

3. 버킷이름, 파일이름, 인풋스트림, 메타데이터

4. 버킷이름, 파일이름, 콘텐트

 

 

PutObjectRequest 를 생성해서 파라미터를 확인해보면, 결국에 필요한 데이터는 아래와 같다.

  • 필수) 버킷이름, 키(객체이름)
  • 선택) File, String, InputSteam+metadata

redirectLocation이 의미하는 바는 다음에 알아봐야 할 듯 하다.

 

여기서는 Multipart 데이터를 바로 인풋스트림으로 변환해줘서 올려보고자 해서, 

3.버킷이름, 파일이름, 인풋스트림, 메타데이터 방법으로 시도해본다.

 

 

...
//버킷이름 받아오기
@Value("${cloud.aws.s3.bucket}")
private String bucket;

public String saveFile(MultipartFile multipartFile) throws IOException {

    //객체이름으로 사용할 파일 이름 가져오기
    String originalFilename = multipartFile.getOriginalFilename();
    
    //멀티파트파일 -> 인풋스트림으로 변환
    InputStream inputStreamFile = multipartFile.getInputStream();    


    //putObject 메소드 실행
    amazonS3.putObject(bucket, originalFilename, inputStreamFile, metadata);

...
}

위처럼 버킷이름, 키(객체를 찾을 수 있는 객체이름), 파일은 세팅이 됐다.

 

메타데이터를 설정해줘야 한다.

의존성을 통해 가져온 ObjectMetadata 사용해서 객체를 생성해준다.

        //메타데이터 생성
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(multipartFile.getSize());
        metadata.setContentType(multipartFile.getContentType());

다양한 값들을 담아서 넘겨줄 수 있는 걸 확인할 수 있다.

 

아래는 AWS 공식사이트에서 안내하고 있는 SDK를 활용한 객체 업로드의 예문이다.

https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/upload-objects.html

 

PutObjectRequest request = new PutObjectRequest(bucketName, fileObjKeyName, new File(fileName));
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType("plain/text");
            metadata.addUserMetadata("title", "someTitle");
            request.setMetadata(metadata);
            s3Client.putObject(request);

 

참고해서 모든 걸 정리하면 아래와 같다.

public class S3UploadService {

    // S3 연동하기 위한 주입받아오기
    private final AmazonS3 amazonS3;

    //버킷이름 받아오기
    @Value("${cloud.aws.s3.bucket}")
    private String bucket;


    public String uploadFile(MultipartFile multipartFile) throws IOException {

        //파일 이름 가져오기.
        String originalFilename = multipartFile.getOriginalFilename();

        //멀티파트파일 -> 인풋스트림으로 변환
        InputStream inputStreamFile = multipartFile.getInputStream();

        //메타데이터 생성
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(multipartFile.getSize());
        metadata.setContentType(multipartFile.getContentType());


        //putObject 메소드 실행
        amazonS3.putObject(bucket, originalFilename, inputStreamFile, metadata);
        return amazonS3.getUrl(bucket, originalFilename).toString();
    }
}

 

 

6. Controller 생성

http요청을 받아올 Controller를 생성할 시간이다.

 

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class S3TestController {

    private final S3UploadService s3UploadService;

    @PostMapping("/s3-test/upload")
    public ResponseEntity<ApiResponseDto> uploadFile(@RequestPart(value = "file") MultipartFile file) throws IOException {

        String fileUrl = s3UploadService.uploadFile(file);

        return ResponseEntity.ok().body(new ApiResponseDto(HttpStatus.OK.value(), fileUrl));
    }
}

여기서 잊으면 안되는 것은 

@RequestPart(value = "file") 어노테이션이다.

 

7.Postman 테스트

응답 값으로 해당 URL을 리턴받을 수 있었다.

 

AWS콘솔에서도 확인할 수 있었다.

 

 


느낀 점

 

AWS 공식문서를 보니까 굉장히 디테일하게 커스텀할 수 있는 요소들이 많아보였다.

발전의 요소들이 굉장히 많다는 것들 정도만 알고 우선 넘어가도록 한다.

 

그리고, 처음이라 갑자기 등장한 인터페이스나, 클래스들이 많아서 당황스러웠는데

하나하나 출처를 확인하고 어디서 등장한 것인지 의존성으로 통해 추가된 것인지, 자바 기본으로 제공하는 것인지 등

오묘조묘 따져보며 확인하는 것만으로도 굉장히 쾌적했다.

 

  • 처음보는 클래스, 인터페이스들의 출처를 확인하는 것이 조금이나마 도움이 됐다.
  • .도트 연산자를 찍어보고 각 파라미터값을 확인해보며 구조를 파악하는 것이 도움이 됐다.
  • 적정 수준까지 파고들고 어느정도 이후로는 유보하는 것이 적절했다.

 

참고링크