Github Actions를 이용한 CI 구축하기(Prettier, ESLint, TSC)

sjoleee·2023년 1월 14일
5
post-thumbnail

왜 CI?

🧐 CI란, 소프트웨어 공학에서 지속적 통합(continuous integration, CI)은 지속적으로 품질 관리(Quality Control)를 적용하는 프로세스를 실행하는 것이다.
(위키피디아)

예전에 진행했던 프로젝트에서, 오류있는 코드를 그대로 올려버리는 바람에 크게 문제가 된 적이 있었다.

1차적으로 문제없는 코드를 작성해야 하고,
2차적으로 문제가 있나 없나 잘 확인해야 할테지만...
사람이라는게 완벽할 수가 없다.

따라서 우리는 CI, 지속적 통합을 통해 신뢰할 수 있는 코드를 공유해야한다.
(그렇지 않으면 대체 어디서부터 문제였는지, 뭐가 문제인지 커밋 기록을 다 뜯어보면서 고쳐야한다...)
(특히 저 스샷의 경우에는 코드가 잘못되었다기 보다는 파일의 대소문자를 수정하면서 생긴 오류였다. 코드로는 찾아낼 수가 없었다...)

요즘 잘 활용하고 있는 Github Actions를 사용한 CI 세팅을 기록하고자 한다!

😅 본 글은 왕왕초보 버전임을 밝힙니다!!

1️⃣ ESLint

프로젝트를 처음 시작한다고 가정하자.
CNA으로 Next + ts 프로젝트를 시작하고 나면, 나는 가장 먼저 ESLint를 세팅한다.

(CNA로 프로젝트를 구성할 경우, ESLint 사용할거냐고 물어보는 절차가 있을 것이다.
YES를 입력하면 ESLint를 별도로 설치할 필요는 없다.)

npm i --save-dev eslint eslint-config-next @typescript-eslint/eslint-plugin @typescript-eslint/parser

ESLint를 설치한 후, .eslintrc에서 규칙을 작성하자.

//.eslintrc

{
  "parser": "@typescript-eslint/parser",
  "extends": ["eslint:recommended", "next", "next/core-web-vitals","prettier", "plugin:@typescript-eslint/recommended"],
  "rules": {
    "prefer-const": "warn",
    "no-undef": "off",
    "import/order": [
      "warn",
      {
        "groups": ["builtin", "external", ["parent", "sibling"], "index"],
        "newlines-between": "always"
      }
    ],
    "@typescript-eslint/no-unnecessary-type-constraint": "off",
    "@typescript-eslint/no-explicit-any": "warn",
    "@next/next/no-img-element": "off",
    "@typescript-eslint/no-empty-interface": "off"
  }
}

나는 AirBnB의 ESLint룰을 좋아하지 않는다.
너무 빡빡하다 ㅠㅠ
recommended로 세팅하고, 필요한 룰을 그때 추가하는 방식으로 사용하면 편하다.

링크에서 체크되어있는 룰들이 recommended에 적용되어 있다.
recommended룰 확인하기

개인적으로 추가해서 좋았던 룰은 import문의 순서를 정하는 import/order 옵션이었다.
훨씬 가독성이 높아지는 기분이 든다!

2️⃣ Prettier

ESLint와 Prettier의 차이를 알고 있는가?
둘 다 Formatter인데, 우리는 왜 둘을 함께 사용하고 있는 걸까?

ESLint는 코드의 품질 향상이 목적,
Prettier는 코드의 가독성(예쁘게 정돈)이 목적이라고 할 수 있다.

따라서 "스프레드 문법 쓰지마" 같은 코드 품질 관련된 역할은 ESLint가 담당하고,
"쌍따옴표를 쓸거냐?", "세미콜론 붙일거냐?" 같은 정돈하는 역할은 Prettier가 담당한다.

이렇게 두 Formatter를 다른 목적으로 사용하지만, 겹치는 룰이 있으면 문제가 될 수 있다.
ESLint는 A로 고치고, Prettier는 B로 고치고, ESLint는 A로 고치고, Prettier는 B로 ...

따라서 우리는 ESLint에게 '너, Prettier 형아 말 들어!'라고 설정해줄 필요가 있다.

npm i --save-dev prettier eslint-config-prettier eslint-plugin-prettier

eslint-config-prettier와 eslint-plugin-prettier을 통해 Prettier를 우선할 수 있다.

//.prettierrc

{
  "singleQuote": true,
  "semi": true,
  "useTabs": false,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100,
  "arrowParens": "always"
}

위 Prettier 룰은 지극히 개인적인 취향이다.
나는 큰따옴표가 뭔가 든든~하게 생겨서 좋아한다.
큰 이유는 없다.
작은따옴표를 더 많이 선호하시는 것 같긴 하다... 왜일까?
백틱이랑 구분도 잘되고 좋은뎅...

3️⃣ Github Actions

우리가 구성할 CI는

  • Prettier
  • ESLint
  • TSC

순서로 진행될 것이다.

Prettier, ESLint 다음에 나오는 TSC는 타입스크립트 컴파일러다.
즉, 타입스크립트를 자바스크립트(es5)로 변환하는 과정에서 에러가 있나 검사하는 과정이다.

이제 위 세가지 작업을 자동으로 수행하도록 세팅해보자.

레포지토리의 Actions 탭에서 설정할 수 있다.
하지만, IDE에서 바로 작성해도 무관하다.

// .github/workflows/node.js.yml

name: sjoleee CI

on: [ push ]

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [ 16.x, 18.x ]

    steps:
      - uses: actions/checkout@v3
      
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'yarn'

      - name: Get yarn cache directory path
        id: yarn-cache-dir-path
        run: echo "dir=$(yarn cache dir)" >>$GITHUB_OUTPUT

      - uses: actions/cache@v3
        id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      - name: Install the project dependencies
        run: yarn install

      - name: Prettier check
        run: yarn prettier

      - name: Lint check
        run: yarn lint

      - name: TS check
        run: yarn tsc

뭔가 길다.
쭉 살펴보면 어떤 환경에서 어떤 절차를 거치며 앱을 빌드할지 정의해놓은 것이다.

먼저, node-version은 현재 lts인 18버전, 그리고 16버전으로 설정했다.
이렇게 해두면 버전별로 CI를 수행하여 버전에 따른 이상이 없는지 검증할 수 있다.

steps에서 어떤 절차를 거칠지 정의하는데,
uses 항목에서 actions/checkout@v3 actions/setup-node@v3 등이 보인다.

이것들은 누군가 미리 만들어놓은 액션인데, 깃허브 마켓플레이스에서 검색하여 사용할 수 있다.

절차 중에 actions/cache@v3를 사용하는 캐싱 절차가 있는데, 이것에 대한 설명은 좋은 글이 있어서 대체하고자 한다.
https://thearchivelog.dev/article/caching-dependencies-to-speed-up-workflows/

노드를 설치하고, 의존성을 설치하고...
그리고 나서 Prettier, Lint, TS 체크 과정이 보인다.
그런데, run: yarn prettier ...? 이런 스크립트가 있었나?

없었다. 작성해줘야 제대로 동작한다.

// package.json

{
...
  
  "scripts": {
	...
    
    "lint": "next lint",
    "prettier": "prettier --write **/*.{ts,tsx}",
    "tsc": "tsc"
  },

...
}

이렇게 체크를 위한 스크립트까지 작성하고 나면 완성!

4️⃣ Branch protection rules

이렇게 구축한 CI, 통과해야지만 머지할 수 있도록 설정해보자.

레포지토리 > Settings > Branches 로 진입하면 Branch protection rules을 설정할 수 있다.

한 명의 approve가 있어야 머지할 수 있다는 규칙을 추가했다.

노드 16, 18버전 체크가 통과해야 머지할 수 있다는 규칙을 추가했다.

결과

이제 협업하는 개발자가 여럿이더라도 코드 품질을 보장받으며 협업할 수 있게 되었다.

사용하면서도 긴가민가... 이렇게 쓰는게 맞나... 했던 부분들이 있었는데, 글을 작성하며 많이 공부할 수 있었다.
테스트코드도 작성해서 추가하면 좋을 것 같은데, 공부해서 다음 개인프로젝트에는 반영해봐야겠다!

profile
상조의 개발일지

0개의 댓글