GitHub Actions
GitHub에서 제공하는 CI/CD 도구입니다. CI/CD는 간단하게 말하자면 빌드, 테스트, 배포를 자동화하는 것을 뜻합니다.
이 글을 통해 하고자 하는 것은 다음과 같습니다.
- 커밋을 GitHub에 push할 때 마다 자동으로 빌드
- 빌드된 결과를 GitHub Pages로 자동 배포
이걸 한 번 쯤은 보신 적 있지 않습니까?
어떤 레포지토리에서는 커밋 아래에 보면 ✔체크 표시(혹은 ❌실패 표시)가 나타납니다. GitHub Actions 실행 결과를 표시하는 것인데요,
이 글에서 GitHub Actions 설정을 마치면 동일한 표시를 확인할 수 있을 것입니다.
GitHub Actions workflow 추가하기
하고자 하는 작업(배포, 테스트) 단위를 workflow라고 합니다. workflow에서는 세부적으로 또 여러 개의 job이 있습니다.
가장 먼저, workflow를 추가해봅시다.
.github/workflows 폴더를 만들고, deploy.yml 파일을 만들어주세요. yml파일의 이름은 임의로 정해도 좋습니다.
workflow 이름과 실행 조건 설정
deploy.yml 파일을 열고 workflow 설정을 진행하겠습니다.
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이 끝난 후에 수행하도록 할 수도 있습니다.
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
에서 이전에 실행중인 작업이 있을 때 취소할 지 여부를 설정합니다. 빌드는 최신을 기준으로만 진행하면 되기에 취소하도록 설정하였습니다.
위의 옵션과 관련하여 참고할 만한 문서 링크를 첨부합니다.
runs-on
에서 사용 가능한 환경: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on- concurrency에 대한 공식 문서: https://docs.github.com/en/actions/using-jobs/using-concurrency
steps 구성하기
하나의 job
은 여러 개의 step
으로 구성되며, 순차적으로 실행됩니다.
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 실행 확인
.github/workflows/deploy.yml
파일을 생성하고 커밋하여 푸시하면 커밋 옆에 아이콘이 생길 겁니다.
workflow가 성공적으로 인식되고 실행중이라는 뜻입니다!
아이콘을 클릭하고 Details를 누르면 workflow 실행을 자세히 볼 수 있습니다.
deploy.yml 에서 정의한 대로 workflow가 잘 실행되는 모습을 볼 수 있습니다!
gh-pages
라는 브랜치가 생성되었을 겁니다. 이 브랜치가 GitHub Pages 배포 브랜치로 사용되도록 설정을 진행해주면 됩니다.
GitHub Pages 배포 브랜치 설정
gh-pages
브랜치에 있는 파일들이 GitHub Pages에서 배포되도록 설정할 겁니다.
Settings > Pages 에 들어가서 위와 같이 설정해주세요.
배포 확인
https://<본인 아이디>.github.io/<레포지토리 이름>
주소로 들어가면 배포를 확인할 수 있습니다.
잘 모르겠다면 Environments > github-pages 를 클릭한 뒤 View Deployment를 클릭하세요.
React 앱이 잘 동작하는지도 확인해보세요!
심화: react-router-dom
의 BrowserRouter
이미 위의 글에서 진행하며 코드에 포함했지만, 추가적으로 다루어보겠습니다.
BrowserRouter
는 History API를 사용하여 라우팅을 구현합니다. SPA(Single Page App)이지만 URL이 실제 페이지가 이동한 것처럼 보이는 장점이 있는데, 문제는 새로고침을 했을 때 입니다.
1: https://solo5star.github.io/react-payments/
2: https://solo5star.github.io/react-payments/register
/register
에서 새로고침을 하면, /register
파일이 존재하지 않기 때문에 404 Not Found 페이지가 나타날 겁니다.
GitHub는 404.html
파일을 추가하여 404 페이지를 커스텀할 수 있는 방법을 제공하는데, 이를 사용하면 위의 문제를 해결할 수 있습니다.
index.html
파일을 그대로 404.html
에서 사용하면 됩니다. 그러면 /register
로 접속해도, /users/100
으로 접속해도 index.html
이 사용되면서 문제가 해결됩니다.
이 방식의 한계점은, 페이지는 정상적으로 뜨지만 응답 코드는 여전히 404로 나타난다는 점입니다.
맺음말
SPA 환경 + History API 라우팅을 하는 프로젝트의 GitHub Actions 자동 빌드 배포를 해보았는데요. 404.html
을 사용한 방법으로, 사실 우회적으로 설정한 것이긴 합니다. Hash를 사용하여 라우팅하는 HashRouter
를 고려해보아도 좋을 것 같습니다.