๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป dev

[spotify-api] VINYLIFY : ์Šคํฌํ‹ฐํŒŒ์ด apiํ™œ์šฉํ•œ ๊ฒ€์ƒ‰ + ์žฌ์ƒ ํ”„๋กœ์ ํŠธ(4) eslint + prettier + commitlint ์„ค์ • (+husky)

2024.06.12 - [๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป dev] - [spotify-api] VINYLIFY : ์Šคํฌํ‹ฐํŒŒ์ด apiํ™œ์šฉํ•œ ๊ฒ€์ƒ‰ + ์žฌ์ƒ ํ”„๋กœ์ ํŠธ(3) ์ค€๋น„๋ฌผ: authorization์„ ๋‹ด๋‹นํ•  ์„œ๋ฒ„ ์ฝ”๋“œ

 

[spotify-api] VINYLIFY : ์Šคํฌํ‹ฐํŒŒ์ด apiํ™œ์šฉํ•œ ๊ฒ€์ƒ‰ + ์žฌ์ƒ ํ”„๋กœ์ ํŠธ(3) ์ค€๋น„๋ฌผ: authorization์„ ๋‹ด๋‹นํ•  ์„œ

2024.06.12 - [๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป dev] - [spotify-api] VINYLIFY : ์Šคํฌํ‹ฐํŒŒ์ด apiํ™œ์šฉํ•œ ๊ฒ€์ƒ‰ + ์žฌ์ƒ ํ”„๋กœ์ ํŠธ(2) ์ค€๋น„๋ฌผ: authorization์„ ๋‹ด๋‹นํ•  ์„œ๋ฒ„์— ์ด์–ด์„œ [spotify-api] VINYLIFY : ์Šคํฌํ‹ฐํŒŒ์ด apiํ™œ์šฉํ•œ ๊ฒ€์ƒ‰ + ์žฌ์ƒ ํ”„

pyotato-dev.tistory.com

์‚ฌ์šฉํ•  spotify api์— ๋Œ€ํ•œ ์ค€๋น„๊ฐ€ ๋๋‚ฌ์œผ๋‹ˆ ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ํ”„๋กœ์ ํŠธ ์„ค์ •์„ ํ•ด๋ณด์ž.
์˜ค๋Š˜์€ eslint์™€ prettier๊ฐ€ ๊ฐ๊ฐ ๋ญ”์ง€, ์™œ ํ•„์š”ํ•œ์ง€ ๊ทธ๋ฆฌ๊ณ  ์„ค์ • ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž.
๊ทธ๋ฆฌ๊ณ  ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฆฐํŠธ๊ฐ€ ๋˜๋„๋ก commitlint ๋„ ์„ค์ •ํ•ด ์ฃผ์ž. 

โš ๏ธ ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋Š” vite+react+yarn์„ ๊ธฐ์ค€์œผ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค! ๋ชจ๋“  ์„ค์ • ํŒŒ์ผ๋“ค์€ root์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

Prettier

[์ถœ์ฒ˜] https://prettier.io/

๐Ÿค” prettier๊ฐ€ ๋ญ๊ณ  ์™œ ํ•„์š”ํ• ๊นŒ?

prettier์˜ ๊ณต์‹ ํŽ˜์ด์ง€์— ๋“ค์–ด๊ฐ€๋ฉด prettier๋Š” "opinionated" ์ฝ”๋“œ ํฌ๋ฉงํ„ฐ๋ผ๊ณ  ์†Œ๊ฐœํ•œ๋‹ค.

prettier๊ฐ€ opinionated(prettier๊ฐ€ ์ •๋ฆฝํ•œ ์Šคํƒ€์ผ์„ ๊ณ ์ˆ˜ํ•˜๋Š”) ์ด์œ ๋Š” ์ฝ”๋”ฉ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ๋…ผ์Ÿ์„ ๋ฉˆ์ถ”๊ธฐ ์œ„ํ•ด์„œ๋ผ๊ณ  ํ•œ๋‹ค.

์Šคํƒ€์ผ์— ๋Œ€ํ•œ ์˜ต์…˜์ด ์ƒ๊ธฐ๋ฉด "์–ด๋–ค ์˜ต์…˜์ด ๋” ์ข‹์€๊ฐ€?", "์™œ?", "์šฐ๋ฆฌ๊ฐ€ ํ•œ ์„ ํƒ์ด ์ตœ์„ ์ธ๊ฐ€?"๋ผ๋Š” ๋…ผ์Ÿ์ด ๋ฐœ์ƒํ•œ๋‹ค. ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋•Œ ์ผ์ผ์ด "์—ฌ๊ธฐ๋Š” ์ž‘์€๋”ฐ์˜ดํ‘œ๋กœ ์“ฐ์…จ๋„ค์š”, ํฐ ๋”ฐ์˜ดํ‘œ๋กœ ๋ชจ๋‘ ๋ณ€๊ฒฝํ•ด ์ฃผ์„ธ์š”."๋ผ๋Š” ๋ง์„ ๋“ฃ๊ฑฐ๋‚˜ ํ•˜๋Š” ๊ฒŒ ์ •๋ง ํ•„์š”ํ•œ ๊ณผ์ •์ผ๊นŒ ์‹ถ๋‹ค. ์ฝ”๋“œ๋ฅผ ์งœ๊ธฐ๋„ ์ „์— ์ด๋Ÿฐ ๋…ผ์Ÿ๋“ค์„ ํ•˜๋‹ค๋ณด๋ฉด ์ •๋ง ์ค‘์š”ํ•œ ์ฝ”๋“œ ์งœ๊ธฐ์— ์“ฐ๋Š” ์—๋„ˆ์ง€๋ฅผ ๋‚ญ๋น„ํ•˜๋Š” ์ผ๋ฟ์ด๋‹ค. 

 

๋‹ค๋ฅธ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ์„ ๋‘๊ณ  ์™œ prettier๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ์‹ถ๊ฒ ์ง€๋งŒ, prettier์—๊ฒŒ๋Š” 100% ์™„์ „ํ•œ ์ž๋™ํ™”๋ผ๋Š” ์ตœ๊ฐ•์˜ ๋ฌด๊ธฐ๊ฐ€ ์žˆ๋‹ค.

๋ถ„๋ช… prettier๊ฐ€ ์ •ํ•œ ์ฝ”๋“œ ํฌ๋งทํŒ… ์ค‘์— ๋งˆ์Œ์— ์™๋“ค์ง€ ์•Š๋Š” ๋ถ€๋ถ„๋„ ์žˆ๊ธฐ ๋งˆ๋ จ์ด์ง€๋งŒ ํ•˜๋‚˜์˜ ์Šคํƒ€์ผ๋กœ ์ž๋™์œผ๋กœ ํ†ต์ผํ•ด ์ฃผ๋Š” ์ด์ ๊ณผ ์ €์žฅ์œผ๋กœ ๋ฐ”๋กœ ํฌ๋งท์ด ์ ์šฉ๋˜๋Š” ์ด์ ์ด ๋” ๋งค๋ ฅ์ ์œผ๋กœ ๋‹ค๊ฐ€์˜จ๋‹ค. 

์ €์žฅ์‹œ ์ž๋™ ํฌ๋ฉงํŒ…

 

prettier๋Š” ์ผ๊ด€์„ฑ์žˆ๋Š” ์ฝ”๋“œ ์Šคํƒ€์ผ๋กœ ํฌ๋งท์„ ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋ผ๋Š” ์ ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

์ด์ œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž. ๋จผ์ € prettier ์„ค์ •์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ณ , ๊ทธ ํ›„ git hooks ์„ค์ •๋„ ์ถ”๊ฐ€ํ•ด ๋ณด์ž.

prettier ์„ค์ •

1. ์„ค์น˜

yarn add --dev --exact prettier

 

2. ์„ค์ • ํŒŒ์ผ

  • . prettierrc ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ทœ์น™์ด ์žˆ๋‹ค๋ฉด ์—ฌ๊ธฐ์— ์ ์œผ๋ฉด ๋œ๋‹ค.
{
  "singleQuote": true,
  "trailingComma": "all",
  "arrowParens": "avoid"
}
  • .prettierignore ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ํฌ๋งท์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ํŒŒ์ผ๋“ค์„ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
.next
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# changelog
CHANGELOG.md

Git hooks ์„ค์ •

1. ์„ค์น˜

yarn add --dev husky lint-staged
npx husky init

 

2. ์„ค์ • ํŒŒ์ผ

  • .husky/pre-commit ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
npx lint-staged
  • package.json์— ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€๋ฅผ ํ•ด์ค€๋‹ค.
{
  "lint-staged": {
    "**/*": "prettier --write --ignore-unknown"
  }
}

์‹คํ–‰

ํ„ฐ๋ฏธ๋„์— yarn prettier . --write ๋ช…๋ น์–ด๋กœ ์‹คํ–‰ํ•ด ๋ณด์ž.

(...์ƒ๋žต...)

 

ํ•ด๋‹น ๋ช…๋ น์–ด๋กœ ํ•ญ์ƒ ์‹คํ–‰ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์„ ๋Š๊ผˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค. package.json scripts์— ๋ช…๋ น์–ด๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ถ”๊ฐ€ํ•ด ๋ณด์ž.

{
  "name": "vinylify",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "prepare": "husky",
    "format": "yarn prettier . --write", // ์ถ”๊ฐ€
    "format:check": "yarn prettier . --check", // ์ถ”๊ฐ€
    "typecheck": "tsc --noEmit --incremental false"
  },
// ... ์ƒ๋žต
}

Eslint

๐Ÿค” eslint๊ฐ€ ๋ญ๊ณ  ์™œ ํ•„์š”ํ• ๊นŒ?

Eslint๋Š” ECMAScript์™€ JavaScript ์ฝ”๋“œ๋ฅผ ์ •์ ์œผ๋กœ ๋ถ„์„ํ•ด์„œ

์ฝ”๋“œ ์—๋Ÿฌ๋ฅผ ์ฐพ์•„์ค˜์„œ ์ฝ”๋“œ ํ€„๋ฆฌํ‹ฐ๊ฐ€ ์ผ์ •ํ•˜๊ณ  ๋ฒ„๊ทธ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋„๊ตฌ๋‹ค.

eslint ์„ค์ •

1. ์„ค์น˜

yarn create @eslint/config@latest

 

2. ์„ค์ •

yarn init @eslint/config

 

์œ„์˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด eslint.config.js ๋‚˜ eslint.config.mjs ๋“ฑ์˜ ํŒŒ์ผ์ด ์ƒ์„ฑ๋œ๋‹ค.

๊ทธ ์•ˆ์—๋Š” ๊ทœ์น™์„ ์„ค์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด "off"๋Š” ๊ทœ์น™์„ ๋„๊ณ , "warn" ๋˜๋Š” 1๋กœ ์„ค์ • ์‹œ ๊ฒฝ๊ณ ๋งŒ ์ผœ๊ณ  exit code๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ณ , "error" ๋˜๋Š” 2๋กœ ์„ค์ •ํ•˜๋ฉด ์—๋Ÿฌ๋ฅผ ํ‚ค๊ณ  exit code๋ฅผ 1๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

// eslint.config.js
import js from "@eslint/js";

export default [
    js.configs.recommended,

    {
        rules: {
            "no-unused-vars": "warn",
            "no-undef": "warn"
        }
    }
];

 

eslint์˜ ํฐ ํŠน์ง•์€ ์ ์šฉํ•  ๊ทœ์น™๋“ค์„ ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์›ํ•˜๋Š” ์„ค์ •๋“ค์„ devDependencies๋กœ ์ถ”๊ฐ€ํ•˜๊ณ  config ํŒŒ์ผ์—๋„ ์ถ”๊ฐ€๋ฅผ ํ•ด์ฃผ์ž.

yarn add @typescript-eslint/parser eslint-plugin-react-hooks eslint-plugin-react-refresh
module.exports = {
  root: true,
  env: { browser: true, es2020: true },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
  ],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parser: '@typescript-eslint/parser',
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': [
      'warn',
      { allowConstantExport: true },
    ],
  },
};

Prettier vs. Linters

prettier์™€ eslint๋Š” ํฌ๋งท์„ ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋ผ๋Š” ๊ฑด ๊ฐ™์€๋ฐ, ๋‹ด๋‹น ์˜์—ญ์ด ์„œ๋กœ ๋‹ค๋ฅด๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

linter๋Š” ๋‹ค์Œ์˜ ๋‘ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๊ทœ์น™์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

 

ํฌ๋ฉง ๊ทœ์น™ : eg: max-lenno-mixed-spaces-and-tabskeyword-spacingcomma-style 

 

prettier๋Š” ์ด๋ ‡๊ฒŒ ๋‹ค์–‘ํ•œ ๊ทœ์น™๋“ค์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์„ ํ•œ๋ฐฉ์— ์—†์• ์ค€๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งœ๋˜๊ฐ„์— prettier๋Š” ์ž์‹ ์˜ ํฌ๋งท ๊ทœ์น™์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ƒˆ๋กœ ํฌ๋งทํ•ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์ฝ”๋“œ ํ€„๋ฆฌํ‹ฐ ๊ทœ์น™: eg no-unused-varsno-extra-bindno-implicit-globalsprefer-promise-reject-errors

 

prettier๋Š” ์ฝ”๋“œ ํ€„๋ฆฌํ‹ฐ์— ๊ด€์—ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. 

์ด ์ผ์€ linter (eslint, tslint, stylelint ๋“ฑ)์ด ๋‹ด๋‹นํ•  ์˜์—ญ์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ์— ์žˆ๋Š” ๋ฒ„๊ทธ๋“ค์„ ์žก๋Š” ์—ญํ• ์€ linter๊ฐ€, ์ฝ”๋“œ ์™ธ๊ด€(?)์„ ์ผ๊ด€๋˜๊ฒŒ ๊พธ๋ฉฐ์ฃผ๋Š” ์—ญํ• ์€ prettier๊ฐ€ ๋‹ด๋‹นํ•œ๋‹ค๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.


Commitlint

 

๋งˆ์ง€๋ง‰์œผ๋กœ commitlint๋ฅผ ์„ค์ •ํ•ด ์ฃผ์ž.

commitlint๋Š” ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜์„ ์ค€์ˆ˜ํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๋ฆฐํ„ฐ๋‹ค.

์„ค์น˜

yarn add --dev @commitlint/{cli,config-conventional}

์„ค์ •

  • commitlint.config.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด ๋ณด์ž.
    • ์•„๋ž˜์˜ ์„ค์ •์€ ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ์ž‘์„ฑ ์‹œ ๋ฐฐ์—ด ์•ˆ์˜ ํ‚ค์›Œ๋“œ๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ทœ์น™์ด๋‹ค.
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'chore',
        'style',
        'refactor',
        'ci',
        'test',
        'perf',
        'revert',
        'init',
      ],
    ],
  },
};

 

์ด์ œ ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ์ž‘์„ฑํ•  ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ๋ฆฐํŠธ๋ฅผ ํ•ด์ค„ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด ๋ณด์ž.

  • github/workflows/commitlint.yml ํŒŒ์ผ ์ƒ์„ฑ
name: Lint Commit Messages
on: [pull_request, push]

jobs:
  commitlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - uses: wagoid/commitlint-github-action@v4

 

  • .husky/_/commit-msg ํŒŒ์ผ์— ์•„๋ž˜์˜ ๋‚ด์šฉ ์ถ”๊ฐ€
npx --no -- commitlint --edit $1

 

์‹คํ–‰

์ปค๋ฐ‹ ํ‚ค์›Œ๋“œ ์—†์ด ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ์ž‘์„ฑ ์‹œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.


๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป ์˜ค๋Š˜์€ ์ฝ”๋“œ๋ฅผ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์งœ๊ธฐ ์ „ ์ฝ”๋“œ ํฌ๋งท์— ๋Œ€ํ•œ ์„ค์ •์„ ๋ชจ๋‘ ํ–ˆ๋‹ค.
์ฝ”๋“œ ์Šคํƒ€์ผ, ์ฝ”๋“œ ๋ฒ„๊ทธ ๋ถ„์„, ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ๋ฆฐํŠธ ๋“ฑ์˜ ๊ทœ์น™์„ ๋…ผํ•˜๋ ค๋ฉด ๋งŽ์€ ์—๋„ˆ์ง€๊ฐ€ ์“ฐ์ผ ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ ๋ฏธ๋ฆฌ ์•ฝ์†ํ•œ ๊ทœ์น™์„ ์ •ํ•˜๊ณ , ์ž๋™ํ™”๊นŒ์ง€ ๊ฐ€๋Šฅํ•œ ๋„๊ตฌ๋“ค์„ ๋„์ž…ํ•ด ์ด๋Ÿฐ ๋…ผ์Ÿ์œผ๋กœ ์“ฐ์ผ ์—๋„ˆ์ง€๋ฅผ ์ ˆ์•ฝํ•ด์„œ
๋” ํšจ์œจ์ ์œผ๋กœ ์ž‘์—…์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๊ธฐ์— ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜ (DX)๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ€ ์˜ค๋Š˜์€ ์ ์šฉํ•œ ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด ๋ณด๋‹ˆ ๊ฐ ๋„๊ตฌ๋“ค์„ ๋” ์‚ดํŽด๋ณผ ๊ธฐํšŒ๊ฐ€ ๋œ ๊ฑฐ ๊ฐ™์•„์„œ ํฅ๋ฏธ๋กœ์› ๋‹ค. 

๐Ÿ“š References 

https://prettier.io/docs/en/comparison

 

Prettier · Opinionated Code Formatter

Opinionated Code Formatter

prettier.io

https://eslint.org/

 

Find and fix problems in your JavaScript code - ESLint - Pluggable JavaScript Linter

A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code quality with ease.

eslint.org

https://commitlint.js.org/

 

commitlint

commitlint Lint commit messages helps your team adhere to a commit convention

commitlint.js.org