프로젝트를 하며 배포 자동화를 위해 CI/CD를 구축하였다. 구축한지는 조금 되었으나, 매 프로젝트마다 유용하게 쓸 것 같아 방법을 기록해두려 한다.
CI = continuous integration (지속적 통합)
CD = continuous delivery (지속적 배포)
쉽게 프론트엔드를 기준으로 설명하자면, 지속적 통합은 프로젝트를 빌드하고 테스트하는 과정을 자동화한 것이다. 지속적 배포는 말 그대로 빌드된 프로젝트를 서버에 업데이트하여 올리는 것이다. 이를 통해 자동화하여 시간도 줄이고, 안정성도 보장할 수 있다.
하루만 시간을 내어 CI/CD를 구축해두면, 프로젝트를 하는 남은 기간은 아주 편해질 것이다. 그럼 CI/CD를 구축하는 방법에 대하여 소개해보겠다.
지속적 배포를 하려면 일단 배포할 방법을 하나 선택하여 설정해두어야 한다. 쉽게 netlify로도 할 수 있지만, 나는 AWS를 이용하여 배포를 하였다. (netlify에서 organization 내의 레포를 배포하려면 유료여서..)
최소한으로 배포를 하고 싶다면 다음과 같은 방식을 따르면 된다.
여기까지만 해도 bucket url로 접근이 가능하지만, 보통 CloudFront를 함께 설정해둔다. CloudFront는 CDN의 일종으로 전세계에 캐시 서버를 분산해두어 데이터 전송을 빠르게 하도록 도와준다. S3와 CloudFront를 함께 쓰면 S3에 직접 접근하는 경우를 줄여 S3 자체적인 부하를 줄일 수 있어 안정성과 속도를 높여준다.
CloudFront를 사용하고 싶다면 CloudFront 구축 방법 참고!
+2024.01.20 수정
이와 관련하여 새로 글을 작성하였다. 👉 프론트엔드 AWS 배포하기
github action을 통하여 나는 CI/CD를 구축하였다. main 브랜치로 merge시에 자동으로 프로젝트를 빌드 및 테스트하고 S3에 업데이트된 파일들을 올려주도록 하였다.
레포지토리 내로 들어가서 Actions 메뉴를 클릭한다.
그러면 처음에는 다음과 같이 여러 템플릿을 추천해준다.
아무것도 모를 때 템플릿을 사용하면 도움이 많이 된다. 하지만 나는 파란색 글씨의 set up a workflow yourself를 클릭하여 스스로 만들어 보았다.
클릭하면 main.yml 파일이 나온다. 이 파일이 바로 github action에 관한 파일이다. 큰 뼈대를 살펴보면서 어떻게 작성하는지 알아보자.
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Runs a single command using the runners shell
- name: Run a one-line script
run: echo Hello, world!
# Runs a set of commands using the runners shell
- name: Run a multi-line script
run: |
echo Add other actions to build,
echo test, and deploy your project.
name
해당 github action의 이름이다. 원하는 이름을 작성하면 된다.
on
github action을 실행하는 조건에 대한 내용이다.
push나 pull request시에 할 수 있다. 그리고 어느 branch에서 하면 github action을 실행하는지 설정할 수 있다.
따라서 위 코드의 의미는 main branch로 코드를 push 및 pull request할 때에 github action이 실행된다는 의미이다.
jobs
여기는 github action시 자동으로 해야 할 일들을 작성하는 곳이다.
- build는 모든 작업을 아우르는 이름이다. 다른 이름으로 할 수도 있다.
- runs-on에는 어떤 OS 환경에서 실행될지를 작성하면 된다.
- steps 에 해야 할 일을 모두 작성하면 된다. name에는 작업에 대한 제목을 적으면 되고 run에 실행 코드를 작성하면 된다. |
바를 사용하여 실행 코드를 구분하고 순서대로 실행되게 해준다.
기본적인 포맷은 다음과 같지만, 중요하게 알아두어야 할 부분이 바로 github 환경변수이다. S3에 배포하기 위해서는 AWS access key, secret key, S3 bucket 이름등이 필요할 뿐만 아니라, 실제 코드에서도 중요한 값은 따로 환경변수로 지정해두기 때문이다. 하지만, 왜 로컬 파일이 아닌, git에 환경변수를 저장해야할까?
보통 로컬에서 .env에 환경변수를 저장하여 작업하고 git push시에는 .env파일을 제거한다. 이러면 github action시에 필요한 환경변수값을 알 수 없다. 그렇다고 해서, 환경변수를 github에 바로 올리자니 보안상의 문제가 있다.
따라서 github 환경변수를 사용해야한다. 이는 repository -> Settings -> Secrets and variables -> actions에서 저장할 수 있다.
Add respository secret을 클릭하여 환경변수를 만들면 된다. 만들고 난 후 키에 대한 값은 나중에 열람할 수 없으니 유의!
이렇게 만든 환경변수를 어떻게 github action시 반영하여 빌드하게 할까?
runs-on과 steps 사이에 env를 사용해주면 된다. 그리고 steps에서 env에서 지정한 환경변수값을 사용해주면 된다. 자세한 코드는 3-4 예시를 보면 쉽게 이해할 수 있다.
프로젝트 코드에서 사용되는 환경변수 값일 땐, $GITHUB_ENV에 그 값을 등록해주면 된다. (3-4 예시 참고)
name: Deploy to s3
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }}
steps:
- uses: actions/checkout@main
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Generate environment variables file for production
run: |
echo "REACT_APP_AWS_SERVER=${{ secrets.REACT_APP_AWS_SERVER }}" >> $GITHUB_ENV
- name: npm install, build
run: |
cd frontend
npm install --legacy-peer-deps
CI='' npm run build
- name: Deploy
uses: reggionick/s3-deploy@v3
with:
folder: frontend/build
bucket: ${{ secrets.S3_BUCKET }}
bucket-region: ${{ secrets.S3_BUCKET_REGION }}
invalidation: /
delete-removed: true
no-cache: true
private: true
위의 코드는 내가 실제 프로젝트에서 사용하고 있는 main.yml 파일이다. 프론트엔드 CI/CD를 구축할 때 참고하여 작성하면 좋을 것 같다.