[side-project] eslint, prettier, husky

yongkini ·2024년 1월 17일
2

Side Project

목록 보기
1/1
post-thumbnail

프로젝트 시작 전에 코드 포매팅 그리고 일관성 유지를 위한 초기 설정에 관하여

Eslint + Prettier

  • ESLint는 여러 코드 작성법 중 일관성 있는 방식으로 구현할 수 있도록 도와주는 도구이고 Prettier는 코드 구현 방식보다 줄바꿈, 공백, 들여쓰기 등 텍스트가 일관되게 작성되는 것에 특화되어 있다.

  • airbnb 세팅으로 적용한다 ⇒ 가장 무난하고, 많은 회사에서 사용하고 있으므로 쓴다. 몇 개 나에게 필요없는건 따로 rules를 추가할 수 있으니까 상관 없을 것 같다.

      ```json
      {
        "extends": ["airbnb", "airbnb/hooks", "airbnb-typescript"],
        "parserOptions": {
          "project": "./tsconfig.json"
        },
        "rules": {
           "react/react-in-jsx-scope": "off"
        }
      }
      ```
    
      ** "react/react-in-jsx-scope” 이걸 쓰고 있어서 off 로 해놨다. 
  • eslint는 code actions on save가 디폴트가 아니다. 즉, prettier 처럼 code를 저장할 때마다 저절로 eslint rule들을 검사하지 않는다(prettier도 디폴트는 아니고 ide에서 설정을 해줘야함). 따라서

      ```json
      "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
      }
      ```
    
      vscode로 치면 settings.json에서 위의 설정을 추가해주면 코드를 수정할 때마다 동작한다. 
  • Prettier와 ESLint는 중복되는 역할이 존재하기 때문에 충돌방지를 위한 패키지를 설치해준다.
    - 레퍼런스 : https://velog.io/@didtjdgns852/협업-필수-ESLint-Prettier로-airbnb-스타일로-코딩하기react-eslint-config-airbnb-typescript
    - eslint-config-prettier
    : 불필요하거나 Prettier와 충돌이(중복이) 일어나는 모든 ESLint의 rules를 무시(turns off)합니다. 다시 말해 중복으로 검사하는 항목에있어서 문법은 ESLint가 포맷팅은 Prettier가 검사하고 실행하게 만들어 준다.
    - 이게 원래는 https://velog.io/@_jouz_ryul/ESLint-Prettier-Airbnb-Style-Guide로-설정하기 이 블로그에서 말하는대로 3 단계를 거쳐야 eslint의 문법 검사와 prettier의 포맷팅 기능을 한번에(eslint의 포맷팅 기능을 끄고 대신 prettier를 사용하는 개념) 쓸 수 있는데, "plugin:prettier/recommended" 이 설정을 통해 한번에 할 수 있게 된거다.
    - eslint와 prettier는 겹치는 기능이 있다(포매팅 기능). 따라서 둘을 같이 사용하려면 이를 분리해주는 기능이 필요하다 ⇒ eslint-config-prettier

Husky

: 본래 git hooks를 통해 pre-commit or pre-push 시점에 원하는 로직을 실행시키도록 할 수 있다. 하지만, 이렇게 하면 팀 프로젝트를 할 때 모든 사람이 이걸 공유하도록 하려면 직접 git hook 코드를 주는 등의 방법을 써야한다(.git 파일은 github에 올리지 않으므로). 하지만, 이걸 좀 더 편하고, 강제적으로 쓰게하는 방법? = husky

module.exports = {
  // Lint & Prettify TS and JS files
  '**/*.(ts|tsx)': filenames => {
    return [
      `yarn eslint --fix ${filenames.join(' ')}`,
      `yarn prettier --write ${filenames.join(' ')}`,
    ];
  },
};

위에 방법은 eslint, prettier를 commit할 때 실행하도록 강제하는거고, 아래 방법은 master or develop에 push 할 수 없도록하는거다(PR로만 강제하도록)

#!/bin/bash

echo -e "===\n>> 0715yk's Pre-push Hook: Checking branch name ..."

BRANCH="$(git rev-parse --abbrev-ref HEAD)"
PROTECTED_BRANCHES="^(master|develop)"

if [[ "$BRANCH" =~ $PROTECTED_BRANCHES ]]
then
  echo -e "\n🚫 Cannot push to remote $BRANCH branch, please create your own branch and use PR." && exit 1
fi

echo -e ">> Finish checking branch name.\n==="

exit 0

실제로 'git push origin master'을 해보면

>> 0715yk's Pre-push Hook: Checking branch name ...

🚫 Cannot push to remote master branch, please create your own branch and use PR.
error: failed to push some refs to 'https://github.com/0715yk/stackquarry_fe.git'

이렇게 PR을 안올리고 직접 master에 푸시하면 지정된 에러 문구가 뜬다.

사용 방법 정리(Husky)

npm install --save-dev husky

위 명령어를 입력하면 .husky 파일이 만들어진다.

// package.json

    {
      "scripts": {
        "postinstall": "husky install",
        "lint": "eslint src/client/**/*.{jsx,js,tsx,ts}",
    	"precommit": "npm run lint",
      },
	}
npx husky add .husky/pre-commit "npm run lint"

위와 같이 해놓고 pre-commit 파일에 이런식으로 하면

// .husky/pre-commit

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint

git commit 을 할 때 npm run lint를 실행해서 eslint 규칙을 검사하고, 만약에 통과하지 못하면 commit을 할 수 없도록 강제한다. 따라서, 협업을 할 때 팀 모두가 무조건 eslint 룰을 지키도록 강제하고 싶으면 husky를 활용하면 된다.

+a

: 이번에 회사에서 프로젝트 시작전에 eslint, prettier 등을 설정할 일이 있었는데, 사실 이 둘을 당연히 같이 쓰는걸로 나는 생각하고 있었다. 하지만, eslint와 prettier는 상호 보완 관계는 맞지만, 서로를 '필수적으로' 필요로하지는 않는 관계였다. 즉, eslint만 써도 문법 검사 + 포매팅을 할 수 있었다(prettier는 포매팅 기능만 한다). 그래서 회사에서는 eslint만을 사용했다. 그 이유는 prettier를 쓰면 예를 들어(나도 전에 느낀건데), printWidth 를 통해 jsx의 attribute 들을 줄넘김을 할지 말지 결정하는데 그것보다 좀더 디테일한 속성은 없을까? 했었다. 근데 eslint의 속성중에

    'vue/max-attributes-per-line': ['error', {
      singleline: {
        max: 3,
      },
      multiline: {
        max: 1,
      },
    }],

(이건 vuejs 관련 속성이다) 이런 속성이 있는데, 이렇게하면 최대 3개의 attribute를 가진 태그들은 아예 줄넘김을 하지 않고, 만약 3개를 넘어가면 그 때부터 한줄에 하나의 attribute가 오도록 줄넘김을 하는 속성이다. 이런식으로 eslint를 쓰면 좀더 디테일한 포매팅이 가능하다. 하지만, prettier가 대세인 이유는 이런 디테일한 설정에 대한 고민과 러닝커브를 줄여주는 '심플함'에 있을 것이기에 둘의 장단점은 꽤나 명확한 편이다. 어쨌든, 나는 여태까지 prettier를 eslint와 무조건 같이 써왔지만, 이런 방법도 있고, 이게 초반엔 좀 힘들지만 나중에는 좀 더 디테일한 커스텀이 가능하다는 측면에서 더 유용할수도 있겠다는 생각도 들었다.

어쨌든 결론적으로 내 회사에서 적용한 세팅은 이런식이다.

  • eslint의 plugin:vue/vue3-strongly-recommended 를 확장으로 받아와서 vue 관련 속성들을 처리한다(체킹한다).
  • plugin:@stylistic/recommended-extends 를 통해 js 관련 속성들을 체킹한다(eslint에서 기본적으로 제공하던 포매팅 관련 속성들은 deprecated 돼 이제는 @stylistic 이걸 따로 써줘야한다고 한다.).
  • 이렇게 적용을 다하고, 커스텀할 부분은 rules에 따로 적어준다.
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-strongly-recommended',
    'plugin:@stylistic/recommended-extends',
  ],
  overrides: [
    {
      env: {
        node: true,
      },
      files: [
        '.eslintrc.{js,cjs}',
      ],
      parserOptions: {
        sourceType: 'script',
      },
    },
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: [
    'vue',
    '@stylistic/js',
  ],
  rules: {
    'no-extra-boolean-cast': 'off',
    'no-prototype-builtins': 'off',
    '@stylistic/semi': ['error', 'always'],
    '@stylistic/multiline-ternary': 'off',
    '@stylistic/arrow-parens': ['error', 'always'],
    '@stylistic/brace-style': ['error', '1tbs'],
    '@stylistic/operator-linebreak': ['error', 'after'],
    'vue/multi-word-component-names': 'off',
    'vue/first-attribute-linebreak': 'error',
    'vue/max-attributes-per-line': ['error', {
      singleline: {
        max: 3,
      },
      multiline: {
        max: 1,
      },
    }],
    'vue/html-indent': ['error', 2, {
      attribute: 1,
      baseIndent: 1,
      closeBracket: 0,
      alignAttributesVertically: true,
      ignores: [],
    }],
    'vue/no-mutating-props': 'off',
    'vue/html-self-closing': ['error', {
      html: {
        void: 'always',
        normal: 'always',
        component: 'always',
      },
      svg: 'always',
      math: 'always',
    }],
    'vue/operator-linebreak': ['error', 'after'],
    'vue/attribute-hyphenation': ['error', 'never'],
    'vue/v-on-event-hyphenation': ['error', 'never'],
    'vue/singleline-html-element-content-newline': ['error', {
      ignoreWhenNoAttributes: true,
      ignoreWhenEmpty: true,
    }],
    'vue/no-dupe-keys': 'off',
    'vue/no-template-shadow': 'off',
  },
};
  • 적용을 마쳤으면, husky를 통해 해당 룰들을 commit 전에(pre-commit) npm run lint를 통해 체킹하게 하고, 통과하지 못하면 commit도 통과하지 못하게 한다.
  • eslint file 명 --fix 옵션을 통해 자동으로 고칠 부분을 고칠수도 있다.

마무리

: 일단 next JS 프로젝트에는 prettier와 eslint를 적용했는데(https://github.com/0715yk/stackquarry_fe), 여기서도 eslint만 쓰는 방향으로 수정해야할 것 같다. 안그래도 원래 printWidth 등의 속성이 너무 추상적이라고 생각했기 때문에 이번에는 좀 더 내 입맛대로 만들어보자.

** 사이드 프로젝트에 적용할 eslintrc 파일

module.exports = {
  "extends": [
    "airbnb",
    "airbnb/hooks",
    "airbnb-typescript",
    "plugin:@typescript-eslint/recommended"
  ],
  "parserOptions": {
    "project": "./tsconfig.json"
  },
  "rules": {
    "react/react-in-jsx-scope": "off",
    "react/jsx-first-prop-new-line": [2, "multiline"],
    "react/jsx-max-props-per-line": [2, { "maximum": { "single": 3, "multi": 1 } }],
    "react/jsx-indent": ["error", 2, {
      checkAttributes: true,
      indentLogicalExpressions: true,
    }],
    "operator-linebreak": "off",

  },
  "ignorePatterns": ['.eslintrc.js'],
}

일단 여기서는 plugin:@typescript-eslint/recommended 이걸 쓰기 때문에 굳이 @stylistic 을 설치해서 쓰지 않는다. 일단 기본적으로 제공하는 것들이 충분히 좋기 때문에 추가 rules는 위에서 보는 정도로 하고, 써보면서 아쉬운 부분들을 보완하도록 한다.

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

1개의 댓글

comment-user-thumbnail
2024년 2월 3일

오.. printWidth 설정 다음에 참고해볼게요.

저도 팀내에서 저 부분이 이슈가 있었던 부분인데 감사합니다!

답글 달기