ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React 웹 애플리케이션 Github에 배포하기
    Today I Learned 2022. 10. 1. 22:00

     

    BrowserRouter를 이용하여 Single Page 내에서 주소를 전환하는 React 웹 애플리케이션을 Github에 배포하기 위해 프로젝트에 추가해야 하는 내용을 정리했다. 다음의 내용은 bundler로 parcel을 사용하는 경우에 한함을 밝힌다.

     

    1. 루트 디렉터리에 .github/workflows/ci.yml 을 생성하고 다음의 내용을 추가한다.

    name: CI
    
    on:
      push:
        branches:
          - main
      pull_request:
          types: [opened, synchronize, reopened]
    
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout
            uses: actions/checkout@v3
          - name: Set up Node.js
            uses: actions/setup-node@v3
            with:
              node-version: '16'
              cache: npm
          - name: Install dependencies
            run: npm ci
          - name: Build
            run: npm run build
          - name: Deploy
            uses: peaceiris/actions-gh-pages@v3
            with:
              github_token: ${{ secrets.GITHUB_TOKEN }}
              publish_dir: ./dist
              keep_files: true

    ci.yml이 github에서 동작되는 원리를 추측해보았다.

     

    - push될 경우에는 main branch에 있을 때, pull request되었을 때에는 opened되거나, synchronized되거나, reopen되는 경우에 job:의 내용을 실행

    - 동작은 가장 최신 버전의 ubuntu에서 이루어짐

    - steps:의 단계를 순차적으로 수행

        - name: 다음 줄의 동작을 설명 (주석의 역할과 비슷한 듯)

        - uses:, run: 해당 동작을 수행하게 하는 듯 (둘의 차이는 무엇일까?)

        - with: 동작을 수행하는 데 필요한 의존성이나 추가적인 정보를 부여하는 듯

    - 해당 동작을 수행하는 결과로 build의 결과물로 생성되는 dist 디렉터리와 그 하위 파일들을 github repository에서 gh-pages라는 이름의 브랜치에 생성하는 것을 확인할 수 있었다.

     

     

    cf. yml이란 어떤 것인지 궁금하여 간단히 찾아보았다. (yaml)

    - 마크업 언어의 일종

    - 설정 데이터들을 계층 구조로 나타내므로 동일한 구성이 중복되는 것을 방지할 수 있음, 따라서 가독성이 properties보다 향상될 수 있음

     

    Yaml, Yml 이란?

    트렌드 변화 최근 들어서 Yaml, Yml이라는 포맷이 자주 활용되는 것을 보고 있으면, 개발의 트렌드가 빠르게 변화하는 것을 알 수 있다. 물론 이 트렌드는 개발자의 트렌드하고도 맞닿아 있다. 최

    needjarvis.tistory.com

     

     

    2.  package.json에 다음의 내용을 추가하거나 삭제한다.

    // "main": "index.js", 가 존재할 경우 삭제
    
    "scripts": {
        "lint": "eslint --ext .js,.jsx,.ts,.tsx",
        
        "build": "parcel build index.html --public-url ./ && cp 404.html ./dist",
    },

    - "build": ci.yml에서 npm run build를 수행할 때 해당 명령어를 수행하는 것으로 보인다.

     

     

    3. index.html의 <body> 요소를 다음의 내용으로 수정한다.

    <body>
      <div id="app">
        Loading...
      </div>
      <script>
        (function(l) {
        if (l.search[1] === '/') {
          var decoded = l.search.slice(1).split('&').map(function(s) { 
            return s.replace(/~and~/g, '&')
          }).join('?');
          window.history.replaceState(null, null,
              l.pathname.slice(0, -1) + decoded + l.hash
          );
        }
      }(window.location))
      </script>
      <script type="module" src="./src/index.jsx"></script>
    </body>

    새로 추가된 function script가 어떤 원리로 작동되는지에 대한 분석이 필요할 것으로 생각된다.

     

     

    4. 루트 디렉터리에 404.html을 생성하고 다음의 내용을 추가한다.

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Single Page Apps for GitHub Pages</title>
        <script type="text/javascript">
          var pathSegmentsToKeep = 1;
    
          var l = window.location;
          l.replace(
            l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
            l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
            l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
            (l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
            l.hash
          );
        </script>
      </head>
      <body>
      </body>
    </html>

    해당 파일은 "build" 명령 수행 시 정의한 내용에 따라 dist 디렉터리 내에 자동으로 복사된다. 배포된 웹 페이지의 location의 명칭을 수정해주는 동작으로 추측되나, 자세한 동작 분석이 필요할 것으로 생각된다.

     

     

    5. index.jsx의 <App /> 요소를 둘러싸고 있는 <BrowerRouter> 요소에 다음의 props를 추가한다.

    import ReactDOM from 'react-dom/client';
    
    import { BrowserRouter } from 'react-router-dom';
    
    import App from './App';;
    
    const container = document.getElementById('app');
    const root = ReactDOM.createRoot(container);
    
    root.render((
      <BrowserRouter basename='/깃헙 레포지토리명'>
        <App />
      </BrowserRouter>
    ));

     

     

    해당 과정들을 수행한 뒤, github repository의 main branch에 프로젝트를 직접 push하거나, pull request에 commit을 쌓을 경우 Actions에서 배포 과정을 진행한다.

     

     

    사실 이렇게 배포 방법을 기록하는 것보다 더 중요한 것은...

    오늘도 동료분을 직접 만나서 주말 일과를 진행했다. 금요일 동안 트레이너님과 고통의 트러블슈팅을 통해 배포 과정을 익힌 동료분으로부터 배포 과정을 전수받으면서 배포 방법을 알고자 하는 것이 목적이었다.

     

    그러나 트레이너님과 문제를 해결하면서 남겼던 거대한 덩어리 형태의 기록을 다시 인출해내어 동료들에게 다시 설명하는 데에는 큰 어려움을 겪었다. 저녁식사를 하고 나서 동료분이 트러블슈팅 과정에서 겪었던 궁금증을 해결하기 위해 동료분의 주도로 위에서 설정했었던 설정 값들을 조금씩 변경해보면서 새 github repository에 push하는 실험을 함께 진행했다. 예를 들면 ci.yml 파일에서 특정 명령어들이 어떤 역할을 수행하는 것인지 직접 눈으로 확인해보기 위해 어떤 명령어는 빼보고 push를 진행해 Actions에서 build가 어떻게 진행되는지 등을 직접 살펴보고, 결과와 추가적인 궁금한 점 등을 계속해서 기록했다.

     

    동료분은 어제 트러블슈팅 과정에서 '기억보단 기록을' 이라는 말의 소중함을 뼈저리게 느꼈다고 한다. 나 또한 어떤 문제에 부딪혔을 때, 문제 해결 과정을 계속해서 기록해나가면서 한 번에 이해하기 어려운 복잡한 과정이 하나하나 어떻게 수행되는지 경험을 쌓으면서 지식의 범위를 확장시켜 나간다는 마음가짐을 가져야 할 것이다.

     

     

     

    댓글

Designed by Tistory.