ESLint, Prettier, TypeScript

ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ๋งˆ๋‹ค ESLint, Prettier, TypeScript๋ถ€ํ„ฐ ์„ค์ •ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๋งค๋ฒˆ ๋‹ค๋ฅธ ์„ค์ •์„ ์ฐพ์•„๋‹ค๋‹ˆ๊ธฐ ํž˜๋“ค์–ด์„œ ๊ทธ๋ƒฅ ์—ฌ๊ธฐ๋‹ค๊ฐ€ ์ •๋ฆฌํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

์„ค์ •์€ ํ˜„์žฌ ์‚ฌ์šฉ๋„๊ฐ€ ๋†’์€ Next.js ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌํ•œ๋‹ค. ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋Š” npm์œผ๋กœ ํ•˜๋‚˜, yarn์ด๋“  ๋ญ๋“  ์ƒ๊ด€์€ ์—†๋‹ค.

Dependencies

Prettier, ESLint

์šฐ์„  Prettier, ESLint๋ถ€ํ„ฐ ๋ถ€ํ„ฐ ๊น”์ž.

$ npm install -D prettier eslint eslint-config-prettier eslint-plugin-prettier

ESLint์€ ๋ฆฐํŠธ๋ฅผ ์„ค์ •ํ•˜๊ณ  Prettier๋Š” ํฌ๋งคํŒ…์„ ์„ค์ •ํ•˜๋Š” ๊ฑฐ์ง€๋งŒ, ๋‘ ์„ค์ • ๊ฐ„ ์ถฉ๋Œ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ eslint-config-prettier, eslint-plugin-prettier์„ ์„ค์น˜ํ•œ๋‹ค.

๊ทธ๋ƒฅ eslint, prettier, eslint-config-prettier, eslint-plugin-prettier๋Š” ๋‹จ์ง์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ณ  ๊ฐ™์ด ๊น”์ž.

eslint-config-prettier - ESLint์—์„œ Prettier์™€ ์ถฉ๋Œ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š” ๊ทœ์น™์„ ์ œ๊ฑฐํ•œ๋‹ค.

// .eslintrc.js (๋˜๋Š” .json)
{
  extends: [
    // ...
    "prettier" // ๋งˆ์ง€๋ง‰์— ๋„ฃ์ž.
  ]
}

eslint-plugin-prettier - ESLint์— Prettier ๊ทœ์น™์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

๊ณต์‹ ๋ฌธ์„œ์—์„œ ์ถ”์ฒœํ•˜๋Š” ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. eslint-plugin-prettier๋Š” plugin:prettier/recommended ์„ค์ •์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด ์„ค์ •์€ eslint-plugin-prettier ๋ฟ ์•„๋‹ˆ๋ผ eslint-config-prettier ์„ค์ •๊นŒ์ง€ ํ•œ๋ฒˆ์— ํ•ด์ค€๋‹ค. ์ฆ‰, ์ด์ „ ๋‹จ๊ณ„์—์„œ ํ–ˆ๋˜ extends๋Š” ์—†์• ๋„ ๋œ๋‹ค.

// .eslintrc.js
module.exports = {
  plugins: ["prettier"],
  rules: {
    "prettier/prettier": "error"
  },
  extends: [
    "plugin:prettier/recommended" // ์ถ”์ฒœ ์˜ต์…˜. ์‚ฌ์šฉํ•œ๋‹ค๋ฉด extends ๋งจ ๋งˆ์ง€๋ง‰์— ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.
  ]
}

์ดํ›„์—๋Š” ์ง์ ‘ .prettierrc ํŒŒ์ผ์„ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค.

TypeScript

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์“ธ๊ฑฐ๋ฉด ๊น”๋ฉด ๋œ๋‹ค.

$ npm install -D typescript

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์„ค์ •์„ ์œ„ํ•ด tsconfig.json์„ ์ด์šฉํ•œ๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๋‹ฌ๋ฆฌ ํ”„๋กœ์ ํŠธ์— ๋”ฐ๋ผ ์–ด๋–ค ์„ค์ •์„ ๋„๊ณ  ์ผœ์„œ ํƒ€์ž… ์ฒดํ‚น์„ ๋Š์Šจํ•˜๊ฒŒ ํ•˜๊ฑฐ๋‚˜ ๊ฐ•ํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด๋Ÿฐ ๋‚ด์šฉ์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด Effective TypeScript๋ฅผ ์ฝ์–ด๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค. ์ด ๋ธ”๋กœ๊ทธ์—๋„ ์ •๋ฆฌ๋˜์–ด ์žˆ๋‹ค.

typescript-eslint

$ npm install @typescript-eslint/parser @typescript-eslint/eslint-plugin

ESLint์™€ TypeScript๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— typescript์— ์ด์–ด์„œ typescript-eslint๋„ ์„ค์น˜ํ•œ๋‹ค. (์„ค์น˜ ๋ฐฉ๋ฒ• ๋ฐ ์šฉ๋„)

@typescript-eslint/parser๋Š” ESLint ์ปค์Šคํ…€ ํŒŒ์„œ์ด๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ESLint์— ํ˜ธํ™˜๋˜๋Š” ๋…ธ๋“œ๋กœ ํŒŒ์‹ฑํ•˜๋ฉฐ ํƒ€์ž…์Šคํฌ๋ฆฝํ”„ ํ”„๋กœ๊ทธ๋žจ์„ ์ง€์›ํ•œ๋‹ค.

์ด๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€๋Š” ๋‹ค๋ฅธ, ESLint์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” AST ์–‘์‹์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋ฌธ๋ฒ•์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ superset์ด๋‹ˆ๊นŒ(e.g., : number).

@typescript-eslint/eslint-plugin๋Š” typescript-eslint์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ทœ์น™๊ณผ ์„ค์ •์„ ESLint์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊น”์•„์•ผ ํ•œ๋‹ค.

์„ค๋ช…์€ ๋งˆ์ณค์œผ๋‹ˆ ์ด์ œ typescript-eslint์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ config๋„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. ๊ณต์‹ ๋ฌธ์„œ ์ถ”์ฒœ ์„ธํŒ…์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// .eslintrc.js
module.exports = {
  root: true,
  parser: "@typescript-eslint/parser", // ์ปค์Šคํ…€ parser ์„ค์ •
  extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  plugins: ["@typescript-eslint"]
}

๋งˆ์ง€๋ง‰์— ๋ชจ๋“  ์„ค์ •์„ ์ •๋ฆฌํ•ด์„œ ํ•œ๋ฒˆ์— ์ฝ”๋“œ๋กœ ์•Œ๋ ค์ค„ํ…Œ๋‹ˆ ๊ฑฑ์ •๋ง๋„๋ก!

Next.js

์˜์กด์„ฑ๋ถ€ํ„ฐ ์„ค์น˜ํ•˜์ž.

$ npm i -D @types/node @types/react @types/react-dom eslint-config-next eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks

React๋‚˜ Next.js๋ฅผ ์“ธ ๊ฒฝ์šฐ ESLint๊ฐ€ ๋ชจ๋“  .jsx, .tsx ํŒŒ์ผ๋งˆ๋‹ค import React ๋ฅผ ํ•˜๋ผ๊ณ  ์•„์šฐ์„ฑ์น˜๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

'React' must be in scope when using JSX

์–ด์ฐจํ”ผ Next.js๋ฅผ ์“ด๋‹ค๋ฉด React๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์œผ๋‹ˆ rule์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

module.exports = {
  rules: {
    "react/react-in-jsx-scope": "off",
  }
}

์ •๋ฆฌ

์ด์ œ ํŒŒ์ผ๋ณ„๋กœ ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์„ค์ •์„ ์ •๋ฆฌํ•œ๋‹ค.

ํฌ๋งคํŒ…์˜ ๊ฒฝ์šฐ ์‚ฌ๋žŒ๋งˆ๋‹ค ์›Œ๋‚™ ์ทจํ–ฅ์ด ๋‹ค๋ฅด๋‹ค. ๋‚œ 2 spaces indentation์— semicolon์€ ๋นผ๋Š” ๊ฒƒ์„, trailing comma๋Š” ๋„ฃ๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  C, Java, Rust ๋“ฑ ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ๋Š” ""๊ฐ€ ๋ฌธ์ž์—ด์ด๋‹ˆ ๋‚œ double quote(")๋กœ ํ•˜๊ฒ ๋‹ค. ์–ด์ฐจํ”ผ Prettier ๋Œ๋ฆฌ๋ฉด single quote(') ์•Œ์•„์„œ double quote๋กœ ๋ฐ”๋€๋‹ค.

// .prettierrc
{
  "printWidth": 80,
  "semi": false,
  "singleQuote": false,
  "trailingComma": "all",
  "tabWidth": 2,
  "bracketSpacing": true,
  "endOfLine": "lf",
  "useTabs": false,
  "arrowParens": "avoid"
}

ESLint ์„ค์ •์€ ์œ„์—์„œ ์„ค์ •ํ•œ ๊ฑธ ํ•ฉ์นœ ๊ฒฐ๊ณผ์ด๋‹ค. rules์—๋Š” ๋‚ด๊ฐ€ ์ข‹์•„ํ•˜๋Š” ์„ค์ •์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

// .eslintrc.js
module.exports = {
  root: true,
  plugins: [
    "prettier",
    "@typescript-eslint"
    "react",
    "react-hooks"
  ],
  extends: [
    "next/core-web-vitals",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended" // ๋งˆ์ง€๋ง‰ ์œ ์ง€
  ]
  rules: {
    "prettier/prettier": "error",
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
    "react/react-in-jsx-scope": "off",
    "no-console": "warn",
    "object-shorthand": "error"
  }
}

TypeScript ์„ค์ •์ด๋‹ค. ๊ธฐ๋ณธ ์„ค์ •์— ๋‚ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์˜ต์…˜์„ ์ถ”๊ฐ€๋กœ ๋„ฃ๋Š”๋‹ค.

{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "noUncheckedIndexedAccess": true, // ์ถ”๊ฐ€
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      { "name": "next" }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

์ดํ›„์— ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์žˆ๋‹ค๋ฉด ์ถ”๊ฐ€ ์˜ˆ์ •์ด๋‹ค.

Last updated

Was this helpful?