본문 바로가기

개발 관련 지식

React 자동 빌드: GitHub Actions를 활용한

React 앱 배포 버전이 바뀔 때마다, 빌드 파일을 서버 개발자님에게 전달해야 했다.

서버 개발자분도 다양한 업무를 보시다 보니, 빌드 파일을 전달해도 응답이 늦거나 실제 서버에 반영되는 시간이 늦어졌다.

 

모바일 서비스와 다르게, 웹 서비스는 심사 과정을 거치지 않고 서비스가 배포되기 때문에 이러한 늦은 반영은 디메리트가 있다고 생각했다.

앞으로 몇달간 여러 프로젝트를 진행하면서, 이러한 상황을 지속적으로 겪을 것이였기 때문에 자동 빌드 및 자동 배포를 고민하게 되었다.


GitHub Actions을 선택한 이유

CI/CD 도구는 여러 가지가 있지만, 이번 자동화 과정에서는 GitHub Actions을 선택했다.

 

이유는 다음과 같다.

 GitHub과 네이티브 통합 → 별도 설치 없이 바로 사용 가능
 간단한 설정  .github/workflows/에 YAML 파일만 추가하면 완료
 클라우드 기반 관리 → 별도의 서버 없이 GitHub이 직접 관리
 비용 효율성 → GitHub Free 플랜에서도 충분한 실행 시간 제공 [ 월 2,000 분 무료 ]

 GitHub 이벤트 트리거 지원 → Push, PR 등 다양한 조건 설정 가능

 

실제로 사용한 명령어 러닝커브가 적었다.

대체적으로 평상시에 사용하던, 쉘 명령어가 많이 활용된다.


GitHub Actions을 활용한 배포 자동화

자동화의 전체적인 구조는 아래와 같다.

  1. 메인 브랜치에 push 발생
  2. GitHub Actions가 실행되어 프론트엔드 저장소의 최신 코드 다운로드
  3. 빌드 실행 및 결과물을 백엔드 저장소에 적용 [ 테스트 코드가 있다면, 이를 실행하기도 한다. ]
  4. 변경된 내용을 새로운 브랜치에서 PR 생성 및 병합 시도
  5. 머지 충돌 발생 시 경고 처리

[ 이후에, 서버 개발자님과 이야기해서 백엔드 저장소의 서비스를 원격 서버에 업로드까지 도전해보고 싶다. ]

GitHub Actions YAML

name: Update Build file to Back Repository

on:
  push:
    branches:
      - main

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
    
      - name: Download project from Front Repository
        uses: actions/checkout@v3 # 명령어 집합
        with:
          path: front-repo # 프로젝트가 저장될 폴더명
        
      - name: Download project from Back Repository
        uses: actions/checkout@v3
        with:
          repository: 주소
          token: ${{ secrets.TOKEN }}
          ref: develop
          path: backend-repo

      - name: Build
        run: |
          cd front-repo
          yarn install  # 의존성 설치
          yarn run build  # 빌드 실행
          
      - name: Set Branch name
        run: echo "AUTO_BUILD_NAME=autoBuild_${{ github.run_id }}_$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_ENV  # 자동 빌드 브랜치 이름 설정

      - name: Move To Branch
        run: |
          cd backend-repo
          git pull origin develop
          git branch $AUTO_BUILD_NAME
          git checkout $AUTO_BUILD_NAME
          
      - name: Replace Static Files
        run: |
          rm -rf backend-repo/-/static/dist
          cp -R front-repo/dist backend-repo/-/static/
          
      - name: Copy contents and Write
        run: |
          ASSET_FILE=$(ls backend-repo/-/static/dist/assets | head -n 1)
          sed -i "10s|.*|<script type=\"module\" crossorigin src=\"{% static 'dist/assets/$ASSET_FILE' %}\"></script>|" backend-repo/-/templates/base.html
          
      - name: Commit and Push Changes
        run: |
          cd backend-repo
          git config --global user.name "GitHub Actions"
          git config --global user.email "email@gmail.com"
          git add .
          git commit -m "Update dist folder with latest build"
          git push --set-upstream origin $AUTO_BUILD_NAME
        env:
          KEY: ${{ secrets.TOKEN }}

      - name: Check for Merge Conflicts
        run: |
          cd backend-repo
          git fetch origin main
          git merge --no-commit --no-ff origin/main || echo "conflict" > merge_status.txt
          
      - name: Handle Merge Conflicts
        if: ${{ failure() || (hashFiles('**/merge_status.txt') != '') }}
        run: |
          echo "Merge conflict detected. Sending alert..."
        env:
          KEY: ${{ secrets.TOKEN }}

      - name: Create Pull Request and Merge
        if: ${{ success() && hashFiles('**/merge_status.txt') == '' }}
        run: |
          cd backend-repo
          echo "No conflicts detected. Creating Pull Request..."  
          gh pr create \ 
            --base main \ 
            --head $AUTO_BUILD_NAME \ 
            --title "New Build Files" \ 
            --body "Automatically generated Pull Request for new build files."
          
          echo "Attempting to merge the Pull Request..."
          PR_NUMBER=$(gh pr view --json number -q '.number')
          gh pr merge $PR_NUMBER --merge --admin
          echo "Pull Request #$PR_NUMBER successfully merged."
          git checkout develop
          git branch -d $AUTO_BUILD_NAME
        env:
          GH_TOKEN: ${{ secrets.TOKEN }}

비용에 관해서는, 기업의 업무 스타일에 따라서 다를 것이다.

자체 서비스가 존재하여, 새로운 업데이트가 많다면 깃 액션의 무료 사용량이 부족할지도 모른다.