구현기/웹 프로젝트

Github Page + Github Action으로 CRA 리액트 앱 자동 배포하기

Sadie Kim 2024. 6. 20. 00:48

https://sadie100.github.io/CypherTypeTest/

 

사이퍼즈 성향 테스트

 

sadie100.github.io

 

3년 전에 만들고 Netlify를 통해 배포했던 사이퍼즈 플레이어 타입 테스트.

도메인도 만료된 지 한참 전이고, 얼마 전 Netlify에서도 배포했던 프로젝트가 expire됐다는 메일을 받았다.

이대로 묵혀둘까... 했는데 그래도 만들어둔 게 아쉬워서,

별도의 도메인 구매가 필요 없는 깃헙 페이지를 이용해서 배포하여

영원히 사는 프로젝트로 만들기로 했다.

하는 김에 깃헙 액션을 통해 CI/CD도 연결해서, 거하게 리팩토링 작업을 했다.

 

 

Github Action을 위한 yaml 파일 만들기

깃헙 액션 워크플로우를 만들기 위해서는 yaml 파일이 필요하다.

해당 워크플로우가 어떤 Github 브랜치 동작에 트리거될 것인지, 언제 실행될 것인지도 yaml 파일 내에서 다 설정 가능하다.

 

다음은 내가 사용한 build.yaml 파일 코드이다.

각 줄에 주석을 달아 설명을 보충했다.

name: build

# 워크플로우를 자동으로 트리거하고자 할 때, 어떤 이벤트에 연결할 것인지를 정의함
# master 브랜치에 push하거나 pull_request가 일어날 때 실행된다.
on:
  push:
    branches: ["master"]
  pull_request:
    branches: ["master"]

# 각 Job은 동일한 러너에서 실행되는 워크플로우 steps들의 집합이다.
jobs:
  build:
  	# job이 돌아갈 머신을 고르는 구문
    runs-on: ubuntu-latest
    # 사용할 환경변수를 고르는 구문. 프로젝트 환경설정에서 정의해 둔 Environments를 사용하도록
    # 지정할 수 있으며, 이 구문이 있어야 설정 페이지에서 만들어 둔 환경변수들에 접근 가능하다.
    environment: github-pages
    # gh-pages(깃헙 페이지 배포)를 위한 권한 설정
    permissions:
      contents: write
	
    # 각 job에서 실행할 명령들의 모음
    steps:
      # 리포지토리 코드를 CI 서버로 내려받고, 해당 브랜치로 전환하는 액션
      - uses: actions/checkout@v4
		
      # node.js 18 버전을 사용하도록 설정
      - name: Use Node.js 18
        uses: actions/setup-node@v3
        with:
          node-version: 18

      # 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 에 있는 것을 꺼낸다.
          restore-keys: |
            ${{ runner.os }}-node-

	  # 캐시 히트가 발생하지 않으면 npm install 명령을 실행한다.
      # 단 기존의 restore-keys 캐시에 저장된 node_modules 기반으로
      # 추가된 패키지만 설치하기 때문에, 아예 처음부터 node_modules 폴더를 생성하는 것보다
      # 시간이 적게 걸린다.
      - name: Install dependencies
        run: npm install
        if: steps.cache.outputs.cache-hit != 'true'
		
      # .env 파일 생성을 위한 코드
      # CRA는 npm run build를 할 때 .env를 자동으로 읽어와서 환경변수를 적용한다.
      # 따라서 job.environment 설정과 별개로 .env 파일을 CI 서버에 생성해야 한다.
      - name: Set environment variables
        run: |
          # 사용하고자 하는 환경변수를 등록한다.
          echo "REACT_APP_KAKAO_KEY=${{ secrets.REACT_APP_KAKAO_KEY }}" >> .env
          echo "REACT_APP_URL=${{ vars.REACT_APP_URL }}" >> .env
          # PUBLIC_URL은 CRA에서 public 폴더의 위치를 가리키는 자동 생성 환경변수이다.
          # 나의 경우 깃헙 페이지 프로젝트 url(github.io/브랜치명)을 사용해서 이 설정이 필요했다.
          # 만약 커스텀 도메인을 연결할 거라면, 해당 설정은 필요없을 수 있다.
          PUBLIC_URL=$(echo $GITHUB_REPOSITORY | sed -r 's/^.+\/(.+)$/\/\1\//')
          echo "PUBLIC_URL=$PUBLIC_URL" >> .env

      - name: Build project
        run: npm run build

      # 빌드된 파일들(./build)을 gh-pages 브랜치로 배포하는 작업. gh-pages의 원리에 대해선 후술
      - uses: peaceiris/actions-gh-pages@v4
        with:
          # 이 GITHUB_TOKEN 환경변수는 기본적으로 주어지는 것으로 별도로 생성할 필요는 없다.
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build

 

해당 yml 파일을 빌드하기 전, 프로젝트 단에서 environments 설정을 위해 해야 할 일이 있다.

 

프로젝트 환경변수 설정

깃헙 레포지토리>Settings>Environments에 들어가서 새 환경을 생성하고

깃헙 액션 안에 들어갈 환경변수를 만들어줄 수 있다.

Environment secrets는 암호화된 환경변수로, yaml 상에서 ${{ secrets.변수이름 }} 으로 접근 가능하며

일반 환경변수(Environment variable)는 ${{ vars.변수이름 }} 으로 접근할 수 있다.

해당 작업을 마친 뒤 환경변수를 사용하려면, yaml 코드의 jobs 하위 environment에

생성한 환경 이름을 입력하면 연동된다.

 

Github Page 설정

위 yaml에서 나는 마지막에 actions-gh-pages를 사용했다.

해당 액션은 깃헙 페이지 배포를 위한 작업으로, 워크플로우 작업이 성공적으로 끝났다면

레포지토리에 gh-pages 브랜치가 새로 생겼고, 빌드 결과 파일들이 복사되어 있을 것이다.

이제 깃헙 페이지가 해당 gh-pages 브랜치를 참조하도록 연결하면 된다.

 

프로젝트>Settings>Pages에 가서 깃헙 페이지를 등록한다.

이때, Deploy from a branch를 선택하고, 브랜치에 gh-pages를 등록한다. 

Deploy from a branch 선택

나는 커스텀 도메인을 설정하지 않아서 이에 대한 설명은 생략한다.

 

문제 - 에러는 안 뜨는데 페이지에 아무것도 뜨지가 않습니다.

나와 같이 프로젝트 깃헙 페이지를 이용하면서 커스텀 도메인을 사용하지 않은 경우라면 이 문제를 마주칠 수 있다.

원인은 React-router가 프로젝트의 URL(github.io/레포 이름)에서 도메인인 io까지를 루트 url로 간주하고, 레포 이름을 별도의 path 세그먼트로 인식해서 생기는 문제다.

해결방법은 간단히 라우터 파일의 BrowserRouter에 basename을 지정해주면 된다.

<BrowserRouter basename={process.env.PUBLIC_URL}/>
	<Route exact path="/" component={Main} />
    ...
</BrowserRouter>

 

마침 나는 PUBLIC_URL에 레포 이름을 넣었으므로 이를 활용했다.

문제 - 루트 페이지가 아닌 경로 페이지에서 새로고침하면 404가 뜹니다.

위의 작업을 무사히 수행하고 별 문제가 없다면 이제 링크를 통한 접속이 잘 될 것이다.

그런데 루트 페이지가 아닌 다른 하위 페이지를 통해 바로 서비스에 접속하려고 하거나 하위 페이지에서 새로고침을 한다면, 404 Not found 페이지가 보이게 될 것이다.

이는 하위 url로 접속했을 때 자동으로 핸들링해 주는 웹서버가 없으므로 생기는 문제로, 이용한 배포 서비스의 종류에 따라 이를 핸들링하기 위한 설정을 해줘야 한다.

그리고 Github Page가 이 설정이 제일 까다로운 것 같다.

 

이 문제는 CRA 공식문서에 아주 잘 안내되어 있어,

여기여기를 잘 따라하면 된다.

다만 해당 문서는 Github Action을 통한 자동배포가 아닌 셀프로 빌드하는 경우를 다루고 있으므로, 적당히 선택하여 추가 작업을 하면 된다.

(예를 들어 나처럼 build.yaml을 통한 자동배포를 할 경우, gh-pages를 package.json에 추가 설치를 할 필요는 없다.)

 

위 문서를 보고 내가 따라했던 작업은 다음과 같다.

1. package.json에 homepage 필드 추가, 설정

2. https://github.com/rafgraph/spa-github-pages 레포에 올라와 있는 404.html 파일 복사(+pathSegmentsToKeep 1로 설정), 내 프로젝트에 붙여넣기

3. 위 레포의 index.html에 있는 리다이렉트 js 스크립트 복사, 내 프로젝트에 붙여넣기

 

이 정도만 하니 잘 작동했다.

단, 만약 본인 프로젝트의 라우터 페이지에서 예외 라우트를 전부 리다이렉트 처리하는 구문이 있다면 제거해 주어야 한다.

위 방식이 404.html 페이지에 주소 이동 로직 삽입 + 해당 주소로 이동 시 리다이렉트를 통해 올바른 경로로 이동시키는 일종의 트릭이기 때문에,

route에 명시하지 않은 주소를 전부 리다이렉트로 빠지게 둔다면, 404.html에서 주소 이동을 한 시점에 그대로 베이스 페이지로 리다이렉트되게 된다.

그러니까 이런 게 있으면 안 된다.

 

후기

옛날 프로젝트 코드 리팩토링 + 깃헙 액션 CI/CD 설정 + 깃헙 페이지 배포, 세 가지 전부 직접 꼭 해보고 싶었던 작업이었는데 이렇게 한번에 해치우게 되어 굉장히 개운하고... 속시원하다.

또 테스트 결과 페이지에 접속할 때마다 나오는 404 에러 때문에 고뇌의 시간을 보내고 있을 때,

과거의 내가 썼던 글에서 안내한 CRA 배포 공식문서 덕분에 구현을 제대로 할 수 있었다.

이 경험을 블로그로 써야겠다고 결심한 것도 이 때문이다. 역시 기록해 두면 나중에 꼭 볼 일이 생기는 것 같다.

 

 

 

참고 링크

https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobs

https://solo5star.tistory.com/36

https://create-react-app.dev/docs/deployment/

https://github.com/rafgraph/spa-github-pages