io.awspring 라이브러리를 활용하면 S3를 더 간결한 코드로 연동할 수 있다는 것을 알게 되었다. 이번 글에서는 기존 S3 연동 코드를 지우고 새롭게 연동하는 과정을 정리하고자 한다.
간단하게 말하면 흔히 사용하는
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
이 네 개의 라이브러리 대신
import io.awspring.cloud.s3.S3Resource;
import io.awspring.cloud.s3.S3Template;
이 두 개의 라이브러리를 써서 더 쉽고 간단하게 구현하는 내용이다!
1️⃣ 라이브러리 의존성 추가
프로젝트의 build.gradle에 라이브러리 의존성을 추가한다.
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.2")
implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3'
의존성 추가 후 왼쪽 위에 나타난 이 버튼 누르는 것을 잊지 말기.
2️⃣ AWS S3 버킷 권한 수정
미리 생성해 둔 S3 버킷으로 들어가 권한을 수정해 주어야 한다. 먼저 퍼블릭 액세스 차단 편집에서 모든 퍼블릭 액세스 차단을 해제해 준다. 위 사진처럼 아무것도 선택 안 하면 된다.
다음으로 버킷 정책을 수정해 준다.
{
"Version": "2012-10-17",
"Id": "Policy1687869450617",
"Statement": [
{
"Sid": "Stmt1687869445984",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::버킷 이름/*"
}
]
}
위 정책을 복사해서 붙여 넣어도 된다. 이때 맨 마지막 줄의 버킷 이름은 자신이 생성한 버킷 이름으로 넣어주어야 한다.
3️⃣ application.yml 작성
spring:
servlet:
multipart:
max-file-size: 20MB
max-request-size: 20MB
cloud:
aws:
credentials:
access-key: ${AWS_ACCESS_KEY_ID} # IAM ID
secret-key: ${AWS_SECRET_ACCESS_KEY} # IAM SECRET KEY
region:
static: ${AWS_S3_REGION} # S3 버킷 생성 지역
s3:
bucket: ${AWS_S3_BUCKET_NAME} # 버킷 이름
stack:
auto: false
application.yml에 해당 환경변수를 추가해 준다. multipart 용량은 20MB가 default이다. AWS KEY는 유출되면 안 되니 반드시 환경변수로 묶어서 업로드해 준다.
우분투 서버에 환경변수를 올리는 방법 아래와 같다.
sudo nano /etc/environment # home에서 루트 권한으로 해당 파일에 들어간다.
AWS_ACCESS_KEY_ID="asdf" # 이렇게 큰 따옴표로 묶어 작성해준다
source /etc/environment # 저장까지 해주기
4️⃣ S3ImageController
@RestController
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@RequestMapping("images")
public class S3ImageController {
S3ImageService s3ImageService;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public String create(@RequestParam("file") MultipartFile file) {
return s3ImageService.create(file);
}
}
@RequestParam("file") 어노테이션을 통해 "file"이라는 이름으로 날아온 이미지를 요청으로 보낸다. 다른 이름으로 날리고 싶으면 수정하면 된다.
5️⃣ S3ImageService
@Service
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@RequiredArgsConstructor
public class S3ImageService {
S3ImageRepository s3ImageRepository;
public String create(MultipartFile file) {
return s3ImageRepository.save(file);
}
}
컨트롤러에서 넘어온 이미지를 저장소에 저장하는 Service 코드이다.
6️⃣ S3ImageRepository
@Repository
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class S3ImageRepository {
S3Template s3template;
String bucketName;
public S3ImageRepository(S3Template s3template, @Value("${spring.cloud.aws.s3.bucket}") String bucketName) {
this.s3template = s3template;
this.bucketName = bucketName;
}
public String save(MultipartFile file) {
String key = S3BucketDirectory.IMAGE.getDirectory() + file.getOriginalFilename();
final S3Resource result = s3template.upload(bucketName, key, getInputStream(file));
return getUrl(result);
}
private InputStream getInputStream(@RequestParam("file") MultipartFile file) {
try {
return file.getInputStream();
} catch (IOException e) {
throw new RuntimeException();
}
}
private String getUrl(S3Resource s3Resource) {
try {
return s3Resource.getURL().toString();
} catch (IOException e) {
throw new RuntimeException();
}
}
}
요청 이미지를 S3 버킷에 저장하는 Repository 코드이다. 이때 유심히 봐야 하는 부분이 save 메서드이다.
public String save(MultipartFile file) {
String key = S3BucketDirectory.IMAGE.getDirectory() + file.getOriginalFilename();
final S3Resource result = s3template.upload(bucketName, key, getInputStream(file));
return getUrl(result);
}
잘 보면 key라는 문자열이 선언되어 있다. 이렇게 작성한 이유는 S3 구조 때문이다. 만약 S3 버킷 안에 폴더 없이 바로 저장할 예정이라면 key 대신 file.getOriginalFilename()을 upload 파라미터로 보내면 된다.
하지만 우리 프로젝트는,
위와 같이 두 개의 폴더로 분류해서 파일을 넣는다. 때문에 이미지를 업로드할 때 해당 폴더를 지정해 주어야 돼서 key라는 변수를 통해 디렉터리 이름과 파일 이름을 합쳐서 업로드한다.
@Getter
public enum S3BucketDirectory {
IMAGE("HowAboutTrip-Backend-Image/");
private final String directory;
S3BucketDirectory(String directory) {
this.directory = directory;
}
}
S3 버킷 내에 폴더로 나누어 파일을 관리할 경우, enum을 이용하여 경로를 관리해 주면 훨씬 편하다.
요청을 보내면 버킷 안에 파일이 들어온 것을 확인할 수 있다 ✅✅✅
'Dev > AWS' 카테고리의 다른 글
[AWS] EC2 + CodeDeploy + S3 + GithubActions 조합으로 자동화배포 구축하기 - (2) CodeDeploy 및 S3 세팅 (0) | 2024.04.15 |
---|---|
[AWS] EC2 + CodeDeploy + S3 + GithubActions 조합으로 자동화배포 구축하기 - (1) EC2 인스턴스 생성 및 세팅 (0) | 2024.04.12 |
[AWS] CodeDeploy의 scripts/deploy.sh를 제대로 작성해보자 (1) | 2024.01.29 |
[AWS] EC2 배포 일주일 삽질 스토리 (feat. 환경변수) (1) | 2024.01.27 |
[AWS] CPU 사용률 급증으로 인한 EC2 무한 로딩 해결 (1) | 2024.01.27 |