참고자료
기존 배포 방식은 로컬에서 build
를 한 뒤, FileZilla
라는 SFTP 툴을 이용하여 직접 파일을 내리고 올리는 수동적인 형태였다.
작업 내역을 반영할 때마다 이런 반복적인 작업을 하는 게 비효율적이라고 생각되어 자동화 시스템을 구축해야겠다고 다짐하게 됐고, 여러가지 자동화 툴들이 있지만 그 중에서도 aws S3
, CloudFront
, github actions
을 사용한 CI/CD pipeline
을 작성하기로 결정했다.
좀 더 자세한 계기는 배포 자동화 프로젝트(3)에서 확인 가능하다.
- CRA 내 main 브랜치에서 push
- GitHub Actions 내에서
build and deploy to S3
- CloudFront 캐시 무효화
이런 흐름으로 배포 자동화를 위한 CI/CD 파이프라인을 구축할 예정이다.
그리고 S3 버킷에서 제공하는 정적 웹 호스팅 기능을 통해 파이프라인이 정상적으로 구동하는지 확인한다.
S3 버킷, CloudFront 생성 및 환경변수 관리를 위한 github repository 내 secrets 관리 방법은 배포 자동화 프로젝트(4)에서 확인 가능하다.
name: Build and Deploy To S3
on:
push:
branches:
- main # main 브랜치에 push가 감지되었을 때 jobs 실행
jobs:
echo:
runs-on: ubuntu-latest
steps:
- run: echo "🚀 This workflow is automatically triggered by ${{ github.actor }} "
- name: 배포 및 빌드 시작 슬랙 알림
if: ${{ always() }}
id: slack-notification
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"channel": "채널 ID",
"attachments": [
{
"color": "#FFD55A",
"title": "${{ github.repository }}",
"title_link": "https://github.com/${{github.repository}}",
"text": "테스트 서버 빌드 및 배포 시작하겠습니다. :rocket:",
"fields": [
{
"title": "브랜치",
"value": "${{ github.ref_name }}",
"short": true
},
{
"title": "작업자",
"value": "${{ github.actor }}",
"short": true
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: '{SLACK API에서 제공하는 URL}' # 실제 값으로 넣어줘야 함.
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: '12.x' # 프로젝트에서 사용 중인 노드 버전에 맞게 설정
- name: .env.dev 파일 생성 및 환경변수 설정 # 테스트 서버용 env 파일명
run: | # 환경변수는 github actions secret variable 로 관리
echo "NODE_ENV=${{ secrets.NODE_ENV }}" >> .env.dev
echo "REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }}" >> .env.dev
echo "REACT_APP_ADMIN_API_URL=${{ secrets.REACT_APP_ADMIN_API_URL }}" >> .env.dev
echo "REACT_APP_KAKAO_AUTH_URI=${{ secrets.REACT_APP_KAKAO_AUTH_URI }}" >> .env.dev
echo "REACT_APP_NAVER_AUTH_URI=${{ secrets.REACT_APP_NAVER_AUTH_URI }}" >> .env.dev
echo "REACT_APP_KAKAO_CLIENT_ID=${{ secrets.REACT_APP_KAKAO_CLIENT_ID }}" >> .env.dev
echo "REACT_APP_NAVER_CLIENT_ID=${{ secrets.REACT_APP_NAVER_CLIENT_ID }}" >> .env.dev
echo "REACT_APP_REDIRECT_URI=${{ secrets.REACT_APP_REDIRECT_URI }}" >> .env.dev
echo "REACT_APP_CHANNEL_IO_KEY=${{ secrets.REACT_APP_CHANNEL_IO_KEY }}" >> .env.dev
- name: dotenv, dotenv-expand 설치 # env 접근을 위한 툴 설치
run: npm install dotenv dotenv-expand
- name: 테스트 서버 빌드 # package.json scripts 내 build-dev 명령어 실행
run: npm run build-dev
env:
CI: false # default는 true
- name: AWS Resource에 접근할 수 있게 AWS credentials 설정
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-2
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # 환경변수는 github actions secret variable 로 관리
- name: S3 버킷에 빌드한 파일들 새로 업로드
run: aws s3 cp build/ s3://{S3 버킷 이름}/ --recursive # 실제 값으로 넣어줘야 함.
- name: CloudFront 캐시 무효화
run: aws cloudfront create-invalidation --distribution-id {CloudFront ID값} --paths "/*" # 실제 값으로 넣어줘야 함.
- name: 빌드 및 배포 성공 슬랙 알림
if: success()
id: slack-success
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"channel": "채널 ID",
"attachments": [
{
"color": "#36a64f",
"title": "${{ github.repository }}",
"title_link": "https://github.com/${{github.repository}}/actions",
"text": "빌드 및 배포를 성공적으로 완료했습니다. :tada:",
"fields": [
{
"title": "레포",
"value": "${{ github.repository }}",
"short": true
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: 'https://hooks.slack.com/services/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'
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- name: 빌드 및 배포 실패 슬랙 알림
if: failure()
id: slack-failure
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"channel": "채널 ID",
"attachments": [
{
"color": "#ff0000",
"title": "${{ github.repository }}",
"title_link": "https://github.com/${{github.repository}}/actions",
"text": "빌드 및 배포에 실패하였습니다. 에러 메세지를 확인해주세요. :x:",
"fields": [
{
"title": "레포",
"value": "${{ github.repository }}",
"short": true
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: '{SLACK API에서 제공하는 URL}' # 실제 값으로 넣어줘야 함.
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
name: Checkout code
:checkout
을 통해 github actions가 해당 레포지토리에 접근 가능하도록 허용name: Set up Node.js
: 프로젝트가 사용 중인 노드 버전에 맞게 설정name: .env.dev 파일 생성 및 환경변수 설정
: 빌드에 필요한 환경변수 값들을 env 파일을 통해 관리, 접근한다name: dotenv, dotenv-expand 설치
:.env.dev
파일에 접근하기 위한 툴 설치name: 테스트 서버 빌드
: 빌드 명령어 실행name: AWS Resource에 접근할 수 있게 AWS credentials 설정
: aws에서 발급 받아 github action 내 secrets로 저장해둔 환경변수 값을 통해 actions와 aws를 연결name: S3 버킷에 빌드한 파일들 새로 업로드
:build/
(빌드 폴더 내 모든 파일들, 즉 빌드 후 생성된 정적 파일들)을 S3 버킷으로 업로드name: CloudFront 캐시 무효화
: S3로 성공적으로 업로드한 후 CloudFront 내 캐시 무효화 작업 실행
💡 코드 중간 중간에 끼워져 있는 슬랙 알림 기능들은 [Github Actions] GitHub Actions을 이용한 Slack 알림 설정하기 를 보면 쉽게 따라할 수 있다.
- Github actions 가 실행되면 "테스트 서버 빌드 및 배포 시작하겠습니다." 라는 알림을 슬랙으로 발송
- 빌드 및 배포가 성공적으로 마쳤을 시 (
if: success()
) 성공 알림 슬랙으로 발송- 빌드 및 배포가 실패했을 시 (
if: failure()
) 실패 알림 슬랙으로 발송
- name: 프로젝트 빌드
run: npm run build-dev
"scripts": {
"build-dev": "env-cmd .env.dev node scripts/build.js",
}
npm run build-dev
실행 시 env-cmd
를 이용하여 .env.dev
파일 내 환경변수 값에 접근하는 게 가능 gitignore
에 .env.dev
파일이 추가되어 있기 때문에 CI 환경에서는 당연히 찾을 수가 없음)이는
.env.dev
파일이 로컬에 존재하고,gitignore
에 추가되어 있기 때문에 Git 저장소에는 포함되지 않지만 로컬에서는 문제없이 동작하는 것.
- name: .env.dev 파일 생성 및 환경변수 설정 # 테스트 서버용 env 파일명
run: | # 환경변수는 github actions secret variable 로 관리
echo "NODE_ENV=${{ secrets.NODE_ENV }}" >> .env.dev
echo "REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }}" >> .env.dev
echo "REACT_APP_ADMIN_API_URL=${{ secrets.REACT_APP_ADMIN_API_URL }}" >> .env.dev
echo "REACT_APP_KAKAO_AUTH_URI=${{ secrets.REACT_APP_KAKAO_AUTH_URI }}" >> .env.dev
echo "REACT_APP_NAVER_AUTH_URI=${{ secrets.REACT_APP_NAVER_AUTH_URI }}" >> .env.dev
echo "REACT_APP_KAKAO_CLIENT_ID=${{ secrets.REACT_APP_KAKAO_CLIENT_ID }}" >> .env.dev
echo "REACT_APP_NAVER_CLIENT_ID=${{ secrets.REACT_APP_NAVER_CLIENT_ID }}" >> .env.dev
echo "REACT_APP_REDIRECT_URI=${{ secrets.REACT_APP_REDIRECT_URI }}" >> .env.dev
echo "REACT_APP_CHANNEL_IO_KEY=${{ secrets.REACT_APP_CHANNEL_IO_KEY }}" >> .env.dev
- name: dotenv, dotenv-expand 설치 # env 접근을 위한 툴 설치
run: npm install dotenv dotenv-expand
dotenv, dotenv-expand를 로컬에서 설치를 해도 CI에서는 라이브러리를 못 찾는 오류가 발생해서 CI 내에서도 확실하게 설치되도록 보장하기 위해 workflow에 install 명령어를 추가해주었다.
dotenv
, dotenv-expand
require문 추가// 수정 전
// Ensure environment variables are read.
require('../config/env');
// 수정 후
// Ensure environment variables are read.
const dotenv = require('dotenv');
const dotenvExpand = require('dotenv-expand');
const myEnv = dotenv.config({ path: '../config/.env.dev' }); // Adjust the path if needed
dotenvExpand(myEnv);
require('../config/env');
dotenv
를 이용하여 .env 파일 내 환경 변수를 읽고,dotenv-expand
를 사용해 확장하는 역할을 한다.
build.js 내에 위처럼 실행 코드를 추가해줘야 설치해준
dotenv
와dotenv-expand
가 제대로 작동한다.
env 환경 변수 문제를 해결했음에도 불구하고 자꾸 build가 fail되는 문제가 발생했다.
이유를 찾아보니, 로컬에서는 warning과 같은 경고문이 발생해도 빌드해주는 반면, CI 환경 내에서는 이러한 warning을 error로 인식해서 빌드를 실패하게 하는 것이다.
- name: 테스트 서버 빌드 # package.json scripts 내 build-dev 명령어 실행
run: npm run build-dev
env:
CI: false # default는 true
빌드를 실행하는 명령어에
CI: false
조건을 추가해주면 warning이 있더라도 무시하고 빌드를 해준다.
이제 커밋 메세지 작성 후 지정해준 브랜치 내에서 push 만 하면 빌드부터 배포까지 자동으로 실행해주는 CI/CD 파이프라인이 구축되었다! 👏🏻
다음에는 빌드 시간을 단축할 수 있는 방법을 고안 해봐야겠다.