logo hsb.horse
← 블로그 목록으로 돌아가기

블로그

CloudFront + S3로 SPA를 배포하는 배포 스크립트

CloudFront 뒤의 S3에서 SPA를 제공할 때 쓸 수 있는 실전형 배포 스크립트. Cache-Control 설정, 블루그린 스타일 배포 구조, 롤백 절차를 정리했다.

게시일:

CloudFront + S3로 SPA를 제공할 때 사용할 수 있는 실전형 배포 스크립트를 정리한다.

전제

  • 클라우드 벤더는 AWS를 사용
  • 프런트엔드 빌드 결과물을 S3에 두고 CloudFront를 통해 배포

인프라 구성

S3

  • 버킷 버저닝: 비활성화
  • 암호화: SSE-S3
  • Block Public Access: ON
  • 정적 웹사이트 호스팅: 비활성화

CloudFront

  • 요금 클래스: 모든 엣지 로케이션 사용
  • 지원 HTTP 버전: HTTP/3, 2, 1.1, 1.0

오리진

  • 오리진 이름: S3-frontend-app
  • 오리진 도메인: <bucket_name>.s3.ap-northeast-1.amazonaws.com
  • 오리진 경로: /current
  • 오리진 액세스: Origin access control settings

비헤이비어

기본값 (*)

  • 오리진: S3-frontend-app
  • 뷰어 프로토콜 정책: HTTP to HTTPS
  • 캐시 정책: Managed-CacheOptimized
  • 뷰어 요청: CloudFront Functions 사용

배포 스크립트

AWS 자격 증명 부분은 실행 환경에 따라 달라지므로 생략한다.

Terminal window
# global vars
readonly S3_BUCKET=""
readonly DIST_DIR=""
function main() {
# BUILD_ID는 Git 커밋 해시여도 된다
local -r build_id=$(date -Iseconds)
local -r base_uri="s3://$S3_BUCKET"
local -r deploy_uri="$base_uri/builds/$build_id"
local -r previous_uri="$base_uri/previous"
local -r current_uri="$base_uri/current"
# builds 아래에 배포한다
# js,css
aws s3 sync "$DIST_DIR" "$deploy_uri" \
--exclude "*" \
--include "*.js" \
--include "*.css" \
--metadata-directive "REPLACE"
--cache-control "public,max-age=31536000,immutable"
# js,css,html 이외
aws s3 sync "$DIST_DIR" "$deploy_uri" \
--exclude "*.js" \
--exclude "*.css" \
--exclude "*.html" \
--metadata-directive "REPLACE" \
--cache-control "public,max-age=1,stale-while-revalidate=604800"
# html
aws s3 sync "$DIST_DIR" "$deploy_uri" \
--exclude "*" \
--include "*.html" \
--metadata-directive "REPLACE" \
--cache-control "no-cache"
# current 경로에서 서비스 중인 프런트엔드 자산을 previous로 백업
aws s3 sync "$current_uri" "$previous_uri" --delete --exact-timestamp
# 이번 빌드를 current로 복사해 사용자에게 공개
aws s3 sync "$deploy_uri" "$current_uri" --delete --exact-timestamp
}

왜 이런 순서로 Cache-Control을 설정하는가

원래는 마지막 단계인 aws s3 sync "$deploy_uri" "$current_uri"에서 cache-control을 설정하고 싶지만, S3에서 S3로 sync할 때 --metadata-directive "REPLACE"를 사용하면 AWS 동작상 Content-Type 추론이 사라져 모든 파일이 binary/octet-stream으로 처리된다.

반면 로컬에서 S3로 sync할 때는 Content-Type 추론 결과가 유지되므로, 실제 운영에서는 이런 식의 순서를 택하고 있다.

롤백 스크립트

문제가 생기면 previous에서 current로 되돌린다.

Terminal window
# global vars
readonly S3_BUCKET=""
function revert() {
local -r base_uri="s3://$S3_BUCKET"
local -r previous_uri="$base_uri/previous"
local -r current_uri="$base_uri/current"
# copy previous to current
aws s3 sync "$previous_uri" "$current_uri" --delete --exact-timestamp
}

정리

CloudFront + S3로 SPA를 제공할 때 적절한 cache-control 설정과 블루그린 스타일의 배포 구조를 함께 쓰면 더 안전하고 효율적인 배포가 가능하다.

builds 아래에 이력을 남기고 current, previous로 현재 버전과 직전 버전을 관리하면, 문제 발생 시 빠르게 롤백할 수 있다.