Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발하는 감자

[Booker] Spring S3 연결 본문

프로젝트/Booker

[Booker] Spring S3 연결

gam_ja 2024. 7. 2. 01:10

✅ 필요성 & S3 변경 이유

booker는 독서 활동을 기록하는 플랫폼으로 기획한 만큼 '사진'으로 하는 기록이 굉장히 중요한 플랫폼이다. 따라서 많은 양의 사진을 어떻게 저장하면 좋을지 고민하다가 클라우드 스토리지를 생각하게 되었다.

처음 v3까지는 Google Cloud Platform을 사용했는데, 이번에 v4는 AWS로 배포를 하게 되면서 클라우드 스토리지 또한 S3로 변경하게 되었다. 

 

따라서 spring과 S3 연결을 하는 과정들과 배운 것들을 기록해 보고자 한다.

 

🗄️ 버킷 생성

생성은 해당 블로그를 참고하여 생성했다.

 

🍃 S3 with spring

먼저 gradle에 해당 의존성을 추가해 줬다.

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

 

applitcation.properties에 버킷에 관한 정보를 추가해 준다.

해당 정보들은 github에 연동하지 않고 사용하는 것이 좋다.

cloud.aws.s3.bucket={버킷명}
cloud.aws.stack.auto=false
cloud.aws.region.static=us-east-1
cloud.aws.credentials.accessKey={발급받은 액세스 키}
cloud.aws.credentials.secretKey={발급받은 시크릿 키}

 

- S3Config

업로드하거나 삭제할 때 AmazonS3 객체가 필요하기 때문에 S3Config 파일에서 applitcation.properties에 저장된 값을 불러와 생성해 준다.

@Configuration
public class S3Config {
    @Value("${cloud.aws.credentials.access-key}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secret-key}")
    private String accessSecret;
    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3 s3Client() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, accessSecret);
        return AmazonS3ClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region).build();
    }
}

 

@Bean으로 등록해 S3Service에서 사용 시 자동 의존관계 주입을 받는다.

- S3Service

upload 시 공식 문서를 확인해 보면, S3에 파일을 업로드할 때 File 객체를 넘겨줘야 한다.

s3.putObject(bucket_name, key_name, new File(file_path))

 

하지만 controller에서 받은 MultipartFile 객체 그대로 전송해 저장하고 싶었기 때문에 MultipartFile 객체를 받아 File로 변환 후 업로드하는 방식이 아닌, MultipartFile 객체 그대로 전송할 수 있는 코드로 구현했다.
1. 해당 블로그2. 해당 블로그를 참고해 구현하였다.

  → MultipartFile을 사용하기 위해서는 설정이 필요하다. 1번 블로그를 참고해 작성할 수 있었다.

spring.servlet.multipart.enabled = true
spring.servlet.multipart.file-size-threshold = 0B
spring.servlet.multipart.location = C:\\capstone-temp
spring.servlet.multipart.max-file-size = 50MB
spring.servlet.multipart.max-request-size = 50MB

 

- upload

  - S3Service

@Service
@Slf4j
public class S3Service {
    private final AmazonS3 amazonS3;
    private final String bucket;

    public S3Service(AmazonS3 amazonS3, @Value("${cloud.aws.s3.bucket}") String bucket) {
        this.amazonS3 = amazonS3;
        this.bucket = bucket;
    }

    public S3ResponseUploadEntity upload(MultipartFile multipartFile, String dir) throws IOException {
        String fileName = multipartFile.getOriginalFilename();

        String uuid = UUID.randomUUID().toString();
        String uniqueFileName = dir + "/" + uuid + "-" + fileName.replace("//s", "-");
        String url = "https://booker-v4-bucket.s3.amazonaws.com/";

        PutObjectRequest putObjectRequest = new PutObjectRequest(bucket,
                uniqueFileName,
                multipartFile.getInputStream(),
                getObjectMetadata(multipartFile));

        try {
            amazonS3.putObject(putObjectRequest);
        } catch (AmazonServiceException e) {
            log.error(e.getErrorMessage());
        }

        return new S3ResponseUploadEntity(uniqueFileName, url + uniqueFileName);
    }

    public ObjectMetadata getObjectMetadata(MultipartFile file) {
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(file.getSize());
        metadata.setContentType(file.getContentType());

        return metadata;
    }
}

 

프로필 사진과 독서록 사진을 구분하기 위해 dir을 넣어 폴더별로 구분할 수 있도록 했다.

 

   - S3Controller ( Test )

@RestController
@RequiredArgsConstructor
public class S3Controller {

    private final S3Service s3service;

    @PostMapping("/S3/upload")
    public S3ResponseUploadEntity upload(@RequestParam("file")MultipartFile file, String dir) throws Exception {
        if(file.isEmpty()) {
            return new S3ResponseUploadEntity("file is empty", null);
        }

        return s3service.upload(file, dir);
    }
}

 

Test를 위해 Controller를 생성했다. postman으로 버킷에 사진이 잘 들어가는 것을 확인할 수 있었다.

 

  - S3Dto

@Data
@AllArgsConstructor
@NoArgsConstructor
public class S3ResponseUploadEntity {
    String imageName;
    String imageUrl;
}

 

- delete

  - S3Service

public String delete(String fileName) throws IOException {
        try {
            amazonS3.getObjectMetadata(new GetObjectMetadataRequest(bucket, fileName));
        } catch (AmazonServiceException e) {
            throw new AmazonServiceException("This file does not exist");
        }

        amazonS3.deleteObject(bucket, fileName);
        return "Deleted complete";
}

S3Service 안에 delete도 추가해 주었다. S3는 버킷 안에 존재하지 않는 객체를 삭제해도 에러를 내지 않는다. 따라서 버킷 안에 삭제하려는 객체가 있는지 먼저 확인하고, 객체가 없다면 에러를 반환하는 코드로 구현했다.

 

  - S3Controller ( Test )

@PostMapping("/S3/delete")
    public String delete(String fileName) throws IOException {
        return s3service.delete(fileName);
}

 

Test를 위한 해당 Controller 코드로 없는 파일을 삭제 시 에러가 잘 반환되는 것을 확인할 수 있었다.

 

 

✅ 마무리

해당 과정을 통해 생성한 S3 버킷에 객체를 저장하고 삭제하는 코드를 구현할 수 있었다.

GCP를 구현할 때는 알아보고 이해하기보다 블로그에 나와있는 이미 구현되어 있는 코드를 따라가기 바빠 정작 완성된 코드를 나중에 봤을 때 완벽하게 이해하지 못했다. 이번엔 구현하면서 새로 알게 된 것들을 찾아보는 시간이 많이 들었지만, 그만큼 코드를 이해하고 구현한 것 같아 좋았다.

 

📝 참고 자료

https://gaeggu.tistory.com/33

 

[Spring boot] AWS S3 를 이용한 파일 업로드

개인 프로젝트를 하며 이미지 서버로 사용하기 위해 AWS S3 버킷을 만들고 Spring 을 연동하는 방법을 정리해보았다. [1] AWS S3 개요 AWS S3 란? S3는 Simple Storage Service 의 약자로 주로 파일 서버로 사용

gaeggu.tistory.com

 

https://docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/examples-s3-objects.html#upload-object

 

Amazon S3 객체에 대한 작업 수행 - AWS SDK for Java 1.x

copyObject와 deleteObject를 함께 사용하면 먼저 객체를 새 이름으로 복사한 다음(동일한 버킷을 소스와 대상으로 모두 사용 가능) 이전 위치에서 해당 객체를 삭제하는 방식으로 객체를 이동하거나

docs.aws.amazon.com

https://techblog.woowahan.com/11392/

 

Spring Boot에서 S3에 파일을 업로드하는 세 가지 방법 | 우아한형제들 기술블로그

Spring Boot에서 S3에 파일을 업로드하는 세 가지 방법 | 안녕하세요. 세일즈서비스팀에서 전자계약서 시스템을 개발하고 있는 박민규입니다. 최근 저는 Spring Boot + Kotlin을 활용한 프로젝트에서

techblog.woowahan.com

https://celdan.tistory.com/41

 

[SpringBoot] SpringBoot로 AWS S3 Bucket에 이미지 업로드

본격적으로 프로젝트에서 이미지를 S3 bucket으로 업로드 하기 위해 업로드 테스트 스크립트를 작성해 보았다. 1. bulid.graddle //aws implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' implem

celdan.tistory.com