10분 만에 기반 지식 없이 Node.js를 위한 Github Actions CI 구축하기

목표

  • 10분 만에 Github Actions를 통한 CI를 구축한다.
  • master에 Merge 시에 ESLint와 테스트를 자동으로 수행하고, 결과에 따라 Merge할 수 없게 한다.

1. 기본 개념 설명

기본 개념 몇 가지를 소개하고 넘어간다.

1. CI

Continuous Integration. 각자의 코드를 병합하기 전에 검토하는 절차를 말한다.

보통 자동화된 상태를 지칭하며, master(or main) 등의 특정 브랜치에 Push(or Merge) Request가 올라오면 코드를 검토한다.

2. GitHub Actions

GitHub에서 특정 작업을 할 때 마다, 이벤트를 발생시키는데, 이를 구독해 특정 작업을 실행하는 것을 Github Actions라고 한다. (옵저버 패턴 참고)

3. Worflow File

GitHub Actions의 이벤트에 대해 무엇을 실행할 지에 대해 기록해 놓은 명령서를 workflow 파일이라고 한다.

4. Github Actions 사용 시의 CI 흐름

PR Created(EVENT!) > Build > Test > PR Merged(EVENT!) > Deploy (배포 자동화는 다음에)

  • PR을 생성할 때 CI 수행
  • Merge할 때 CD 수행

CI 과정에서 빌드가 성공했을 때만 Merge가 가능하게 설정하자.

2. Node.js App 으로 CI 구축 시작

이 챕터에서 구축을 완료하고, 결과를 확인한다.

아주 간단한 과정이어서 CI라고 하긴 부끄럽지만, 아래 과정을 수행한다.

  • npm module 설치
  • ESLint를 통한 코드 스타일 체크
  • 테스트 실행

1. 필요한 자료

  • ESLint가 설치된, 스택에 상관 없는 Node.js 샘플 앱

  • 샘플 앱을 올린 Public Repo가 필요하다.

  • (이 글에서 코드를 따로 제공하지는 않는다.)

  • 글쓴이는 토이 작업 중인 이 레포를 활용하였다.

글에서 Jest를 설치하고, ESLint와 연동할 것이다.


2. Node.js 템플릿 가져와서 사용하기

아래는 레포지토리에서 Actions 탭을 눌러, Get started with GitHub Actions 아래에 있는 Node.js 템플릿을 가져온 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
name: Node.js CI

# 구독할 이벤트
on:
push:
branches: [master]
pull_request:
branches: [master]

# jobs 단위로 개별 서버(정확히는 Docker 컨테이너 단위라고 한다.)에서 작업이 수행된다.
# 각 작업은 병렬로 실행 된다고 하는데, needs: build와 같이 표시해서 기다릴 수도 있다.
jobs:
build:
# Ubuntu, Windows, MacOS를 지원한다.
runs-on: ubuntu-latest

# 영상에서도 소개됐는데, 변수 개념으로 생각하면 된다.
# node-version 과 같이 배열로 돼있으면, 해당 원소를 순회하면서 작업이 반복해서 실행된다.
# matrix 때문인지 배열만 되는 것 같다. (TODO)
# 응용해서 runs-on에 여러 OS에서 돌릴 수도 있다.
strategy:
matrix:
node-version: [14.x] # 템플릿 기본값: [10.x, 12.x, 14.x]

# uses 개념은 다른 사람이 작성한 내용을 실행하는 개념이다.
# actions/checkout: GitHub의 마지막 커밋으로 Checkout 한다.
# actions/setup-node: Node.js를 설치한다.
# run 개념은 명령어를 실행한다. 셸 스크립트와 동일하다.
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
# npm ci는 npm install과 같은 기능을 수행한다. 자세한 내용은 아래 링크 참조.
- run: npm ci
# --if-present 옵션은 npm 스크립트가 존재할 때만 실행시키라는 의미이다.
# 만약 build 스크립트가 없는 경우, 오류 없이 지나간다.
- run: npm run build --if-present
- run: npm test

npm ci에 대한 스택 오버 플로우 설명 (속도가 2배 가량 빠르다고 한다. 캐싱에 대한 내용도 있으면 좋겠다)

–if-present 옵션에 대한 스택 오버 플로우 설명

GitHub Actions Checkout ?


프로젝트에 이 파일을 .github/workflows/ci.yml로 저장한다.

이후 Push 하면 Actions 탭을 눌렀을 때 해당 빌드 과정이 수행됨을 볼 수 있다.

단, 아직 설정이 다 끝나지 않았으므로, 이 파일의 구조만 확인하기 바란다.

2. 기본 제공 Workflow에 Lint, Test 추가

Lint와 Test 과정을 추가한다. 각 과정은 실패 없이 진행돼야 빌드가 성공한다.

Lint 과정에선 error로 설정된 Rule을 위반한 경우 빌드가 실패하게 된다.


1. Lint: node_modules 에 있는 ESLint를 수행하는 스크립트가 필요하다. 아래 내용을 추가하자.

1
2
3
4
5
//...
"scripts": {
//...
"lint": "./node_modules/.bin/eslint ."
},

ci.yaml 파일에 npm run lint를 추가하자.

1
2
3
4
5
steps:
# ...
- run: npm run build --if-present
- run: npm run lint
- run: npm test

2. Test: Jest를 설치하고, 아래 내용을 추가하자.

폴더를 /tests로 설정했는데, 굳이 그럴 필요가 없다면 생략해도 된다.

Test가 하나라도 실패하면 당연히 빌드는 실패하게 된다.

1
2
3
4
5
//...
"scripts": {
//...
"test": "./node_modules/.bin/jest --verbose ./tests"
},

Jest 폴더 설정 스택 오버플로우 설명


Jest는 글로벌로 API를 expose하기 때문에 ESLint error가 나지 않으려면 플러그인을 설치해줘야 한다.

npm i --save-dev eslint-plugin-jest 로 ESLint-Plugin-Jest를 설치한다.

eslintrc.yml 파일에 아래의 내용을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//...
env:
//...
jest: true # Jest 글로벌
plugins:
- jest # Jest 테스트를 위해 플러그인이 필요하다.
rules:
//...
# Jest Eslint 옵션은 0,1,2 (off, warn, error) 만 옵션으로 사용 가능하다.
jest/no-disabled-tests:
- warn
jest/no-focused-tests:
- error
jest/no-identical-title:
- error
jest/prefer-to-have-length:
- warn
jest/valid-expect:
- error

ESLint 설정 스택오버플로우 설명

3. 빌드 성공 전에 Merge Button을 누를 수 없게 하기

GitHub에서 Branch Protection Rule이라는 기능을 제공한다. 레포지토리 > Settings 탭 > Branches 탭 > Branch protection rules 탭 > Add Rule 버튼 클릭 후 아래와 같이 설정하였다.

4. 끝!

  • ci.yml 파일을 Push 하자.
  • Master에 Push하거나 Pull Request를 올리자
  • CI가 동작함을 확인하자.

글쓴이는 아래처럼 잘 동작함을 확인했다.

코드 베이스가 작고, 테스트가 사실상 전무하지만, 그래도 Node 설치부터 실행까지 20초밖에 걸리지 않는다는 점은 신기하고 인상적이다. Public 레포로 작업하면 좋은 성능의 CI를 무료로 사용할 수 있어 좋은 것 같다.


TODO

  1. CD 과정도 구축하기. Docker 레지스트리 배포가 일반적인 듯하다. (쿠버네티스가 사용되는듯)

  2. Jobs에서 build 하나만으로 괜찮은 것 같긴 한데, 나누는 case는 뭐가 있을지 확인해보기

  3. npm run build 명령어로 무엇을 실행할지 고민해보기. Node.js로 프로덕션 배포를 해 본 적이 없어서 뭐가 필요한지 아직 파악하지 못 했다.

  4. GitHub Actions에 대해 이론적으로 더 공부해보고, 할 수 있는 것들 더 많이 배우기

  5. Git Hooks라는 개념도 있다고 한다. 로컬 수준에서도 프로세스를 자동화할 수 있는 것 같은데, 한 번 알아봐야겠다.


기타 내용 정리

Why is it free?

public은 무료, private은 사용량 만큼 낸다고 한다.

왜 무료일지 확인해봤는데, 출처에 따르면 Open Source 프로젝트 지원이라는 명목이다.

We want every open source project to be productive and use best practices, so Actions is free for the 40 million developers on GitHub to use with public repositories. For private repositories, Actions offers simple, pay-as-you-go pricing. (…)

Supported OS

위에서 언급했듯, Ubuntu, Windows, MacOS 이다. Docker 컨테이너로 작동한다고 하며, 매 번 Fresh한 Docker Container가 제공된다고 한다.

계기가 된 Video

참고한 유튜브 비디오. 간단하게 Github Actions이 뭔지 영상을 보기만 해도 파악이 가능하다.

https://www.youtube.com/watch?v=R8_veQiYBjI&ab_channel=TechWorldwithNana

글쓴이는 도커 기본 개념과 컨테이너 개념에 조금 익숙한 상태로 봐서 쉽다고 느꼈지만, 정말 아무것도 모른다면 조금 어려울 수도 있다. 영상에서도 언급했듯 Github Actions의 설정 파일은 Docker와 비슷하다.