React 프로젝트를 GitHub Actions와 GitHub Page를 이용해서 배포해보자.

나는 기존에 오픈 소스로 개발 중인 프로젝트를 사용했다.

전체 코드

전체 코드를 먼저 보고 하나씩 의미를 알아 가보자.

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2.1.2
        with:
          node-version: "12.x"

      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
                        ${{ runner.os }}-node-

      - name: Install dependencies
        run: npm install

      - name: Make env file
        run: |
          touch .env
          echo REACT_APP_MODE=prod >> .env
          echo REACT_APP_GA_ID=${{ secrets.REACT_APP_GA_ID }} >> .env          

      - name: Build app
        run: npm run build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build

workflow 이름 및 이벤트 정의

name: Deploy

on:
  push:
    branches:
      - master

workflow의 이름은 Deploy로 정의하고, 이벤트는 master 브랜치로 푸시하는 것으로 정의하였다.

job 정의

jobs:
  deploy-job:
    runs-on: ubuntu-18.04

job의 이름은 deploy-job으로 정의하고, runner의 os는 ubuntu-18.04로 정의한다.

step 정의

checkout actions

- uses: actions/checkout@v2

이 액션은 GitHub Actions의 workflow가 레포지토리에 접근 할 수 있도록 $GITHUB_WORKSPACE에 레포지토리를 복사해 주는 일을 해준다. 예를 들어, /home/runner/work/my-repo-name/my-repo-name 경로로 레포지토리를 복사해준다. (기본 변수 참고)

setup-node actions

- name: Setup Node
  uses: actions/setup-node@v2.1.2
  with:
    node-version: "12.x"

이 액션은 노드 환경을 세팅해준다. Actions를 실행하면 아래와 같이 log가 찍힌다.

with으로 사용된 변수들에 대해 알아보자.

  • node-version: node 버전을 정의해주는 변수다. 이 변수는 옵션이다. (README에서 이 변수가 만약 정의되어 있지 않다면 PATH를 사용한다고 되어있는데 이미 정의되어있는 node 버전을 사용하는 것으로 보인다)
  • always-auth: npmrcalways-auth 변수의 값을 세팅하는 변수이다.
  • check-latest: 이 변수의 기본값은 false인데, false로 두면 local에 캐시 된 노드 버전을 사용한다. 만약 true로 세팅하면 캐시 된 노드 버전을 확인하고 최근에 업데이트되지 않았으면, 새로 다운로드한다.
  • token: node-versions에 요청할 때 쓰이는 값인데, 이미 기본값으로 ${{ github.token }}이 세팅되어 있어서 사용자(이 액션을 쓰는 개발자)는 신경 쓸 필요 없다.

cache actions

- name: Cache dependencies
  uses: actions/cache@v2
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
            ${{ runner.os }}-node-

이 액션은 의존성과 빌드된 결과를 캐싱하여 workflow의 실행 시간을 향상해준다.

일반적으로 Runner에서 실행되는 Job은 새로운 가상 환경에서 실행되서, 실행될 때마다 의존성을 다운로드 해야 한다. 그렇기 때문에 시간과 비용이 많이 소모된다,

  • path: 캐싱하고 저장할 파일, 디렉터리를 지정한다. wildcard patterns(@actions/glob에서 더 자세히 알아볼 수 있다.)으로 표현 할 수 있다.

  • key: 캐시를 저장하고 보관하기 위한 키다.

  • restore-keys: 키에 대한 캐시가 발생하지 않을 때, 캐시를 복원하기 위해 사용될 키의 순서가 정의된 목록이다.

Node의 npm 의존성 관리 툴에 대한 구성은 문서에 잘 정리되어 있다. 각 의존성 툴별로 각자의 방법이 있으니 문서에서 확인하면 된다.

나의 프로젝트에서는 캐싱 되기 전에 의존성 설치가 49s가 걸렸다. 캐싱 된 후에는 41s로 줄었다.(응?) 뭔가 비약적으로 줄진 않았는데, 로그를 보니 캐싱이 되었으니 아무튼 성공이다.

❗️ 캐싱되기 전에 워크플로우를 실행하면 캐싱 관련 로그가 Cache not found for input keys: ~로 찍혀 있다. 나는 처음에 액션이 동작 자체가 안되는 것인 줄 알고 헷갈렸는데, 알고 보니 두 번째부터 캐싱 된 것을 잘 가져왔다.(아니 그러면 캐싱 되었다는 메시지를 띄워주는 게 더 좋지 않았을까?)

Build app

- name: Install dependencies
  run: npm install

- name: Make env file
  run: |
    touch .env
    echo REACT_APP_MODE=prod >> .env
    echo REACT_APP_GA_ID=${{ secrets.REACT_APP_GA_ID }} >> .env    

- name: Build app
  run: npm run build

app을 빌드하기 위해 의존성 들을 설치한다.

Make env file step은 아직 좋은 방법을 못 찾은 케이스다. React에 사용할 환경 변수를 .env에 정의했는데, 적용하는 방법으로 위와 같이 파일을 만드는 방법밖에 생각을 못 해봤다.(좋은 방법이 있으면 소개 부탁드립니다.)

Deploy to GitHub Page

- name: Deploy
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./build

이 액션은 정적 파일들을 GiHub Pages에 배포해준다. 위의 예제는 ./build 디렉터리를 gh-pages 브랜치에 배포하는 의미다.

❗️ 예제에 사용된 GITHUB_TOKEN은 GitHub Actions runner가 자동으로 생성해주기 때문에, 유저가 따로 설정할 필요 없다.

GitHub Actions 실행

이제 push를 하면 Github Actions가 실행되고 배포가 끝난다.


Reference