해당 게시글에서는 본격적으로 SpringBoot에 코드를 작성하여 실질적으로 s3에 이미지 혹은 파일을 업로드하는데 사전작업이 필요합니다.
사전작업방법은 하단의 링크를 참고하시기 바랍니다.
사전작업을 마치셨다면 이제 본격적으로 코드를 작성해보겠습니다, SpringBoot에서는 다음과 같은 설정을 거쳐야합니다.
이 게시글에서는 다음과 같은 환경에서 제작했습니다.
OS : window11
Framework : Spring Boot 3.4.5
gradle : 8.8
제일먼저 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를 분실한 경우 사용자에서 액세스 키를 추가로 생성해야 합니다.)
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}
해당 AWS를 연결하는 의존성을 추가합니다.
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에 파일을 저장하는 클래스를 만들어 보겠습니다.
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 권한 종류인데 권한 종류는 하단에 작성했으니 참고바랍니다.
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) |
AWSExecRead | AWS 서비스(예: Amazon CloudFront)가 버킷 객체를 읽을 수 있는 권한을 부여합니다. (드물게 사용) | 전체 제어(Full Control) | AWS 관리 서비스 읽기(Read for AWS Managed Services) |
추가 사항
Canned ACL은 S3 리소스에 대한 간단한 액세스 제어를 설정하는 데 유용하며, 상황에 맞게 적절한 ACL을 선택하는 것이 중요합니다.
@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
(항상 감사합니다.)