Vuejs 기반 static page AWS CloudFront + S3로 배포
aws s3
vuejs 같은 경우는 static page로서 lambda에 배포하기에는 부적절합니다.
리서치해본 결과 aws s3에 많이 배포한다고 합니다. Vuejs 공식 문서에도 언급되어 있습니다.
또한 AWS 공식 문서에도 s3 버킷을 이용하여 static website를 관리하는 방법이 잘 나와있습니다.
Amazon S3 버킷에서 호스팅하는 웹 사이트로 트래픽 라우팅
체크할 점으로는 이 방식(route53에서 s3로 직접 라우팅)의 경우 버킷명이 도메인명과 동일해야 한다는 점입니다.
그 후 업로드 자체는 정말 쉽습니다. 빌드 후 aws s3 sync만 하면 되기 때문입니다.
# github action yml 일부 ...
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: sync to s3
run: aws s3 sync ./dist s3://버킷명 --delete
...
cloudfront
업로드가 성공했으니 이제 route53 도메인 연결, TLS 인증서 적용만 해주면 됩니다.
리서치해보면 인증서 적용을 위해서는 s3와 route53 사이에 cloudfront를 두어야 한다고 합니다.
CloudFront를 사용하여 Amazon S3에서 호스팅되는 정적 웹 사이트 제공
문서를 따라해보면 여기까지도 어렵지 않습니다. 다만 실제 배포를 해보면 코드가 바로 반영되지 않는데, 그 이유는 cloudfront 단에서 기본적으로 캐싱을 하기 때문입니다.
해결 방법으로는
- 옵션에서 TTL을 일괄 0으로 설정
- 배포 직후 invalidation이라는 일종의 캐시 무효화 작업을 생성
- s3 객체(파일)에 메타데이터를 추가하여 캐싱이 되지 않도록 하는 방법
이 있는데 저 invalidation 작업이 생각보다 시간이 오래 소요되어 피하고 싶고, 일부 파일은 캐싱을 시키고 싶었기 때문에 3번 방법을 택했습니다.
즉 s3에 파일 업로드 시 캐싱을 원하지 않는 파일에만 metadata를 추가해 배포하면 이슈가 해결됩니다.
aws s3 sync ./dist s3://dev.just1s.xyz --exclude "*.js*" --delete
aws s3 sync ./dist s3://dev.just1s.xyz --exclude "*" --include "*.js*" --metadata-directive COPY --cache-control max-age=0,no-cache,no-store,must-revalidate --delete
위와 같이 하면 static asset 등 js가 아닌 파일은 그대로 배포, js 파일들은 cache-control metadata가 추가된 채로 배포가 됩니다.
덧 : 지금 다시 보면 캐싱 대상을 반대로 잡았다. Vue 빌드 산출물의 js는 파일명에 해시가 붙어 내용이 바뀌면 이름도 바뀌므로 오히려 캐싱해도 되는 쪽이고, 정작 no-cache가 필요한 건 이름이 늘 같은
index.html이다. 위 설정은index.html이 첫 번째 sync에 포함되어 그대로 캐싱되므로 “코드가 바로 반영되지 않는” 원래 문제가 남는다. 그리고--metadata-directive는cp의 S3 → S3 복사용 옵션이라 로컬 → S3 업로드인 여기서는 불필요하다. 값도COPY는 원본 메타데이터를 유지하라는 뜻이라--cache-control과 방향이 반대다.--cache-control만 주면 된다.
결론
최종 development branch 배포 github action yaml은 다음과 같습니다.
name: Deploy to Development
on:
push:
branches:
- development
jobs:
build_vue:
name: Build Vue
runs-on: ubuntu-latest
env:
NODE_ENV: "development"
steps:
- name: checkout
uses: actions/checkout@master
- name: setup node
uses: actions/setup-node@v1
with:
node-version: "12"
- name: Cache node modules
uses: actions/cache@v2
with:
path: |
**/node_modules
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
- name: npm install
run: npm install
- name: Build
run: npm run build:development
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: sync to s3 exclude js
run: aws s3 sync ./dist s3://dev.just1s.xyz --exclude "*.js*" --delete
- name: sync to s3 js with disabled cache
run: aws s3 sync ./dist s3://dev.just1s.xyz --exclude "*" --include "*.js*" --metadata-directive COPY --cache-control max-age=0,no-cache,no-store,must-revalidate --delete