IT일상

GitHub Actions: CRA(create-react-app) 빌드 + GitHub Pages 배포 자동화하기

  • 프론트엔드
  • 인프라
Profile picture

Written by solo5star

2023. 4. 23. 18:00

GitHub Actions

1

GitHub에서 제공하는 CI/CD 도구입니다. CI/CD는 간단하게 말하자면 빌드, 테스트, 배포자동화하는 것을 뜻합니다.

이 글을 통해 하고자 하는 것은 다음과 같습니다.

  • 커밋을 GitHub에 push할 때 마다 자동으로 빌드
  • 빌드된 결과를 GitHub Pages로 자동 배포

이걸 한 번 쯤은 보신 적 있지 않습니까?

2

어떤 레포지토리에서는 커밋 아래에 보면 ✔체크 표시(혹은 ❌실패 표시)가 나타납니다. GitHub Actions 실행 결과를 표시하는 것인데요,

이 글에서 GitHub Actions 설정을 마치면 동일한 표시를 확인할 수 있을 것입니다.

GitHub Actions workflow 추가하기

3

하고자 하는 작업(배포, 테스트) 단위를 workflow라고 합니다. workflow에서는 세부적으로 또 여러 개의 job이 있습니다.

가장 먼저, workflow를 추가해봅시다.

4

.github/workflows 폴더를 만들고, deploy.yml 파일을 만들어주세요. yml파일의 이름은 임의로 정해도 좋습니다.

workflow 이름과 실행 조건 설정

deploy.yml 파일을 열고 workflow 설정을 진행하겠습니다.

deploy.yml
name: Deploy

on:
  push:
    branches: ['step1']

  workflow_dispatch:
  • name: 에는 workflow의 이름이 됩니다.
  • on: 은 workflow가 실행될 조건입니다. 위의 예시에선 step1 이라는 브랜치에 push가 될 때 마다 실행되도록 설정되어 있습니다.
  • workflow_dispatch: 는 workflow를 수동으로도 실행이 가능하게끔 해주는 옵션입니다.

workflow jobs 구성하기

name: Deploy

on:
  push:
    branches: ['main']

jobs:
  build-prepare-1:
    name: Build Prepare 1
  build-prepare-2:
    name: Build Prepare 2
  deploy:
    name: Deploy
    needs: [build-prepare-1, build-prepare-2]

하나의 workflow는 여러 개의 job으로 구성되어 있습니다. 기본적으로 병렬로 수행되지만 needs를 추가하면 특정 job이 끝난 후에 수행하도록 할 수도 있습니다.

deploy.yml
name: Deploy

on:
  push:
    branches: ['step1']

  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    concurrency:
      group: ${{ github.workflow }}
      cancel-in-progress: true

복잡한 workflow는 아니기 때문에, job을 하나만 추가하겠습니다.

  • runs-on: 은 실행할 환경을 설정합니다. 우분투, 윈도우 서버, macOS가 있습니다. 어떤 환경을 지원하는지는 아래 링크에 들어가시면 자세히 나와있습니다.
  • permissions: 는 workflow에 권한을 추가합니다. peaceiris/actions-gh-pages 에서 요구하는 설정이기 때문에, 적어주도록 합시다.
  • concurrency: 는 동시성과 관련한 옵션입니다. group: 은 키 값이 되며, 동시에 한 번만 실행되도록 해줍니다. 위의 예시에선 deploy.yml workflow가 동시에 한 번만 실행되도록 합니다.
  • cancel-in-progress: 는 똑같은 group에서 이전에 실행중인 작업이 있을 때 취소할 지 여부를 설정합니다. 빌드는 최신을 기준으로만 진행하면 되기에 취소하도록 설정하였습니다.

위의 옵션과 관련하여 참고할 만한 문서 링크를 첨부합니다.

steps 구성하기

하나의 job은 여러 개의 step으로 구성되며, 순차적으로 실행됩니다.

deploy.yml
name: Deploy

on:
  push:
    branches: ['step1']

  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    concurrency:
      group: ${{ github.workflow }}
      cancel-in-progress: true

    steps:
      # 레포지토리의 소스 코드를 사용하는 step입니다. 이게 없으면 빈 폴더가 됩니다.
      - name: Use repository source
        uses: actions/checkout@v3

      # node.js 런타임을 사용합니다.
      - name: Use node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18 # 프로젝트에서 사용하는 node.js 버전을 사용하세요.

      # node_modules 폴더를 캐싱합니다.
      # 이게 없으면 workflow를 실행할 때 마다 node_modules를 다시 생성합니다.
      - name: Cache node_modules
        id: cache
        uses: actions/cache@v3
        with:
          # 캐싱할 폴더를 지정합니다.
          path: '**/node_modules'
          # package-lock.json 파일의 변경이 발생했을 경우 새로 캐싱될 수 있도록 해줍니다.
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          # cache hit이 발생하지 않았을 경우, restore-keys 에 있는 것을 꺼냅니다.
          # 단, npm ci 명령은 실행됩니다. 기존의 node_modules 캐시를 기반으로
          # 추가된 패키지만 설치할테니, 아예 처음부터 node_modules 폴더를 생성하는 것보다
          # 시간이 적게 걸릴 겁니다.
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        run: npm ci
        # cache hit이 발생한 경우 (즉 package-lock.json이 변경되지 않은 경우)
        # npm ci 실행은 skip 됩니다.
        if: steps.cache.outputs.cache-hit != 'true'

      # $GITHUB_REPOSITORY 환경 변수는 solo5star/react-payments 형태의
      # owner과 repository 값입니다. 이를 .env에 PUBLIC_URL=/react-payments/ 형태로
      # 저장하기 위한 명령입니다.
      - name: Set PUBLIC_URL
        run: |
          PUBLIC_URL=$(echo $GITHUB_REPOSITORY | sed -r 's/^.+\/(.+)$/\/\1\//')
          echo PUBLIC_URL=$PUBLIC_URL > .env

      # 빌드를 수행합니다.
      # react-router-dom의 BrowserRouter를 지원하기 위해
      # index.html 을 복사하여 404.html에서도 사용하도록 합니다.
      - name: Build
        run: |
          npm run build
          cp ./build/index.html ./build/404.html

      # 빌드된 파일들(./build)을 gh-pages 브랜치로 배포합니다.
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build

코드가 길어져서 주석으로 설명을 적어두었으니 참고해주세요.

workflow 실행 확인

5

.github/workflows/deploy.yml 파일을 생성하고 커밋하여 푸시하면 커밋 옆에 아이콘이 생길 겁니다.

workflow가 성공적으로 인식되고 실행중이라는 뜻입니다!

6

아이콘을 클릭하고 Details를 누르면 workflow 실행을 자세히 볼 수 있습니다.

7

deploy.yml 에서 정의한 대로 workflow가 잘 실행되는 모습을 볼 수 있습니다!

8

gh-pages 라는 브랜치가 생성되었을 겁니다. 이 브랜치가 GitHub Pages 배포 브랜치로 사용되도록 설정을 진행해주면 됩니다.

GitHub Pages 배포 브랜치 설정

gh-pages 브랜치에 있는 파일들이 GitHub Pages에서 배포되도록 설정할 겁니다.

9

Settings > Pages 에 들어가서 위와 같이 설정해주세요.

배포 확인

https://<본인 아이디>.github.io/<레포지토리 이름> 주소로 들어가면 배포를 확인할 수 있습니다.

10 11

잘 모르겠다면 Environments > github-pages 를 클릭한 뒤 View Deployment를 클릭하세요.

12

React 앱이 잘 동작하는지도 확인해보세요!

심화: react-router-domBrowserRouter

이미 위의 글에서 진행하며 코드에 포함했지만, 추가적으로 다루어보겠습니다.

BrowserRouter 는 History API를 사용하여 라우팅을 구현합니다. SPA(Single Page App)이지만 URL이 실제 페이지가 이동한 것처럼 보이는 장점이 있는데, 문제는 새로고침을 했을 때 입니다.

1: https://solo5star.github.io/react-payments/
2: https://solo5star.github.io/react-payments/register
13
잘 쓰다가 새로고침했는데... 404가 뜬다?

/register 에서 새로고침을 하면, /register 파일이 존재하지 않기 때문에 404 Not Found 페이지가 나타날 겁니다.

GitHub는 404.html 파일을 추가하여 404 페이지를 커스텀할 수 있는 방법을 제공하는데, 이를 사용하면 위의 문제를 해결할 수 있습니다.

index.html 파일을 그대로 404.html에서 사용하면 됩니다. 그러면 /register로 접속해도, /users/100 으로 접속해도 index.html이 사용되면서 문제가 해결됩니다.

14

이 방식의 한계점은, 페이지는 정상적으로 뜨지만 응답 코드는 여전히 404로 나타난다는 점입니다.

맺음말

SPA 환경 + History API 라우팅을 하는 프로젝트의 GitHub Actions 자동 빌드 배포를 해보았는데요. 404.html을 사용한 방법으로, 사실 우회적으로 설정한 것이긴 합니다. Hash를 사용하여 라우팅하는 HashRouter를 고려해보아도 좋을 것 같습니다.


Profile picture

Written by solo5star

안녕하세요 👋 개발과 IT에 관심이 많은 solo5star입니다

  • GitHub
  • Baekjoon
  • solved.ac
  • about
© 2023, Built with Gatsby