개발/Spring, Spring Data JPA, Querydsl

Spring - Spring <-> AWS S3 첨부 파일 업로드, 삭제 (첨부 파일 업로드 수정 및 삭제)

잇(IT) 2023. 12. 12. 03:41
728x90
1. S3 생성

 

AWS S3를 통해 bucket을 생성한다.

- bucket : 다수의 객체를 관리하는 컨테이너로 파일시스템이라고 보면 된다.

 

체크를 하지 않으면 public으로 해당 버킷에 접근 할 수 있다.

public으로 접근 가능하다는 것은 외부에서 해당 버킷에 접근이 가능하다는 뜻이다.

2. 사용자 생성 (IAM)

 

AWS IAM을 통해 사용자를 생성한다.

 

사용자의 권한 정책으로 AmazonS3FullAccess 권한을 부여해준다.

 

3. 액세스 키 생성

 

새롭게 생성한 사용자를 선택하여 액세스 키를 생성해준다.

 

생성한 액세스키, 비밀 액세스 키는 따로 확인 가능하도록 보관해놓는다.

4. 스프링 연동
4.1. build.gradle 의존성 추가
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
4.2. application.yml 설정
cloud:
  aws:
    s3:
      bucket: spring-bucket-bis
    stack.auto: false
    region.static: ap-northeast-2
    credentials:
      accessKey: AKIAWEAMPDQHPEFNKQS2
      secretKey: 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

 

1. AWS Buckeyt 이름, 2. AWS IAM을 통해 사용자 생성할 때 만들었던 액세스 키를 입력해주고, 3. region의 경우 한국에 해당하는 ap-northeast-2를 입력한다.

4.3. 스프링 설정

 

- S3Config.java

@Configuration
public class S3Config {

    @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 AmazonS3 amazonS3Client() {

        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

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

 

@Configuration을 통해 설정을 해준다.

@Value를 통해 application.yml 파일에 작성한 정보들을 가져온다.

 

@Bean
    public AmazonS3 amazonS3Client() {

        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

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

 

gradle를 통해 주입 받은 AmazonS3 클래스를 이용하며, 해당 클래스는 Amazon S3와 상호 작용할 수 있는 인터페이스를 제공한다.

BasicAWSCredentials() 객체에 액세스 키 값을 주입한다.

최종적으로 builder 패턴을 이용하여  AmazonS3 객체를 반환한다.

 

- Service

@Service
@RequiredArgsConstructor
public class S3UploadService {

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

    private final AmazonS3 amazonS3;

    public void deleteFile(String name) {
        if (name != null) {
            int i = name.lastIndexOf("/");
            String substring = name.substring(i + 1);
            amazonS3.deleteObject(bucket, substring);
        }
    }

    public String saveFile(MultipartFile multipartFile) throws IOException {

        if (multipartFile.isEmpty()) {
            return null;
        }
        String originalFilename = multipartFile.getOriginalFilename();
        String storeFileName = createStoreFileName(originalFilename);

        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(multipartFile.getSize());
        metadata.setContentType(multipartFile.getContentType());

        amazonS3.putObject(bucket, storeFileName, multipartFile.getInputStream(), metadata);
        String string = amazonS3.getUrl(bucket, storeFileName).toString();
        return string;
    }

    private String createStoreFileName(String originalFilename) {
        String uuid = UUID.randomUUID().toString();
        String ext = extracted(originalFilename);
        return uuid + "." + ext;
    }

    private String extracted(String originalFilename) {
        int pos = originalFilename.lastIndexOf(".");
        return originalFilename.substring(pos + 1);
    }
}

 

Service를 통해 클라이언트로부터 전달받은 첨부 파일 데이터를 AWS Bucket에 저장, 삭제하는 코드를 작성한다.

 

* 저장

1. saveFile의 경우 저장하는 파일의 이름은 중복되면 안되기 때문에 UUID를 통해 무작위의 이름으로 저장한다.

- createStoreFileName, extracted 메서드를 통해 클라이언트가 전송한 데이터의 이름을 변경한다.

2. amazonS3 클래스의 putObject 메서드를 이용하여 AWS Bucket에 파일을 저장할 수 있다.

- bucket : 버킷 이름

- storeFileName : bucket에 저장할 이름

- multipartFile.getInputStream() : 업로드할 파일의 데이터를 읽어오는 InputStream이며, 업로드할 파일의 내용을 제공한다.

- metadata : 선택 요소이며, 업로드할 객체에 추가 메타데이터를 포함 할 수 있다.

 

* 삭제

1. deleteFile의 경우 DB에 저장되어 있는 해당 첨부파일의 Url 주소를 가져온다. AWS에 저장된 데이터의 Url의 경우

s3://spring-bucket-bis/0ae3589c-39b7-4ad0-a234-d66b406d4fa6.jpg

위와 같이 표시 되어 있기 때문에, (saveFile 메서드에서 AWS에 저장되는 Url을 저장하였다.) 저장된 파일의 이름만 추출한다.

2. deleteObject의 파라미터로 bucket이름과 파일 이름을 넘기면 AWS Bucket에 저장된 해당 파일을 삭제하게 된다.

 

- Controller

 

* 파일 저장

@PostMapping("/new")
    public String saveContent(@Validated FreeBoardFormDto freeBoardFormDto,
                              BindingResult bindingResult,
                              Principal principal, Model model) throws IOException {

        if (bindingResult.hasErrors()) {
            return "freeBoard/newContent";
        }

        String url = s3UploadService.saveFile(freeBoardFormDto.getAttachFile());

        Member member = principalService.findMember(principal);

        freeBoardService.saveContent(freeBoardFormDto, member, url);

        return "redirect:/freeBoard";
    }

 

클라이언트로부터 freeBoardFormDto로 MultipartFile 즉, 첨부파일 데이터를 받아온다.해당 파일을 s3UploadService 즉, AWS 저장을 위한 Service의 saveFile 메서드의 파라미터로 넘긴다.

 

* 파일 삭제

@GetMapping("/{freeBoardId}/deleteFile")
    public ResponseEntity<String> deleteFile(@PathVariable("freeBoardId") Long freeBoardId) {
        FreeBoard one = freeBoardService.findOne(freeBoardId);
        FreeBoard freeBoard = freeBoardService.deleteUrl(one);
        s3UploadService.deleteFile(one.getAWSUrl());
        return ResponseEntity.ok("삭제되었습니다.");
    }

 

s3UploadService의 deleteFile 메서드에 삭제하고 싶은 파일의 이름을 넘기게 되면 해당 메서드에서 deleteObject가 실행되어 Bucket 안의 해당 파일을 삭제하게 된다.

728x90