S3에 업로드 테스트를 하는데 자꾸
(Service: Amazon S3; Status Code: 301; Error Code: PermanentRedirect; Request ID:)
AWS Error Code: PermanentRedirect
이런 에러가 나는 것임.
좀 찾아보니까 region설정, endpoint에러 등등 이유가 있다고 하는데
아무리봐도 region도 문제가 없고
- AmazonS3ClientBuiler로 셋팅할 때 application.yml에 설정해둔 값으로 잘 들어감
s3 정책도 잘 주어져있고
#버킷 정책 : S3 -> 버킷 -> 권한탭 [버킷정책]
{
"Version": "2012-10-17",
"Id": "Policy1696948386695",
"Statement": [
{
"Sid": "Stmt1696948368464",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::project-shopping1"
}
]
}
위 정책을 해석하자면 모두에게 허가되어있으며 PUT, DELETE, POST Action이 다 허가된다.
정도로 이해하면 되겠다. 좀 더 궁금하다면 AWS S3 버킷정책을 보도록 하자.
일부 아이디에만 권한을 주겠다면 그건 S3 인스턴스 생성 때 부터 설정을 좀 해주거나 수정해야됨. 이건 상관없으니 쓰루하고
이곳저곳을 뒤지다가 발견한 한 글 덕에 어????? 하고 시도했다가 뭔가 해결의 실마리를 찾았다.
변경전 S3설정
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
public AmazonS3 amazonS3Client() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withRegion(region)
.build();
}
application.yml에 설정한 aws설정값을 가지고와 설정하는 파일이다.
한 파일 안에 몰아서 관리할 수도 있는데 일단 나는 config파일을 따로 빼서 관리했고 여기서 주의깊에 봐야할 곳은 pulbic AmazonS3 amazonS3Client() 이 부분이다.
@Service
public class S3ImgUploaderService {
@Value("${cloud.aws.s3.bucket}")
private String bucket;
private final AmazonS3 amazonS3; #변경 전
public List<ItemImgDTO> upload(String fileType, List<MultipartFile> multipartFiles) throws IOException {
List<ItemImgDTO> s3files = new ArrayList<>();
String uploadFilePath = fileType + "/" + getFolderName();
for (MultipartFile multipartFile : multipartFiles) {
String oriFileName = multipartFile.getOriginalFilename();
String uploadFileName = getUuidFileName(oriFileName);
String uploadFileUrl = "";
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(multipartFile.getContentType());
try (InputStream inputStream = multipartFile.getInputStream()) {
// ex) 구분/년/월/일/파일.확장자
String keyName = uploadFilePath + "/" + uploadFileName;
// S3에 폴더 및 파일 업로드 # 변경 전
amazonS3.putObject(
new PutObjectRequest(bucket, keyName, inputStream, objectMetadata));
// S3에 업로드한 폴더 및 파일 URL # 변경 전
uploadFileUrl = amazonS3.getUrl(bucket, keyName).toString();
} catch (IOException e) {
e.printStackTrace();
log.error("Filed upload failed", e);
}
s3files.add(
ItemImgDTO.builder()
.oriImgName(oriFileName)
.uploadImgName(uploadFileName)
.uploadImgPath(uploadFilePath)
.uploadImgUrl(uploadFileUrl)
.build());
}
return s3files;
}
}
그리고 업로드 서비스를 시행하는 파일이다. 이것도 뒤져보면 소스가 다 비슷비슷하다.
여기서도 주의깊에 봐야할 곳은 private final AmazonS3 amazonS3; 와 그를 사용 하는 곳이다.
변경 후 S3설정
@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client)AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withRegion(region)
.build();
}
@Service
public class S3ItemImgUploaderService {
@Value("${cloud.aws.s3.bucket}")
private String bucket;
private final AmazonS3Client amazonS3Client;
public List<ItemImgDTO> upload(String fileType, List<MultipartFile> multipartFiles) throws IOException {
List<ItemImgDTO> s3files = new ArrayList<>();
String uploadFilePath = fileType + "/" + getFolderName();
for (MultipartFile multipartFile : multipartFiles) {
String oriFileName = multipartFile.getOriginalFilename();
String uploadFileName = getUuidFileName(oriFileName);
String uploadFileUrl = "";
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(multipartFile.getContentType());
try (InputStream inputStream = multipartFile.getInputStream()) {
// ex) 구분/년/월/일/파일.확장자
String keyName = uploadFilePath + "/" + uploadFileName;
// S3에 폴더 및 파일 업로드
amazonS3Client.putObject(
new PutObjectRequest(bucket, keyName, inputStream, objectMetadata));
// S3에 업로드한 폴더 및 파일 URL
uploadFileUrl = amazonS3Client.getUrl(bucket, keyName).toString();
} catch (IOException e) {
e.printStackTrace();
log.error("Filed upload failed", e);
}
s3files.add(
ItemImgDTO.builder()
.oriImgName(oriFileName)
.uploadImgName(uploadFileName)
.uploadImgPath(uploadFilePath)
.uploadImgUrl(uploadFileUrl)
.build());
}
return s3files;
}
차이점이 보이나?
AmazonS3 -> AmazonS3Client로 바꾸자마자 잘 됨. 와 이게 뭐냐??
일단 이렇게하면 해결은 되는데
진짜!!! 최종 답안은 이게 아니니 진짜가 궁금하다면 제일 마지막으로 스크롤 고고
아니 그래서 두개의 차이가 뭔지 좀 찾아봤는데
오히려 AmazonS3Client가 deprecated됐다는 말도 있고 AmazonS3를 써야 됐다는 사람들도 있고...
실제로도 AWS제공 github소스코드를 보면 AmazonS3를 쓰고 있어서 더 미스테리,,
AWS 개발자가이드에도 AmazonS3를 쓰고 있다. 이러면 더 이상해지는데..?
그래서 뭔가 찜찜하다. 조금만 더 테스트를 해보자!
테스트1
테스트1-1. S3Config 파일에서 AmazonS3 return
결국 S3와 연결 설정하는 가장 중요한 이 부분을 가지고
AmazonS3로 리턴했을 경우, AmazonS3Client로 리턴했을 경우를 각각 테스트해봤는데 놀라운 사실이 발견되었다.
public AmazonS3 amazonS3Client() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withRegion(region)
.build();
}
특이사항이라고 하면 AmazonS3가 Interface였다는 점.
그리고 여기 설정까지만 해도 withRegion(region)에는 ap-northeast-2로 잘 설정되어있었음!!
근데 이제 upload하는 곳에서 amazonS3를 가져와서 썼더니 ??? us-west-2는 웬말이여?
테스트1-2. S3Config 파일에서 AmazonS3Client return
그래서 다시 AmazonS3Client로 바꿔서 리턴해주고
@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client)AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withRegion(region)
.build();
}
upload서비스 로직에서도 amazonS3Client를 가져와 사용했더니 이건 정상적으로 ap-northeast-2로 뜸.
혹시나해서 AmazonS3Client를 봤더니 AmazonS3를 상속받고 있는데...? 모지..??
이정도면 두개는 같은 라이브러리라고봐도 사실 무방한데..
흠.. 흠.. 암튼 region이 다르니까 301이 떴었다는 것은 이로 증명이 되긴했는데 왜 저런지는 이해가 여전히 잘 안된다.
테스트2
그러다가 문득 AmazonS3 에 대해 @Bean 선언을 해서 주입을 했는데
DI가 제대로 되지 않은 것이 아닐까 하여 다시 테스트를 했다. 객체 번호(?) 도 함께 확인을 해봤는데
테스트2-1. S3Config 파일에서 AmazonS3 return
최초셋팅값고 실제 업로드에 사용되는 amazonS3의 번호가 달라짐!!
빈 선언까지는 제대로 됐는데 upload파일에서 AmazonS3의 의존성 주입이 제대로 안된게 맞는듯?
테스트2-2. S3Config 파일에서 AmazonS3Client return
혹시 모르니 더블체크로 AmazonS3Client로 돌려봤다.
사진 설명을 입력하세요.
사진 설명을 입력하세요.
즉, 같은 객체니까 이건 문제없이 실행이 됐던 것이다!!!
이걸 어떻게 해결하냐.. 혹시몰라
@Autowired
private final AmazonS3 amazonS3;
도 해줬는데 왜 안되는 것일까나...
테스트3 이자 최종 답안
두번째 테스트에 이르러서 암만생각해도 뭔가 빈주입에서 문제가 생긴 것 같다. 라는 유추에 도달
뭘 더 해야할지 몰라 고민하다가 흠..? 혹시..? 하면서 Config파일의 빈 선언부를 다시 고쳐보았다.
테스트3-1. S3Config 파일에서 메소드이름 amazonS3()로 변경
@Bean
public AmazonS3 amazonS3() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withRegion(region)
.build();
}
빈 주입하는 친구 이름때문인가..? 싶어서
public AmazonS3 amazonS3()로 이름을 바꾸고 테스트를 했더니
@Service
public class S3ItemImgUploaderService {
private final AmazonS3 amazonS3;
갑자기 여기에 정상적으로 주입이 됐음.............. 아니 뭐지?? 빈 주입하는거에 저 함수 이름이 중요해..?
그냥 리턴 객체를 빈 등록하는거 아니었어????? 하고서 부랴부랴 찾아보고나서야 이유도 알아냈다.
@Bean에 대해서 좀 알아봤더니 날림으로 공부한 티가 나네 나 ..
1. 빈의 이름은 기본적으로 메서드 이름이 됨
2. 메서드의 리턴 객체가 스프링 빈 객체임을 선엄함
3. @Configuration설정된 클래스의 메서드에서 사용 가능
4. @Component 어노테이션을 이용해 @Configuration 없이도 빈객체 생성 가능
응 1번때문에 안된거였어~~ amazonS3Client()로 호출하니까 amazonS3Client이 빈 객체가 되면서 amazonS3Client로 final 선언했을 때만 잘가져오지 미친놈들아 ㅠㅠㅠ
에효 그래도 덕분에 몇 시간 삽질했지만 좋은 것 알아간다.
추가로 S3 파일 업로드 테스트하다가 난 에런데 최대 이미지 사이즈가 제한이 있나보다
고화질로 찍어둔 사진가지고 테스트하다가 받은 오류값임
첨부파일 사이즈 오버오류로 추정. 이거는 다음에 해결방법을 찾아보겠다.. 오늘은 이만..
2023-10-17 00:37:54.129 WARN 67959 --- [nio-8080-exec-4] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field files exceeds its maximum permitted size of 1048576 bytes.]
'IT이야기 > AWS' 카테고리의 다른 글
AWS EC2서버에 SWAP 적용하기 (Linux) (0) | 2024.10.30 |
---|---|
[AWS Redis] SpringBoot + Redis 설정 및 생성, EC2(AmazonLinux) 설정까지 (1) | 2024.02.13 |
[GitActions] AWS EC2로 자동 배포하기 (feat Linux) / GITAction + EC2 + CodeDeploy등을 이용한 CD (0) | 2024.01.18 |
AWS EC2 mysql설치 및 RDS 연동 그리고 배포 (MAC) (0) | 2024.01.17 |
AWS EC2인스턴스생성부터 EC2 연결까지(MAC/AmazonLinux2023) (0) | 2024.01.17 |