Github Actions로 CI/CD 환경 구축하기

정다빈·2023년 7월 31일
1
post-thumbnail

키즈노트 FE개발파트는 몇 달 전부터 젠킨스로 구축되어 있던 CI/CD 환경을 Github Actions로 마이그레이션 해왔어요.

젠킨스는 좋은 도구이지만, 실무에서 사용해 보니 전체적인 관리가 조금 어렵다고 느껴졌어요. 젠킨스의 다양한 기능들을 사용하기 위해 그루비를 학습해야 하고, 데브옵스 담당자분들에게 많이 의지해야 했어요.

"도와주세요 @데브옵스" 🥺

이러한 단점들을 보완하고자, Github Actions를 도입해서 사용 범위를 점진적으로 확대해가고 있습니다.

젠킨스보다 더 쉽게 CI/CD 환경을 만들 수 있다니! 이쯤 되니 Github Actions에 대해 궁금해지는데요, 하지만 저는 회사 레퍼지토리 설정에 대한 권한이 없기 때문에 개인 레퍼지토리에서 CI/CD 환경을 구축해 보도록 하겠습니다.

🏃🏻‍♀️ Github Actions가 뭔가요?

Github Actions는 빌드, 테스트, 배포 파이프라인을 자동화할 수 있는 CI/CD 플랫폼이에요. Github Actions를 이용해서 PR을 빌드 및 테스트하거나, 누군가 생성한 이슈에 라벨을 달아주는 등 다양한 액션을 자동화할 수 있어요.

레퍼지토리에서 특정 이벤트가 발생하면 Github Actions를 이용해서 워크플로우를 트리거 할 수 있어요. 이 워크플로우는 하나 이상의 job으로 구성되어 있는데요, 이 job은 Github에서 제공하는 가상 머신 러너에서 실행됩니다. 만약 특별한 설정이 필요한 서버에서 job을 실행하고 싶다면 자체 호스팅도 가능해요.

좀 더 자세한 내용은 공식 문서에서 확인할 수 있어요!

🛠️ CI/CD 환경을 만들어볼까요?

제가 만들고 싶은 CI/CD 환경은 아래와 같아요.

  1. main 브랜치로 머지 하는 PR이 생성되면 빌드 및 테스트를 실행해요.
    • 실무에서는 Git Flow를 사용하고 있기 때문에 많은 브랜치 보호 규칙이 필요하지만, 실습에서는 main 브랜치만 설정해 줄게요.
    • PR의 Merge pull request 버튼을 기본적으로 비활성화하고, 빌드 및 테스트가 모두 성공할 경우 버튼을 활성화해줄게요.
  2. main 브랜치로 머지가 완료되면 Netlify를 이용해서 자동으로 배포해요.

PR이 생성될 때 테스트하고 싶은 것들은 아래와 같아요.

  1. Install Dependencies : 패키지들이 정상적으로 설치되는지 확인해요.
  2. Lint : 코드에 문법 오류나 안티 패턴들이 있는지 확인해요.
  3. Test : 테스트 코드를 모두 통과하는지 확인해요.
  4. Build : 빌드가 정상적으로 실행되는지 확인해요.

1. 리액트 어플리케이션 만들기

Create React App을 이용해서 간단한 리액트 어플리케이션을 만들어줄게요.

2. PR 워크플로우 만들기

루트 디렉토리에서 .github/workflows 디렉토리를 만들고 하위에 pull-request.yml 파일을 생성해 줄게요. pull-request 워크플로우는 PR이 생성될 때 트리거 해줄 거예요.

name: Pull Request
on:
  pull_request:
    types: [opened, synchronize]

PR이 생성(opened) 되거나 코드가 업데이트(synchronize) 된 경우 트리거 되도록 이벤트를 지정해 주었어요.

Install Dependencies job 추가하기

PR 워크플로우에서 실행될 job들을 지정해 줄 거예요. 가장 먼저 패키지들을 정상적으로 설치할 수 있는지 테스트하기 위해 install-dependencies job을 추가해 줄게요.

jobs:
  install-dependencies:
    name: Install Dependencies
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: '14'

      - name: Install Dependencies
        run: npm install

npm install 명령어를 실행하도록 설정해 주었어요.

Lint job 추가하기

문법이나 컨벤션에 어긋나는 코드가 없는지 검사하기 위해 lint job을 아래에 추가해 줄게요.

lint:
  name: Lint
  needs: [install-dependencies]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3

    - uses: actions/setup-node@v3
      with:
        node-version: '14'

    - name: Install Dependencies
      run: npm install

    - name: Run Lint
      run: npm run lint

이번에는 needs 옵션을 추가해 주었어요. 패키지들이 정상적으로 설치되지 않으면 린트 검사도 실행할 수 없기 때문에, install-dependencies job이 성공적으로 실행되면 lint job을 실행시켜줄 거예요.

그런데, 이전에 실행되는 install-dependencies job에서 패키지들을 설치해 주었는데 왜 lint job에서 또 다시 패키지들을 설치해 주어야 할까요?
각각의 job들은 서로 다른 가상 머신 러너에서 병렬적으로 실행되기 때문에, ESLint를 실행하기 위해서는 다른 job과 관계없이 패키지를 설치해 주어야 합니다.

마지막에 npm run lint 명령어를 추가해서 린트 검사를 실행하도록 설정해 주었어요.
앗! 그런데 ESLint 설정을 하지 않아서 lint job 테스트를 할 수 없군요! ESLint 설정을 추가해서 airbnb 규칙을 적용해 줄게요.

ESLint에 airbnb 규칙 적용하기

아래와 같이 package.jsondevDependencies에 ESLint 패키지들을 추가해 줍니다.

"devDependencies": {
  "eslint": "^8.45.0",
  "eslint-config-airbnb": "^19.0.4",
  "eslint-plugin-import": "^2.27.5",
  "eslint-plugin-jsx-a11y": "^6.7.1",
  "eslint-plugin-react": "^7.33.0"
}

그리고 루트 디렉토리에 .eslintrc.json 파일을 생성해 줄게요.

{
  "env": {
    "browser": true,
    "es2021": true,
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "airbnb" // airbnb 규칙 추가
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": [
    "@typescript-eslint",
    "react"
  ],
  "rules": {},
  "settings": {}
}

이제 PR 워크플로우에 추가한 job들이 정상적으로 실행되는지 확인해 볼게요. develop 브랜치를 생성해서 사용하지 않는 변수를 추가해 줍니다.

그리고 maindevelop으로 머지 하는 PR을 생성해 줄게요.

의도한 대로 lint job에서 실패했네요! Details 버튼을 눌러서 확인해 볼까요?

notUsed 변수에서 린트 에러가 발생한 것을 확인할 수 있어요.

브랜치 보호 규칙 설정하기

그런데 PR을 살펴보면 lint job에서 실패했는데도 머지 버튼이 활성화되어 있어요.

레퍼지토리에서 브랜치 보호 규칙을 설정하지 않았기 때문에 머지가 가능한 상태인데요, 브랜치 보호 규칙을 추가해서 모든 job이 성공해야 머지 버튼이 활성화되도록 설정해 줄게요.

Github 레퍼지토리에서 Settings > Branches 메뉴로 이동하면 현재 브랜치 보호 규칙이 없는 것을 확인할 수 있어요. 여기서 add for branch protect rule 버튼을 클릭해서 규칙을 추가해 줍니다.

실습에서는 main, develop 브랜치만 사용할 예정이기 때문에 main 브랜치를 보호해 주도록 할게요.

아래 체크박스를 활성화해서 install-dependencies, lint job이 통과하면 머지가 가능하도록 지정해 주었어요.

하단에 Create 버튼을 클릭하고 다시 PR로 돌아오면 머지 버튼이 비활성화된 것을 확인할 수 있어요.

Test job 추가하기

test job을 아래와 같이 추가해 줄게요.

test:
  name: Test
  needs: [install-dependencies]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3

    - uses: actions/setup-node@v3
      with:
        node-version: '14'

    - name: Install Dependencies
      run: npm install

    - name: Run Test
      run: npm run test

lint job과 마찬가지로 need 옵션과 npm install 명령어를 넣어주고, 브랜치 보호 규칙에서 test job도 통과해야 머지가 가능하도록 수정해 주었어요.

테스트는 CRA 기본 설정에 포함되어 있기 때문에 별도의 테스트 설정은 하지 않을게요.

테스트를 위해 App.test.tsx 파일을 수정해 줍니다.

develop 브랜치를 push 하면 코드가 업데이트되었기 때문에 PR 워크플로우가 트리거 된 것을 확인할 수 있어요.

어떤 테스트 파일에서 왜 실패했는지 자세히 알려줍니다.

Build job 추가하기

마지막으로 빌드를 체크하는 job을 추가해 줄게요.

build:
  name: Build
  needs: [install-dependencies]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3

    - uses: actions/setup-node@v3
      with:
        node-version: '14'

    - name: Install Dependencies
      run: npm install

    - name: Run Build
      run: npm run build

모든 job이 정상적으로 실행되는 것을 확인할 수 있어요.

3. Deploy 워크플로우 만들기

Deploy 워크플로우를 만들기 전에!!! 어떻게 배포할 것인지 결정해서 배포를 해봐야겠죠? 이번 실습에서 만든 리액트 어플리케이션은 아주 작은 규모이기 때문에 Netlify로 배포해 줄 거예요.

Netlify에 가입할 때 Github 계정을 연동하면 레퍼지토리 목록을 확인할 수 있어요.

레퍼지토리를 선택하면 곧바로 배포가 시작됩니다.

빠르게 배포에 성공했네요!

이제 .github/workflows 디렉토리 하위에 deploy-production.yml 파일을 생성해 줄게요. deploy-production 워크플로우는 PR이 main 브랜치에 머지 되면서 main 브랜치에 새로운 커밋이 생겼을 때 트리거 해줄 거예요.

name: Deploy to Production
on:
  push:
    branches:
      - 'main'

Deploy job 추가하기

배포 자동화를 구현하기 위해 deploy job을 추가해 줄게요.

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: '14'

      - name: Install Dependencies
        run: npm install

      - name: Run Build
        run: npm run build

      - name: Deploy to Netlify
        uses: nwtgck/actions-netlify@v2.0
        with:
          publish-dir: './build'
          production-deploy: true
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
        timeout-minutes: 1

배포 전에 npm install, npm run build 명령어를 실행해서 빌드 파일을 생성해 줄 거예요. 이렇게 만들어진 빌드 파일은 Netlify를 이용해서 배포해 줄 것이기 때문에, 마켓 플레이스에서 다운로드 수가 가장 많은 netlify-actions를 가져왔어요.

netlify-actions를 사용하기 위해서 아래 세 가지를 필수로 입력해 주어야 합니다.

  • publish-dir : 빌드 결과물이 저장될 디렉토리 이름
  • NETLIFY_AUTH_TOKEN : Netlify에서 발급한 액세스 토큰
  • NETLIFY_SITE_ID : 배포할 사이트 아이디

액세스 토큰은 User Settings > Applications > OAuth 메뉴에서 생성할 수 있어요.

액세스 토큰에 대한 간략한 설명을 적고 Generate token 버튼을 클릭하면 액세스 토큰을 생성할 수 있어요. 액세스 토큰은 보안 상 외부 노출에 유의해야 합니다!

사이트 아이디는 Site configuration > General > Site details 메뉴에서 확인할 수 있어요.

이렇게 가져온 액세스 토큰과 사이트 아이디를 Github에 저장해 줄게요. 레퍼지토리에서 Settings > Security > Secrets and variables > Actions 메뉴로 이동합니다. New repository secret 버튼을 클릭해서 액세스 토큰과 사이트 아이디를 저장합니다.

저장 후 아래와 같이 Repository secrets에서 목록을 확인할 수 있어요. 사이트 아이디는 외부에 노출되어도 문제없지만 원활한 변수 관리를 위해 액세스 토큰과 같은 위치에 저장해 주었습니다.

이제 배포 테스트를 해볼까요? develop 브랜치에서 App.tsx 파일을 수정해 줄게요.

netlify-actions의 enable-pull-request-comment 옵션 기본값이 true로 설정되어 있기 때문에 아래와 같이 코멘트를 확인할 수 있어요. 최신 커밋, 배포 로그, 프리뷰 정보를 제공해 주고 있군요!

모든 job들이 성공적으로 실행되었으니, 머지를 해보도록 하겠습니다!

머지 후 사이트로 이동하면 정상적으로 배포된 것을 확인할 수 있어요.

👏🏻 마무리

사실 개인적으로 젠킨스를 이용해서 CI/CD 환경 구축을 도전해 본 적이 있는데요, 생각보다 높은 진입 장벽에 막혀서 쩔쩔맸던 기억이 있어요. 그런데 Github Actions는 공식 문서도 친절하고 마켓 플레이스에서 제공하는 다양한 기능들 덕분에 쉽게 CI/CD 환경을 만들 수 있었습니다.
앞으로 개인 프로젝트를 진행할 때 Github Actions를 적극적으로 사용해야겠네요!

실습에 사용된 코드는 여기서 확인할 수 있습니다.

📚 Reference

profile
Frontend Developer

0개의 댓글