[AWS] Spring Boot S3 이미지 업로드 (2)

LDB·2025년 6월 20일
0

AWS

목록 보기
4/4
post-thumbnail

사전작업

해당 게시글에서는 본격적으로 SpringBoot에 코드를 작성하여 실질적으로 s3에 이미지 혹은 파일을 업로드하는데 사전작업이 필요합니다.

  • s3 버킷 생성
  • IAM 사용자 생성

사전작업방법은 하단의 링크를 참고하시기 바랍니다.

https://velog.io/@half-phycho/AWS-Spring-Boot-S3-이미지-업로드-1


SpringBoot 코드 작성

사전작업을 마치셨다면 이제 본격적으로 코드를 작성해보겠습니다, SpringBoot에서는 다음과 같은 설정을 거쳐야합니다.

  • application.properties 파일에 사용자 및 버킷 등록
  • gradle에 Amazon S3 의존성 추가
  • Amazon S3 config 파일 생성
  • Amazon S3에 파일을 등록하는 기능 클래스 생성
  • Spring Boot service layer에서 활용

이 게시글에서는 다음과 같은 환경에서 제작했습니다.

OS : window11
Framework : Spring Boot 3.4.5
gradle : 8.8

properties 파일 작성

제일먼저 application 파일에 사전에 만든 IAM사용자 정보와 S3 버킷의 정보를 등록해야 합니다.

# Amazon S3 config
cloud.aws.credentials.accessKey=
cloud.aws.credentials.secretKey=
cloud.aws.s3.bucket=
cloud.aws.region.static=
cloud.aws.stack.auto=false

# 파일이 업로드 되는 위치
file.upload.url=
  • cloud.aws.credentials.accessKey : IAM 사용자 Access key를 입력합니다.
  • cloud.aws.credentials.secretKey : IAM 사용자 Secret key를 입력합니다.
  • cloud.aws.s3.bucket : 생성한 S3 버킷의 이름을 입력합니다.
  • cloud.aws.region.static : 버킷을 생성한 리전을 입력합니다. 만약 한국리전인 경우 ap-northeast-2를 입력하면 됩니다.
  • cloud.aws.stack.auto=false (선택) : 해당 설정은 EC2에 배포하는 용도인 경우에 설정하면 됩니다. EC2는 기본적으로 CloudFormation을 구성합니다. 그래서 사용할지 안할지를 선택하는데 해당 게시글에서는 사용하지 않기 때문에 false로 설정하겠습니다.

    CloudFormation : AWS 리소스를 생성하기 위한 각종 설정을 템플릿 파일로 만들어서 사용하는 도구입니다.

(IAM의 Access Key, Secret Key를 분실한 경우 사용자에서 액세스 키를 추가로 생성해야 합니다.)


Amazon S3 의존성 추가

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

해당 AWS를 연결하는 의존성을 추가합니다.


Amazon S3 config 파일 생성

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

@Configuration
public class s3SetConfig {

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

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

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

    @Bean
    public AmazonS3Client amazonS3Client() {
        
        // 임시로 자격증명
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); 

        return (AmazonS3Client) AmazonS3ClientBuilder // S3용 빌더
                .standard() // 기본 
                .withCredentials(new AWSStaticCredentialsProvider(credentials)) // IAM 사용자
                .withRegion(region)  // S3 버킷이 위치한 리전
                .build();
    }
}

해당 config파일을 보면 먼저 properies파일에 입력한 IAM의 accessKey, secretKey, region을 가져옵니다. 그리고 amazonS3Client Bean 객체를 등록하고 AWSCredentials 객체에 사용자를 등록한 후 리턴값으로 S3 빌더를 생성하는 코드 입니다.


S3에 파일을 등록하는 기능 클래스 생성

이제 S3에 파일을 저장하는 클래스를 만들어 보겠습니다.

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

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.PutObjectRequest;

import lombok.extern.slf4j.Slf4j;

/**
 * S3 Bucket에 포스터 이미지 파일 업로드 class
 */
@Component
@Slf4j // 로그 기록 목적으로 추가한 lombok의 어노테이션 입니다.
public class awsfileUtil {
    
    // Amazon S3 버킷 이름
    @Value("${cloud.aws.s3.bucket}")
    private String filebucket;
	
    // 파일 업로드 경로
    @Value("${file.upload.url}")
    private String fileUrl;
    
    private final AmazonS3Client amazonS3Client;

    public awsposterfileUtil(AmazonS3Client amazonS3Client){
        this.amazonS3Client = amazonS3Client;
    }
	
    public String Upload(MultipartFile multipartFile){

        UUID uuid = UUID.randomUUID();
        
        // UUID 난수 + _ + 파일 원본 이름 
        String fileName = uuid + "_" + multipartFile.getOriginalFilename();
        String uploadImageUrl; // s3 저장경로
        File saveFile = new File(fileUrl, fileName); // 파일 위치정보
	
        if(!saveFile.getParentFile().exists()){ // 폴더 존재 여부 체크
            saveFile.getParentFile().mkdir(); // 폴더가 없으면 폴더 생성
        }

        try{
            multipartFile.transferTo(saveFile);// 생성한 파일 경로에 저장
            // 파일 업로드 후 S3경로 리턴
            uploadImageUrl = this.S3upload(saveFile, fileName);
        } catch(IOException e) { // 저장 실패
            saveFile.delete(); 
            uploadImageUrl = ""; 
            e.printStackTrace();
        }

        return uploadImageUrl; // s3 bucket에 올린 파일 URL 혹은 "" 리턴
    }

    /**
     * Amazon S3에 파일 업로드 후 폴더의 원본파일 삭제
     * 
     * @param uploadFile
     * @param fileName
     * @return String
     */
    private String S3upload(File uploadFile, String fileName){

        /**
         * 로컬 업로드 -> S3upload(저장한 로컬파일 위치, 새로 지정한 파일이름)
         * -> 로컬 업로드 파일 삭제
         */

        String uploadImageUrl = putS3(uploadFile, fileName); // s3 업로드
        
        if(uploadFile.delete()) { // 기존 로컬 환경에 업로드한 파일 삭제
            log.info("파일이 삭제되었습니다.");
        }else {
            log.info("파일이 삭제되지 못했습니다.");
        }

        return uploadImageUrl; // s3 bucket에 올린 파일 URL 리턴
    }

    /**
     * Amazon S3에 파일 업로드
     * 
     * @param uploadFile
     * @param fileName
     * @return String
     */
    private String putS3(File uploadFile, String fileName) {
        amazonS3Client.putObject( // s3 파일 업로드
                new PutObjectRequest(filebucket, fileName, uploadFile) 
                        .withCannedAcl(CannedAccessControlList.PublicRead)	
                        // PublicRead 권한으로 업로드 (사용자 모두가 접근 가능)
        );

        // s3 bucket에 올린 파일 URL
        return amazonS3Client.getUrl(filebucket, fileName).toString();
    }
}

저의경우에는 해당 기능을 다른 서비스에서도 활용이 가능하도록 작성했습니다, 해당 코드에서 중요한 부분중에 하나가 S3 CannedAccessControlList 권한 종류인데 권한 종류는 하단에 작성했으니 참고바랍니다.

S3 CannedAccessControlList 권한 종류

Amazon S3의 CannedAccessControlList(Canned ACL)는 버킷이나 객체에 대해 미리 정의된 권한을 설정할 수 있는 간단한 방식입니다. 각 Canned ACL의 권한은 다음과 같습니다.

Canned ACL설명소유자 권한부여된 권한
Private버킷 또는 객체 소유자만 전체 제어 권한을 가집니다.전체 제어(Full Control)없음(None)
PublicRead소유자는 전체 제어 권한을 가지며, 모든 사용자에게 읽기 권한을 부여합니다.전체 제어(Full Control)모두 읽기(Read for Everyone)
PublicReadWrite소유자는 전체 제어 권한을 가지며, 모든 사용자에게 읽기와 쓰기 권한을 부여합니다.전체 제어(Full Control)모두 읽기 및 쓰기(Read and Write for Everyone)
AuthenticatedRead소유자는 전체 제어 권한을 가지며, 인증된 AWS 사용자에게 읽기 권한을 부여합니다.전체 제어(Full Control)인증된 사용자 읽기(Read for Authenticated Users)
BucketOwnerRead소유자는 전체 제어 권한을 가지며, 버킷 소유자에게 읽기 권한을 부여합니다. (계정 간 접근 시 사용)전체 제어(Full Control)버킷 소유자 읽기(Read for Bucket Owner)
BucketOwnerFullControl소유자와 버킷 소유자 모두 전체 제어 권한을 가집니다. (계정 간 접근 시 사용)전체 제어(Full Control)버킷 소유자 전체 제어(Full Control for Bucket Owner)
LogDeliveryWrite로그 전달 그룹(Log Delivery Group)에게 버킷에 로그를 작성할 수 있는 권한을 부여합니다. (S3 서버 액세스 로깅에 사용)전체 제어(Full Control)로그 전달 그룹의 쓰기 및 액세스 제어 정책 읽기(Write and Read ACP for Log Delivery Group)
AWSExecReadAWS 서비스(예: Amazon CloudFront)가 버킷 객체를 읽을 수 있는 권한을 부여합니다. (드물게 사용)전체 제어(Full Control)AWS 관리 서비스 읽기(Read for AWS Managed Services)

추가 사항

  1. 소유자: 버킷 또는 객체를 생성한 AWS 계정입니다.
  2. 모두: 인증된 사용자와 비인증 사용자를 포함합니다.
  3. 인증된 사용자: 유효한 AWS 자격 증명을 사용하여 로그인한 사용자입니다.
  4. 계정 간 접근: 다른 AWS 계정과 객체 또는 버킷을 공유할 때 사용합니다.

Canned ACL은 S3 리소스에 대한 간단한 액세스 제어를 설정하는 데 유용하며, 상황에 맞게 적절한 ACL을 선택하는 것이 중요합니다.


Spring Boot service layer에서 활용

@Service
@Slf4j
public class fileService {
	
    private final awsfileUtil awsfileUtil;
    
    public fileService(awsfileUtil awsfileUtil){
    	this.awsfileUtil = awsfileUtil;
    }
    
    public void fileSave(MultipartFile file) {
    	 String storedFileName = awsfileUtil.posterUpload(file);
    }
    
}

(단순사용은 이렇게 표현할 수 있지만 awsfileUtil에서 Exception을 발생할 수 있기 때문에 추가 설정사항이 필요할 수 있습니다.)


주의사항

만약 putS3 부분에서 Exception혹은 에러가 발생하면 properties파일을 제대로 작성했는지 그리고 IAM의 권한 부분을 확인하기 바랍니다.


참고 사이트

https://jojoldu.tistory.com/300

(항상 감사합니다.)

profile
가끔은 정신줄 놓고 멍 때리는 것도 필요하다.

0개의 댓글