이번 캡스톤디자인종합 프로젝트를 진행하면서 Vite 번들러 기반의 React App을 제작해보았다. 그동안 대부분 CRA 기반으로 제작했는데, 이전에 Rollup을 통한 모듈을 npm에 배포했던 경험을 생각해보았을 때 Rollup의 슈퍼셋이라는 Vite 역시 너무 좋아보여서 도전했다.
사실 도전이라고 하기에 뭐 할 만큼 너무나도 간단했다.
여튼 이번에도 역시 Vercel
같은 서버리스로 배포할까하다가, 데브옵스적인 지식도 많이 필요하다 느끼기도 했고... 프로젝트 주제가 너무 괜찮았고 완성도도 꽤 높았기 때문에 (머신러닝 정확도가 무려 84%!) 실제 서비스화를 계획하던 찰나, 비용이나 속도 등 가성비 측면에서 더 좋다는 AWS로 배포를 진행하게 되었다.
AWS로 배포해본게 처음이라, 여러 블로그를 참고하기도 하고 실제 배포된 프로젝트의 레포에서 yaml을 많이 참고하기도 하였다.
하루정도 고군분투한 것 같긴한데, 사실 말이 하루지 누워있던 시간 제외하면... 그렇게 어렵지 않았던 것에 비해 많이 배운 경험이었다.
처음엔 EC2로 배포하였다.
하지만 CSR 기반의 React App을 EC2로 배포하는 경우가 많이 없다는 것을 알았고, 애초에 번들 사이즈도 그렇게 크지 않거니와 무엇보다도 빌드에서의 문제가 있었다.
대부분의 EC2 배포 관련 레퍼런스를 보면 EC2에 Route53을 통하여 도메인 설정을 하고, NGINX와 PM2를 통해 무중단 배포를 등록한 후 CertBot을 이용해서 https까지 설정하는 것을 알 수 있었다.
이 방법 역시 너무나도 간단하였고, 레퍼런스도 꽤 있는 편이니 아래 링크를 참고해봐도 좋을 것이다.
EC2가 필요한 프로젝트 환경이었다면 모르겠으나, 해당 App은 CSR 기반이었다.
때문에 캐시하여 더 빠르게 볼 수 있는 S3 CloudFront가 적합하다고 판단했다.
(EC2 배포까지 했다가 S3 Cloudfront 조합으로 돌아왔다)
위 레퍼런스를 따라 작업을 해보면, 빌드 속도에서 뭔가 잘못됐음을 알 수 있다.
Vite 기반이기에 로컬에서 빌드하면 대략 6초 안에 완성되는 것을 알 수 있는데, 내 EC2에서는 한 번 빌드하면 최소 30분 ~ 1시간 이상 걸리거나 아예 멈춰버리는 사고가 발생했다.
번들 사이즈를 최적화한 프로젝트임에도 이정도라니. 규모가 조금이라도 더 커지면 빌드가 안되는 배포 구조는,, 뭔가 잘못되었다고 생각했다.
이러한 방법도 있었지만, 사실 이 방식으로 수정할 바엔 그냥 S3와 CloudFront를 쓰는게 베스트이지 않을까... 싶었다.
가비아에서 도메인을 하나 구입해준다.
Route53을 이용해서 도메인을 구매할 수도 있지만, 예전부터 자주 사용하던 사이트라 그런지... 이게 익숙했다. 일단 자국어로 된 점이 가장 좋다ㅋㅅㅋ
나는 대충 .site라는 반쯤 하꼬인 도메인을 하나 구입했다. (.shop이라는 완전 하꼬 도메인도 있는데, 1년에 100원이라 ㄱㅇㄷ이다)
도메인을 구입하고 한 5분정도 기다리면 등록이 완료된다.
등록이 완료되면 이용중인 서비스
탭에서 해당 도메인을 확인할 수 있다.
아래 설정은 본인에게 필요하게 설정하면 된다.
IAM 발급이 완료되면, 보안 자격 증명 탭에서 액세스 키와 시크릿 키가 있는 csv파일을 받을 수 있다.
생성된 IAM에서 권한추가 -> 직접 정책 연결 후 이미지에 있는 두 권한을 추가해준다.
여기서 필요한 권한을 적절하게 부여하면 된다.
해당 IAM의 경우에는 S3와 CloudFront만 쓸거니까 두개만 부여했다.
S3 버킷을 생성하면서 ACL을 활성화한다.
객체 퍼블릭 차단을 해제하고 버킷 만들기 클릭!
생성된 버킷의 속성 탭을 클릭 후 최하단으로 내려보면 정적 호스팅과 관련된 설정이 있다.
편집을 선택하고, 위와 같이 설정해준다.
이번에는 권한 탭으로 이동하고, 버킷 정책을 수정해준다.
정책 생성기 클릭
Select Type of Policy
- S3 선택
Principal
- * 입력
Actions
- GetObject 선택
Amazon Resource Name
- 해당 버킷의 ARN/* (뒤에 /* 붙여야함)
Add Statement를 클릭하고, 아래 표에서 등록이 되었는지 확인한다.
등록이 완료되면 Generate Policy를 눌러서 정책 생성을 완료한다.
이제 버킷에 빌드 파일을 업로드하면 우선 정적 배포가 완료된다.
생각보다 너무 쉬워서 당황했던...
$ yarn build
나는 패키지 매니저로 yarn을 사용하고 있었고, vite로 생성한 프로젝트의 경우 package.json을 딱히 수정하지 않는다면 yarn build
로 빌드 명령을 수행할 수 있다.
이제 이러한 dist 디렉토리가 성공적으로 생성되었다면, 당신의 프로젝트는 빌드에 성공한것이다!
나는 인덱스 문서의 경로를 /index.html 로 설정한 상태이기 때문에 (나와 같은 과정을 따라왔다면 동일하다) dist 내부의 파일들을 S3 버킷에 드래그앤 드랍한다.
dist 디렉토리 자체를 올리는게 아니라, 내부 파일들을 올려야한다!
이제 또 속성탭에서 해당 버킷의 엔드포인트로 직접 접속해본다.
url이 심각하게 못생겼지만, 우선 배포가 정상적으로 완료되었다는 것을 알 수 있다!
Route53에 접속하여 호스팅 영역 생성을 클릭한다.
가비아에서 구매한 도메인 이름을 그대로 작성하면 되고, 나머지는 위 이미지 설정대로 유지한다.
이제 가비아에서 구매한 나의 도메인과 Route53에 등록한 도메인을 매핑해야한다.
가비아 > My가비아 > 도메인 > 매핑할도메인의 관리버튼 > 네임서버 설정 버튼
클릭후 Route53에 있는 값을 순서대로 입력해준다.
AWS Certificate Manager(ACM) 콘솔에 접속한다.
아마존에서 쉽고 간편하게 SSL 인증서를 발급받을 수 있다.
인증서 요청을 진행한다.
도메인 이름만 Route53에 등록한 도메인과 동일하게 작성하고, 나머지 설정은 그대로 유지한다.
이후 인증서 발급 상태 창 > Route53에서 레코드 생성하기 버튼을 클릭한다.
이제 다시 Route53에서 내 도메인을 확인해보면, CNAME 유형의 라인이 하나 생긴 것을 알 수 있다.
여기의 레코드 이름
과 값/트래픽 라우팅 대상
값이 이전 인증서의 값과 같은지 확인하고 넘어가자.
이후 인증서 발급이 완료되면, 우리의 https 도메인을 CloudFront와 연결해보자!
CloudFront에 접속하여, 배포 생성을 클릭한다.
기본 경로는 index.html로 설정한다.
Amazon CloudFront는 .html, .css, .js 및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 사용자에게 더 빨리 배포하도록 지원하는 웹 서비스입니다.
CloudFront는 엣지 로케이션이라고 하는 데이터 센터의 전 세계 네트워크를 통해 콘텐츠를 제공합니다. CloudFront를 통해 서비스하는 콘텐츠를 사용자가 요청하면 지연 시간이 가장 낮은 엣지 로케이션으로 요청이 라우팅되므로 가능한 최고의 성능으로 콘텐츠가 제공됩니다.
CloudFront는 CDN(Content Delivery Network)서비스이고, 이는 지리적으로 가까운 곳에 있는 캐시 서비스가 콘텐츠를 사용자에게 전달해주는 서비스이다.
그리고 CloudFront에서 사용하는 캐시 서버가 엣지 로케이션이라고 불린다.
때문에 엣지 로케이션의 캐시 무효화(Cache Invalidation)을 통해 나의 캐시 정책을 무효화하고, 새롭게 캐시할 수 있다.
무효화 탭 > 무효화 생성 클릭
캐시 무효화가 필요한 부분을 분석해서 해당 부분에만 적용해보는 것도 좋을 것 같다.
React는 Single Page Application이기 때문에, 잘못된 페이지에 접근했을 때에 대한 설정을 따로 해준다.
A 레코드(A Record)
는 DNS에 저장되는 정보의 타입으로 도메인 주소와 서버의 IP 주소가 직접 매핑시키는 방법이다.
Route53과 ACM을 통해 완성한 도메인과, CloudFront의 IP주소를 매핑할것이다.
Route53 > 레코드 생성
레코드 유형 A > 별칭 > CloudFront > 레코드 생성
이렇게 완벽하게 배포가 완료되었다!
이제 남은 것은 CICD.
프로젝트를 빌드할 때마다 S3에 드래그앤 드랍하는 상상하면 조금 끔찍하다.
나는 요즘 Hot하고 가장 가볍다는 Github-Action으로 CICD 파이프라인을 구성해주었다.
환경변수와, IAM의 액세스 키, 시크릿 키, 지역까지 저장해준다.
해당 레포지토리 > Settings > Secret and variables > New repository secret
나머지 환경 변수는 본인 프로젝트에서 관리하는대로 입력하면 된다.
프로젝트 루트 디렉토리에
.github > workflows > deploy.yaml 생성
name: Build
on:
push:
branches:
- master #빌드하고싶은 브랜치
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Create env file
run: |
touch .env
echo VITE_GOOGLE_CLIENT_ID=${{ secrets.VITE_GOOGLE_CLIENT_ID }} >> .env
# ...이런식으로 환경 변수 삽입
cat .env
- name: Yarn Install
run: |
yarn install
- name: Yarn Build
run: |
yarn build
- name: Deploy to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_STAGING_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
SOURCE_DIR: "dist"
run 하위에 있는 echo 부분은 Github Secret에 등록한 환경 변수를 빌드 과정에서 삽입하는 과정이다.
본인이 입력한 환경 변수를 해당 부분에 알맞게 커스텀하여 넣으면 된다.
이제 Action 탭에서 CICD가 적용된 나의 프로젝트를 확인할 수 있다.
이렇게 배포가 완료되었다. CICD 까지 끝!
사실 이전까지 배포와 AWS에 대한 막연한 .. 두려움이 조금 있었는데, 완전히 사라졌다.
이 배포 이후에도 EC2나 Amplify를 이용해서 다른 프로젝트를 배포했는데, 오히려 그 두개가 더 쉬웠다.
이번에 SEO 점수를 100점을 맞아보았다ㅋㅋ
다음 포스팅은 SEO 최적화 꿀팁을 정리해봐야겠다.
Github-actions, S3, CloudFront, Route53으로 React CI/CD하기
AWS : 정적 사이트 배포 하는 4가지 방법 (feat: cloudfront / nginx /amplify / netlify)