사용하게 된 계기
- 처음에는 EC2 서버에 image 파일을 경로 지정해서 올리려고 했는데 경로 서버내에서 경로 지정이 잘 안되는 경우가 존재하였다.
- 알고보니 EC2 내에 Docker Container안에 SpringBoot를 띄워서 Docker container 안에가 Local이 되었다.
- 즉, container 안에 경로를 설정해 줘야하는데 그 부분을 잠시 접어두고 구글링 서치를 해본 결과 AWS S3 Bucket을 사용하는 경우가 대다수라고 하였다.
- 그래서 주변에 AWS에 다니는 개발자 친구에게 물어보았다.
- 역시..효율성도 효율성이지만 가격에 대한 메리트면 채택하기 충분하다고 생각한다.
- 위 개발자의 블로그는 여기이다. → youngdeveloper.tistory.com
공대생의 비망록
Never Stop Learning!
blog.youngdeveloper.cf
대략적인 실행 과정
- S3 Bucket 생성
- S3 Bucket 접근 권한을 가진 IAM 사용자 생성하기
- AWSS3Config 파일 작성하기
- application.yml 파일 작성하기
- AWSS3Service 파일 작성하기
실행과정
1. S3 Bucket 생성
1. AWS S3 Bucket 사이트에 접속한다.
- https://s3.console.aws.amazon.com/s3/buckets?region=ap-northeast-2
- 해당 사이트에서 버킷 만들기를 클릭한다.
2. 해당 사이트에서 버킷 만들기를 클릭한다.
3. 기본적인 정보를 입력한다.
- 버킷 이름 → 하고싶은 이름으로 하면 됩니다.
- ACL 활성화 → 체크
- 객체 소유권 → 객체 라이터
- 객체 라이터로 설정하지 않으면 ACL이 비활성화 되어 springBoot를 통해 파일 업로드 시 The bucket does not allow ACLs라는 에러가 발생한다.
- 밑 부분에 퍼블릭 엑세스 차단 설정을 완료한다.
4. 나머지는 Defalut 값으로 두고 생성을 완료한다.
5. 생성된 버킷에 들어가서 권한 → 버킷 정책 → 편집을 클릭한다.
- 버킷 정책 편집에 접속했다면 버킷 ARN은 copy 하고 정책 생성기를 클릭한다.
- Select Type of Policy → S3 Bucket Policy
- Principal → *
- Action → DeleteObject, GetObject, PutObject
- ARN → 위에서 복사한 값을 넣어주면 된다.
- 그리고 Add Statement를 누르면 지금까지의 상태가 등록된다.
- 밑에 Generate Policy를 클릭한다.
- 해당 내용을 복사한다.
- 위 복사한 내용을 버킷 정책에 입력한다.
- 근데 방금 값으로 넣었을 경우 Resource 부분에 에러가 날수도 있다.
- 왜냐면 해당 경로에 대한 위치를 제대로 설정하지 않았기 때문이다.
- 따라서 "나의 버킷 이름/” 나의 버킷 이름 뒤에 / 를 붙여줘야지 경로설정이 제대로 된다.
2. S3 Bucket 접근 권한을 가진 IAM 사용자 생성하기
1. AWS IAM 사이트에 접속한다.
- 접속 후 사용자 탭에 들어가서 사용자 추가를 클릭한다.
2. 사용자 세부 정보
- 사용자 이름 → 자기가 지정하고 싶은 이름으로 저장.
3. 권한 설정
- 직접 정책 연결 → AmazonS3FullAccess를 선택한다.
- 그 다음 페이지는 그대로 넘어가고 IAM을 생성하면 된다.
4. 생성된 IAM 사용자를 클릭힌다.
- 보안 자격 증명 → 엑세스 키 → 엑세스 키 만들기를 클릭한다.
- AWS 컴퓨팅 서비스에서 실행되는 애플리케이션을 선택한다.
- 그 다음 설명 및 태그는 넘어가도 무방하다.
이제 액세스 키가 생성이 되었다. 이 키들은 한번 보고 못 보니 잘 저장해두자.
3. AWSS3Config 파일 작성하기
@Configuration
public class AwsS3Config {
@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;
@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
}
}
- 해당 파일은 S3 Bucket에서 받은 키값들과 어떤 region을 통해 접근하는지 설정하는 설정파일이다.
4. application.yml 파일 작성하기
cloud:
aws:
credentials:
access-key: AWS_S3_access-key
secret-key: AWS_S3_secret-key
region:
static: ap-northeast-2
s3:
bucket: AWS_S3_bucket
stack:
auto: false
- AWS_S3_access-key → 바로 위에서 발급 받은 엑세스 키를 입력한다.
- AWS_S3_secret-key → 바로 위에서 발급 받은 비밀 엑세스 키를 입력한다.
- AWS_S3_bucket → 우리의 bucket 이름을 적어준다.
5. AWSS3Service 파일 작성하기
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AwsS3Service {
@Value("${cloud.aws.s3.bucket}")
private String bucket;
private final AmazonS3 amazonS3;
private final ImageRepo imageRepo;
private static final String IMAGE_URL_PREFIX = "https://나의 버킷 이름.s3.ap-northeast-2.amazonaws.com/";
@Transactional
public List<Image> uploadFile(Post post, List<MultipartFile> multipartFile) {
List<Image> images = new ArrayList<>();
// forEach 구문을 통해 multipartFile로 넘어온 파일들 하나씩 fileNameList에 추가
multipartFile.forEach(file -> {
String fileName = createFileName(file.getOriginalFilename());
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
// 이미지 DB 저장.
String originalFilename = file.getOriginalFilename();
String imageUrl = IMAGE_URL_PREFIX + fileName;
Image image = Image.builder()
.imageName(fileName)
.imageUrl(imageUrl)
.originalImageName(originalFilename)
.post(post)
.build();
Image saveImage = imageRepo.save(image);
images.add(saveImage);
try(InputStream inputStream = file.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch(IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드에 실패했습니다.");
}
});
return images;
}
@Transactional
public void updateFile(Post post, List<Image> images, PostDto postDto) {
images.forEach(image -> {
amazonS3.deleteObject(new DeleteObjectRequest(bucket, image.getImageName()));
imageRepo.delete(image);
});
if (postDto.getImageFiles().size() > 0 && !Objects.equals(postDto.getImageFiles().get(0).getOriginalFilename(), "")) {
uploadFile(post, postDto.getImageFiles());
}
}
@Transactional
public void deleteFile(String fileName) {
amazonS3.deleteObject(new DeleteObjectRequest(bucket, fileName));
}
private String createFileName(String fileName) { // 먼저 파일 업로드 시, 파일명을 난수화하기 위해 random으로 돌립니다.
return UUID.randomUUID().toString().concat(getFileExtension(fileName));
}
private String getFileExtension(String fileName) { // file 형식이 잘못된 경우를 확인하기 위해 만들어진 로직이며, 파일 타입과 상관없이 업로드할 수 있게 하기 위해 .의 존재 유무만 판단하였습니다.
try {
return fileName.substring(fileName.lastIndexOf("."));
} catch (StringIndexOutOfBoundsException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "잘못된 형식의 파일(" + fileName + ") 입니다.");
}
}
}
'DevOps' 카테고리의 다른 글
Nextjs Springboot Nginx Docker-compose (0) | 2023.03.16 |
---|---|
Github Action CI/CD (0) | 2023.02.21 |
AWS CM, ELB, Nginx use HTTPS server building (0) | 2023.01.25 |
__AWS Route 53 DNS and EC2 linkage__ (0) | 2023.01.25 |
AWS EC2 mariadb download (1) | 2023.01.25 |