사전 설정은 버킷을 만들고 사용자 생성 및 액세스 시크릿 키 발급을 해주면 된다.
이미지를 저장하는 폴더라고 생각하면 된다.
https://ap-northeast-2.console.aws.amazon.com/s3/get-started?region=ap-northeast-2
위의 s3 사이트에 들어간후
버킷 만들기를 클릭 한다.

버킷 생성을 했다면
버킷 이름을 정해주고
모든 퍼블랙 엑세스 차단을 해제 해준다.
그 후 아래의 알림을 체크 해주면 된다.

버킷 버전 관리를 활성화 하면 좋지만 좀 더 추가 된다.
그리하여 비활성화를 해줄 것이다.
나머지는 다 기본 설정대로 그냥 생성 해주면 된다.

버킷이 생선되면 다시 버킷 이름을 클릭한다.

편집을 누른후 아래의 코드를 복사해서 넣어준다.(이미지 url로 들어갔는지 확인 해주기 위함)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mungpedia/*"
}
]
}

후에 저장을 눌러주면 끝난다.

만약 이 버킷 정책을 추가를 안해주면 url로 접근 했을시에 위와 같은 창이 뜨고 사진이 보이지 않는다.
https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-2#/home
이제는 사용자 등록을 해줘야 한다.
위의 링크로 IAM 설정으로 가도 되고 aws에서 검색으로 IAM으로 가도 된다.

처음 들어오면 위와 같은 대쉬보드가 나온다
왼쪽의 사용자를 클릭 하자

그다음 사용자 생성을 해준다.

이름만 설정해주고 바로 다음으로 넘어가자

권한 옵션에서 직접 정책 연결을 눌러주고
아래의 권한 정책에서 s3FullAccess를 찾아 체크 해준 후 다음으로 넘어가면 된다.

그후 사용자 생성을 해준다.

이제는 사용자가 만들어 졌다
사용자 이름를 눌러서 들어가자

그다음 액세스 키 만들기를 눌러준다

AWS 컴퓨팅 서비스에서 실행되는 애플리케이션 과 확인 을 클릭 해준 후 다음으로 넘어간다.

설명 태그 값은 굳이 안해도 된다.

이제 액세스 키랑 시크릿 키를 발급 받았다
이는 잃어버리거나 노출되면 안되니 주의하자
.CSV 파일을 다운로드 하면 액세스키와 시크릿키를 저장 할 수 있다.
dependencies {
// 실제 AWS S3 기능 제공 (AWS SDK v2 기반)
// - 파일 업로드, 다운로드, 삭제 등의 S3 기능을 수행하는 핵심 API들이 포함되어 있음
// - 예: S3Client, PutObjectRequest, DeleteObjectRequest, RequestBody 등
implementation 'software.amazon.awssdk:s3'
// Spring Cloud AWS 3.x (Spring Boot 3.x 지원)
// - Spring 환경에서 AWS SDK를 쉽게 사용할 수 있도록 도와주는 설정 지원 모듈
// - application.yml을 통해 region, bucket 설정 가능
// - 내부적으로 AWS SDK v2의 BOM을 포함하므로, 위 S3 의존성의 버전을 생략해도 됨
implementation 'io.awspring.cloud:spring-cloud-aws-s3:3.0.2'
}
cloud:
aws:
credentials:
accessKey: "발급받은 accessKey"
secretKey: "발급받은 secretKey"
s3:
bucket: 버킷 이름
region:
static: ap-northeast-2
사용자 등록 후 발급 받은 액세스키와 시크릿키를 등록 해주고
버킷 이름을 적어준다.(큰따음표 필요 없음)
yml은 spring의 하위 레벨로 만들어주면 된다.
@Configuration
public class S3Config {
//지역 정보 yml에 있는 정보를 가져왔다.
@Value("${spring.cloud.aws.region.static}")
private String region;
//IAM에서 만든 AccessKey
@Value("${spring.cloud.aws.credentials.accessKey}")
private String accessKey;
//IAM에서 만든 SecretKey
@Value("${spring.cloud.aws.credentials.secretKey")
private String secretKey;
@Bean
public S3Client s3Client() {
return S3Client.builder()
// 지역 설정
.region(Region.of(region))
// 자격 증명자 설정
.credentialsProvider(StaticCredentialsProvider.create(
// accessKey와 secretKey로 자격 증명 생성
AwsBasicCredentials.create(accessKey,secretKey)
))
.build();
}
}
S3를 사용하기 위해서는 S3Client 인스턴스를 생성해야 한다.
이를 위해 S3Config 클래스에서 AWS 리전, 자격 증명 정보를 설정하고 @Bean으로 등록해 스프링 컨테이너에서 사용할 수 있도록 해준다.
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class S3StorageServiceImpl implements S3StorageService {
//yml 에서 작성한 static을 가져왔다.
@Value("${spring.cloud.aws.region.static}")
private String region;
//yml에서 사용한 버킷을 가져왔다.
@Value("${spring.cloud.aws.s3.bucket}")
private String bucketName;
//S3Client를 주입 받는다
//이는 우리가 만든 클래스가 아니고 gradle을 추가하면 자동적으로 만들어지는 클래스이다.
private final S3Client s3Client;
//필자는 서비스를 구현을 하여 사용하기에 override를 썼으나 단일 클래스로 사용시 @Override는 생략 가능하다.
@Override
public String upload(byte[] file, String fileName) throws IOException {
// s3에 실직적으로 넣어 주는 부분이다.
// 버킷 이름, 적용할 파일이름, contentType를 PutObjectRequest타입으로 만들어
//s3파일을 업로드 할 때 어디에,어떻게 업로드 할 지 설정하는 요청을 만든다.
// RequestBody.fromBytes(file))에 실제로 얻르도 할 데이터를 넣어준다.
s3Client.putObject(
PutObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.contentType("image/jpeg")
.build(),
RequestBody.fromBytes(file));
//업로드가 완료된 파일의 S3 URL을 반환 해준다.
return "https://" + bucketName + ".s3." + region + ".amazonaws.com/" + fileName;
}
@Override
public void delete(String fileName) {
// s3의 파일을 지우는 요청을 만든다.
s3Client.deleteObject(
DeleteObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.build()
);
}
}
위 코드는 필자가 실제로 작성한 예제이며, S3StorageService 인터페이스를 구현한 S3StorageServiceImpl 클래스를 기반으로 구성되었으나 반드시 이렇게 인터페이스를 구현해야 하는 것은 아니며, 단일 서비스 클래스로 작성해도 무방하다.
@Override
public TradePostMessageResponse createTradePost(CreateTradeBoardRequest request, List<MultipartFile> sourceImage) throws IOException {
//필자는 거래글을 만들고 그리고 사진을 추가 하니 무시해도 된다.
TradePost tradePost = saveTradePost(request);
//사진은 MultipartFile타입으로 받는다.
//MulipartFile을 우리가 s3에 업로드를 하려면 바이트 배열로 컨버트를 해줘야한다.
List<byte[]> files = productImageConvertService.convert(sourceImage);
//필자는 List로 파일받기에 for each문으로 작업 했다.
for (byte[] fileData : files) {
//유니크 파일 경로 만들기
String fileName = UUID.randomUUID().toString();
//s3 업로드 후 이미지 url을 반환 받는다.
String imageUrl = s3StorageService.upload(fileData,fileName);
// product image 객체 생성
// 이부분은 필자는 productImage라는 엔티티를 만들고 postId 와 imageUrl을 보관 할 것이기 때문에 이리 했으나
// 굳이 이렇게 하지는 않아도 된다.
ProductImage productImage = productImageService.createTradeProductImage(tradePost.getPostId(), imageUrl);
}
return TradePostMessageResponse.builder()
.message("Success Created Trade Post")
.result(true)
.build();
}