들어가기 전에
기존에 SpringBoot를 이용한 S3에 이미지 업로드 및 삭제에 대해 포스팅한 적이 있습니다.
해당 포스팅을 할 때에는 프론트엔드에서 S3 이미지를 불러올 때 아래 사진과 같이 S3의 각 객체 URL을 통해 호출하면 될 것이라 생각하고 진행했습니다.
위와 같이 진행하게 되면, Client가 S3를 직접 접근하게 되어 캐싱 기능이나 보안 측면에서 아쉬운 점이 있었습니다.
이번 포스팅에는 AWS가 제공하는 Contents Delivery Network(CDN)인 CloudFront를 사용하여 S3 데이터를 좀 더 효율적으로 전송하는 방법에 대해 알아보도록 하겠습니다.
CloudFront
Client가 S3 컨텐츠를 직접 접근하지 않고 CloudFront를 통해 S3에 접근할 때의 이점
- 콘텐츠 캐싱을 통한 S3 부하 감소
- Edge Location을 통한 응답속도 향상
- 콘텐츠 보안 유지
CloudFront는 Edge Location이라고 하는 데이터 센터의 전 세계 네트워크를 통해 콘텐츠 서비스를 제공합니다. Edge 서버를 사용하여 콘텐츠를 캐싱하고 서비스를 제공하면 최종 사용자가 위치한 곳에 더욱 가깝게 콘텐츠를 전송할 수 있어 성능이 향상됩니다. CloudFront는 아래 지도에서처럼 전 세계 여러 곳에 Edge Server를 가지고 있습니다.
사용자가 CloudFront를 통해 콘텐츠를 요청하면, 사용자와 가까운 Edge Location으로 라우팅됩니다. 이때 CloudFront에 요청된 파일에 대한 캐시 사본이 있으면 CloudFront에 의해 해당 사본을 사용자에 빠르게 전송(낮은 지연 시간)합니다. 만약, CloudFront에 캐싱되어 있지 않은 파일이라면, 해당 파일이 저장된 S3에서 원본 파일을 가져옵니다.
즉, CloudFront는 콘텐츠를 Edge Location에 캐싱하기 때문에 S3 버킷에 대한 부하를 줄일 수 있고, 콘텐츠를 요청하는 사용자에게도 빠르게 응답할 수 있습니다.
또한, CloudFront를 이용해 S3에 접근하게 되면 S3에 대한 액세스 제한을 강화할 수 있습니다. 예를 들어 CloudFront를 사용하면 지리적 제한, 서명된 URL, 서명된 쿠키 등을 통해 액세스 제한을 추가로 설정하여 좀 더 상세한 기준에 따라 액세스 권한을 부여할 수 있습니다.
CloudFront의 보안 기능에는 Origin Access Identity(OAI)도 있으며, 이 기능은 S3 버킷 및 콘텐츠에 대한 액세스를 CloudFront와 CloudFront가 실행하는 작업으로 제한합니다.
이외에도 여러 보안적인 측면에서 사용할 수 있는 기능들이 있으며 해당 내용은 하기 참고 자료의 AWS 한국 블로그 자료를 참고하시기 바랍니다.
CloudFront 요금
필자의 경우 프리 티어로 사용할 예정이라 50GB의 데이터 전송에 대해 무료로 사용할 수 있습니다.
만약 프리 티어가 없다면 좀 더 자세한 요금 정보는 AWS CloudFront 요금제에서 확인하시는 게 좋습니다.
S3와 CloudFront 연결하기
S3에 대한 설정은 이미 완료되어있다는 가정하에 진행합니다. 따라서, 버킷 생성 등의 작업을 하지 않았다면 상단에 링크 걸어둔 AWS S3에 파일 업로드 포스팅을 참고하시길 바랍니다.
- 이번 실습에서 사용한 S3 설정(권한)은 하기에 설명없이 첨부해두도록 하겠습니다. 오로지 설정값만 확인하시고 싶으시다면 하기에서 확인하셔도 무방합니다.
먼저 AWS Console에 접근하여 CloudFront 서비스의 CloudFront 배포 생성을 클릭합니다.
CloudFront 배포 생성 버튼을 클릭하면 하기와 같은 과정을 통해 배포를 생성할 수 있습니다.
Origin Domain Name(원본 도메인)
- CloudFront와 연결하고자 하는 S3를 찾아 넣어주면 됩니다(입력칸 클릭 시, 현 계정에서 넣을 수 있는 AWS 서비스들이 나오며 그 중 S3 연결).
Origin Path(원본 경로)
- 필자의 경우 S3 버킷 한개를 dev와 prod로 디렉토리를 나누어 cloudfront를 사용할 것이라, 원본 경로에
/earthDevImg
를 넣어주었습니다. 이렇게 작성할 경우{Origin-Domain-Name}/earth.png
요청 시,{Origin-Domain-Name}/earthDevImg/earth.png
를 호출합니다.
- 필자의 경우 S3 버킷 한개를 dev와 prod로 디렉토리를 나누어 cloudfront를 사용할 것이라, 원본 경로에
Origin Access ID(원본 액세스 ID, OAI)
- CloudFront를 통해서만 S3의 객체를 가져올 수 있도록 하기 위해서 사용하는 기능입니다. 즉, 해당 기능을 사용하게 되면 S3의 객체 URL을 통해 객체에 접근이 불가능합니다.
- OAI를 따로 생성하지 않았다면, 새 OAI 생성을 통해 만들어주면 됩니다.
Alternate Domain Name(CNAME, 대체 도메인)
- CloudFront의 도메인이 아닌 다른 도메인을 사용하고자 할 경우 작성합니다.
- 필자의 경우, 기존에 가지고 있던 도메인에 연결해서 테스트해보고자 대체 도메인을 작성하였습니다. 관련 내용은 하기 CloudFront 배포 생성 시 ACM 인증서 생성하는 방법에 링크 걸어두었으니 필요하신 경우 확인하시기 바랍니다.
SSL Certificate(사용자 정의 SSL 인증서)
- 뷰어 프로토콜 정책에서 HTTPS를 사용하도록 지정했다면, SSL 인증서를 넣어주어야 합니다. CloudFront의 경우
us-east-1 리전
에 대해 생성된 ACM 인증서만 사용 가능하기 때문에 인증서를 따로 생성하지 않았다면 인증서 요청을 클릭하여 인증서를 생성합니다. - 인증서 요청 클릭 시 동작하는 부분은 하기 추가 정보에 작성해 두었으니 참고 바랍니다.
- 뷰어 프로토콜 정책에서 HTTPS를 사용하도록 지정했다면, SSL 인증서를 넣어주어야 합니다. CloudFront의 경우
위 과정을 통해 CloudFront를 배포하면, 정상적으로 CloudFront의 배포 정보에 적혀있는 배포 도메인 이름으로 S3 버킷에 접근되는 것을 확인할 수 있습니다. 하지만, 아래와 같이 대체 도메인으로 설정한 도메인으로 접근 시 제대로 페이지가 뜨지 않음을 확인할 수 있습니다.
이는 현재, CloudFront 배포 생성 시, ACM 인증서는 생성하여 HTTPS로 만들어진 dev-img.earth-h.tk
에 대한 레코드(_XXX...dev-img.earth-h.tk
)는 Route 53에 생성되었지만, dev-img.earth-h.tk
에 대한 레코드가 따로 없어 해당 도메인으로 접근 시 어디로 보내야할 지 알 수 없기 때문입니다.
따라서, 하기와 같이 Route 53에 접근하여 사용하고 있는 호스팅 영역에 접근하고, CloudFront 배포 생성 시 CNAME으로 CloudFront와 연결해준 도메인에 대해 A 레코드를 생성해주어야 합니다.
추가적으로, 필자가 위에서 접근한 dev-img.earth-h.tk/testearth.png
파일의 실 S3 경로는 하기와 같습니다. 즉, Origin Path(원본 경로)에 미리 earthDevImg를 넣어두었기 때문에 브라우저에서 이미지 호출 시, dev-img.earth-h.tk/earthDevImg/testearth.png
가 아닌 dev-img.earth-h.tk/testearth.png
로 호출이 가능한 것을 확인할 수 있습니다.
추가 정보
CloudFront 배포 생성 시 ACM 인증서 생성하는 방법
하기 플로우는 CloudFront 배포 생성 시, 인증서 요청 버튼을 클릭했을 때 진행하는 내용입니다.
- Route 53에 이미 호스팅 영역이 생성되어 있다는 가정하에 작업이 진행되기 때문에, 만약 Route 53에 호스팅 영역 생성 및 도메인 구매 및 적용에 대한 내용을 확인하고 싶으시다면 하기 포스팅을 확인하기 바랍니다.
- [AWS] Route 53, ACM을 통해 Elastic Beanstalk HTTPS 설정하기(freenom을 통한 무료 도메인 사용)
CloudFront 도메인 또는 대체 도메인으로 S3 객체 접근 시 Access Denied가 뜬다면?
CloudFront 도메인 또는 대체 도메인으로 S3 객체 접근 시 Access Denied가 뜬다면, S3 버킷의 권한 설정 중 객체 소유권을 확인해보시기 바랍니다.
만약, 아래와 같이 객체 소유권이 "버킷 소유자 적용"으로 되어 있다면 ACL 비활성화된 것이며, ACL 비활성화 시 하기와 같이 AccessDenied가 뜰 것입니다.
따라서, 객체 소유권 편집을 클릭하여 ACL 활성화를 해주어야 하며,ACL 활성화 시 CloudFront의 URL(또는 대체 도메인)으로만 객체에 접근이 가능하고 S3 객체 도메인을 통해서는 호출이 되지 않는 것을 확인할 수 있습니다.
- 만약, S3 객체 URL을 통해서도 호출이 된다면 해당 객체의 권한을 확인하시기 바랍니다. 모든 사람에 대해 읽기를 허용해두었다면 해당 부분을 비활성화하면 S3 객체 URL을 통해서는 AccessDenied가 뜰 것입니다.
CloudFront 배포 생성 시, OAI 관련 권한을 S3에 자동 업데이트가 되지 않았다면?
만약 하기와 같이 Amazon S3 버킷 권한을 업데이트하지 못하였다고 에러가 발생할 경우, 직접 S3에 접근하여 정책을 넣어주어야 합니다.
먼저, CloudFront의 보안 > 원본 액세스 ID
탭에 접근하여 현 CloudFront 배포에서 사용하는 OAI의 ID 값을 확인합니다.
그 후 AWS S3 서비스에 접근하여, CloudFront에 의해 접근할 S3 버킷의 권한 탭을 눌러 버킷 정책을 하기와 같이 수정합니다.
위 캡쳐 사진의 빨간 네모 박스에 작성된 내용은 하기와 같습니다.
[자신의 OAI ID]
에 CloudFront에서 확인한 OAI ID값을 넣어주면 됩니다.[cloudfront가 접근할 S3 버킷 경로]
의 경우, 예를 들어 s3-upload-img라는 이름을 가진 버킷 하위 모든 디렉토리 및 파일에 대해 권한을 주고자 한다면, s3-upload-img/*와 같이 작성해주시면 됩니다.
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity [자신의 OAI ID]"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[cloudfront가 접근할 S3 버킷 경로]"
}
이번 포스팅에서 사용한 S3 버킷 권한 설정
S3 버킷 권한 - 퍼블릭 액세스 차단(버킷 설정)
S3 버킷의 퍼블릭 액세스 차단의 경우, 활성화 시켜도 CloudFront와 연결하는데는 문제가 없지만 SpringBoot를 통해 S3에 파일 업로드/삭제를 진행하고 있어 새 ACL을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단은 비활성화하였습니다.
- 위와 같이 테스트하였을 때 SpringBoot를 통해 파일 업로드/삭제가 문제 없이 되는 것을 확인하였습니다.
S3 버킷 정책
필자의 S3 버킷 정책은 크게 2가지로 나뉘어집니다.
- 첫번째 정책의 경우는 SpringBoot에서 파일 업로드/삭제를 위해 사용하는 AWS IAM 계정(S3FullAccess 가능 계정)에 대해 Delete/Get/PutObject를 허용하는 정책입니다.
- 두번째 정책의 경우, CloudFront에 OAI 설정을 진행하여 해당 OAI에 대해 S3의 객체를 불러오도록 하는 정책입니다.
위 캡쳐본과 같이 정책을 작성할 때에는 버킷 정책 편집을 눌러 정책 생성기를 통해 만들 수 있습니다. 만약, 그 부분이 어렵다면 하기 코드를 복사하셔서 사용해보셔도 됩니다.
{
"Version": "2012-10-17",
"Id": "Policy1635341891814",
"Statement": [
{
"Sid": "Stmt1635341884230",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::755817737425:user/[ S3FullAccess 권한을 가진 IAM 계정명 ]"
},
"Action": [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::s3-upload-img/*"
},
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity [ 자신의 OAI ID ]"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::s3-upload-img/*"
}
]
}
S3 버킷 내 객체 소유권
S3 버킷 객체 소유권의 경우, ACL을 활성화하여야 합니다. 위에서도 관련 내용을 작성하였지만, S3 버킷에 대해 접근을 하기 위해 필요한 항목이니, 위와 같이 설정하시면 됩니다.
마치며
추후 이렇게 연결한 CloudFront + S3를 이용하여 S3에 업로드된 이미지를 Lambda@Edge를 이용해 리사이징하는 방법에 대해 알아보도록 하겠습니다.