Skip to Content
Nextra 4.0 is released πŸŽ‰

πŸ›‘οΈ ESLint

이 ν”„λ‘œμ νŠΈμ—μ„œλŠ” ESLint λ₯Ό μ‚¬μš©ν•˜μ—¬ μ½”λ“œ ν’ˆμ§ˆμ„ μœ μ§€ν•˜κ³ , μΌκ΄€λœ μŠ€νƒ€μΌμ„ μ μš©ν•©λ‹ˆλ‹€. ESLint λŠ” JavaScript 및 TypeScript μ½”λ“œμ—μ„œ 잠재적인 였λ₯˜λ₯Ό μ°Ύμ•„λ‚΄κ³ , μ½”λ“œ μŠ€νƒ€μΌμ„ κ²€μ‚¬ν•˜λŠ” λ„κ΅¬λ‘œ, ν˜‘μ—… μ‹œ μ½”λ“œ μŠ€νƒ€μΌμ˜ 일관성을 μœ μ§€ν•˜λŠ” 데 큰 도움이 λ©λ‹ˆλ‹€.

μ‹€ν–‰ 방법

λͺ…λ Ήμ–΄

ESLint κ²€μ‚¬λŠ” lint CLIλ₯Ό μ‚¬μš©ν•˜μ—¬ μˆ˜ν–‰ν•©λ‹ˆλ‹€. λ‹€λ§Œ, λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜λŠ” μœ„μΉ˜μ— 따라 μ‹€ν–‰ 방법이 λ‹¬λΌμ§‘λ‹ˆλ‹€.

pnpm run lint

ν”„λ‘œμ νŠΈ λ£¨νŠΈμ—μ„œ μ‹€ν–‰ν•  경우

루트 package.json에 μ •μ˜λœ lint λͺ…λ Ήμ–΄λŠ” turbo task λ₯Ό μ‹€ν–‰ν•˜λ„λ‘ μ„€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 즉, 루트 λ””λ ‰ν† λ¦¬μ—μ„œ pnpm run lint λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜λ©΄, Turboκ°€ λͺ¨λ“  μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ— λŒ€ν•΄ ESLint 검사λ₯Ό λ³‘λ ¬λ‘œ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

package.json
{ "scripts": { "lint": "turbo run lint" // ... λ‹€λ₯Έ μŠ€ν¬λ¦½νŠΈλ“€ } }

κ°œλ³„ μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ—μ„œ μ‹€ν–‰ν•  경우

각 μ›Œν¬μŠ€νŽ˜μ΄μŠ€λ³„ package.json νŒŒμΌμ—λ„ λͺ¨λ‘ lint μŠ€ν¬λ¦½νŠΈκ°€ μ •μ˜λ˜μ–΄ μžˆμœΌλ―€λ‘œ, 이λ₯Ό 직접 μ‹€ν–‰ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. λ‹€λ§Œ 이 경우, turbo task λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ , ν•΄λ‹Ή μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ—λ§Œ ESLint 검사λ₯Ό μˆ˜ν–‰ν•˜κ²Œ λ©λ‹ˆλ‹€. 즉, turbo μ—μ„œ μ œκ³΅ν•˜λŠ” 병렬 μ‹€ν–‰ 및 캐싱 κΈ°λŠ₯을 ν™œμš©ν•˜μ§€ μ•Šκ³  직접 ESLint 검사λ₯Ό μˆ˜ν–‰ν•˜κ²Œ λ©λ‹ˆλ‹€.

apps/docs/package.json
{ "scripts": { "lint": "next lint --max-warnings 0" // ... λ‹€λ₯Έ μŠ€ν¬λ¦½νŠΈλ“€ } }

Turbo Task

μ•žμ„œ μ–ΈκΈ‰ν•œ λŒ€λ‘œ, ν”„λ‘œμ νŠΈ 루트 λ””λ ‰ν† λ¦¬μ—μ„œ pnpm run lint λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜λ©΄ turbo task 둜 μ •μ˜λœ lint μž‘μ—…μ΄ μ‹€ν–‰λ©λ‹ˆλ‹€. lint μž‘μ—…μ€ ν”„λ‘œμ νŠΈ 루트 디렉토리에 μžˆλŠ” turbo.json νŒŒμΌμ— λ‹€μŒκ³Ό 같이 μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

turbo.json
{ "tasks": { "lint": { "dependsOn": ["build", "^lint"] } // ... λ‹€λ₯Έ μž‘μ—…λ“€ } }

λΉŒλ“œ μ‹€ν–‰ ν•„μš”

이 ν”„λ‘œμ νŠΈμ—μ„œλŠ” νŒ¨ν‚€μ§€λ“€μ˜ λΉŒλ“œ λ°©μ‹μ˜ νŠΉμ„±μƒ, ESLint 검사 전에 λ°˜λ“œμ‹œ λΉŒλ“œλ₯Ό λ¨Όμ € μ‹€ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€. 각 νŒ¨ν‚€μ§€λ“€μ΄ μ‹€μ œ λΉŒλ“œλœ μ½”λ“œλ₯Ό λ²ˆλ“€μ— ν¬ν•¨μ‹œν‚€κΈ° λ•Œλ¬Έμ—, λΉŒλ“œκ°€ μ™„λ£Œλ˜μ§€ μ•Šμ€ μƒνƒœμ—μ„œλŠ” ESLint 검사가 μ˜¬λ°”λ₯΄κ²Œ μˆ˜ν–‰λ˜μ§€ μ•Šμ„ 수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. λ”°λΌμ„œ, lint μž‘μ—…μ˜ dependsOn ν•„λ“œμ— build μž‘μ—…μ— μ˜μ‘΄ν•˜λ„λ‘ μ„€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

이 ν”„λ‘œμ νŠΈμ—μ„œ μ±„νƒν•˜κ³  μžˆλŠ” λΉŒλ“œ 방식에 λŒ€ν•œ 보닀 μžμ„Έν•œ λ‚΄μš©μ€ νŒ¨ν‚€μ§€ > λΉŒλ“œ 방식 λ¬Έμ„œλ₯Ό μ°Έκ³ ν•΄ μ£Όμ„Έμš”.

ESLint Config

곡톡 Config

곡톡 ESLint Config 듀은 configs/eslint-config 디렉토리 μ•„λž˜μ— μœ„μΉ˜ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 이 λ””λ ‰ν† λ¦¬μ—λŠ” μ—¬λŸ¬ 개의 ESLint Config 파일이 있으며, 각 νŒŒμΌμ€ νŠΉμ • λͺ©μ μ— 맞게 μ„€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

  • base.js: κΈ°λ³Έ ESLint 섀정을 ν¬ν•¨ν•©λ‹ˆλ‹€. 이 섀정은 λͺ¨λ“  ESLint Config νŒŒμΌμ— κ³΅ν†΅μ μœΌλ‘œ μ μš©ν•  κΈ°λ³Έ κ·œμΉ™λ“€μ„ μ •μ˜ν•©λ‹ˆλ‹€.
  • react-internal.js: React λ₯Ό μ‚¬μš©ν•˜λŠ” μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ— μ μš©λ˜λŠ” ESLint μ„€μ •μž…λ‹ˆλ‹€. React κ΄€λ ¨ κ·œμΉ™λ“€μ„ ν¬ν•¨ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • next.js: Next.js λ₯Ό μ‚¬μš©ν•˜λŠ” μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ— μ μš©λ˜λŠ” ESLint μ„€μ •μž…λ‹ˆλ‹€. Next.js κ΄€λ ¨ κ·œμΉ™λ“€μ„ ν¬ν•¨ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

곡톡 Config μƒμ†ν•˜κΈ°

각 μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ—μ„œλŠ” 곡톡 ESLint Config λ₯Ό 상속받아 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜κ±°λ‚˜, 좔가적인 κ·œμΉ™μ„ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜κΈ°

apps/docs μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ—μ„œλŠ” configs/eslint-config/next.js νŒŒμΌμ— μ •μ˜λœ Next.js κ΄€λ ¨ ESLint 섀정을 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

apps/docs/.eslintrc.js
import { nextJsConfig } from "@repo/eslint-config/next-js"; /** @type {import("eslint").Linter.Config} */ export default nextJsConfig;

ν™•μž₯ν•˜κΈ°

apps/frontend-workshop μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ—μ„œλŠ” configs/eslint-config/react-internal.js νŒŒμΌμ„ ν™•μž₯ν•˜μ—¬ React κ΄€λ ¨ κ·œμΉ™μ„ μ μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

apps/frontend-workshop/eslint.config.js
import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; import { config } from "@repo/eslint-config/react-internal"; export default tseslint.config( { ignores: ["dist", "storybook-static"] }, { extends: [...config], files: ["**/*.{ts,tsx}"], // ... 좔가적인 κ·œμΉ™λ“€ }, );

ESLint Plugins

직접 μž‘μ„±ν•˜κΈ°

이 ν”„λ‘œμ νŠΈμ—μ„œλŠ” ESLint ν”ŒλŸ¬κ·ΈμΈμ„ 직접 μž‘μ„±ν•˜μ—¬ μ‚¬μš©ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

ESLint Config 에 μ μš©ν•˜κΈ°

μž‘μ„±ν•œ ESLint κ·œμΉ™μ€ ESLint Config 에 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, configs/eslint-config/base.js νŒŒμΌμ„ 보면, eslint-plugin-sample νŒ¨ν‚€μ§€μ—μ„œ μž‘μ„±ν•œ κ·œμΉ™μ„ λ‹€μŒκ³Ό 같이 μ μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λ§ˆμ°¬κ°€μ§€λ‘œ μƒˆλ‘œμš΄ ESLint κ·œμΉ™μ„ μž‘μ„±ν•œ ν›„, ν•΄λ‹Ή κ·œμΉ™μ„ 이와 같이 ESLint Config 에 μΆ”κ°€ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜μ‘΄μ„± μΆ”κ°€ν•˜κΈ°

μž‘μ„±ν•œ ESLint ν”ŒλŸ¬κ·ΈμΈμ„ ESLint Config 에 μ μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” ν•΄λ‹Ή ν”ŒλŸ¬κ·ΈμΈμ„ eslint-config νŒ¨ν‚€μ§€μ˜ devDependencies에 μΆ”κ°€ν•©λ‹ˆλ‹€.

configs/eslint-config/package.json
{ // ... λ‹€λ₯Έ μ„€μ •λ“€ "devDependencies": { "@repo/eslint-plugin-sample": "workspace:*", // ... λ‹€λ₯Έ devDependencies }, }

μ΄μ–΄μ„œ μ•„λž˜μ˜ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜μ—¬ eslint-plugin-sample νŒ¨ν‚€μ§€λ₯Ό eslint-config νŒ¨ν‚€μ§€ μ˜μ‘΄μ„±μœΌλ‘œ μ„€μΉ˜ν•©λ‹ˆλ‹€.

pnpm install --filter @repo/eslint-config

Config 에 μ μš©ν•˜κΈ°

이제 eslint-config νŒ¨ν‚€μ§€μ˜ ESLint Config νŒŒμΌμ— μž‘μ„±ν•œ ESLint ν”ŒλŸ¬κ·ΈμΈμ„ μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이 μ˜ˆμ‹œμ—μ„œλŠ” base.js νŒŒμΌμ— ESLint ν”ŒλŸ¬κ·ΈμΈμ„ μ μš©ν•˜λŠ” 방법을 보여주고 μžˆμ§€λ§Œ, λ‹€λ₯Έ ESLint Config 파일(e.g. next.js, react-niternal.js, …)μ—μ„œλ„ λ™μΌν•œ λ°©μ‹μœΌλ‘œ ESLint ν”ŒλŸ¬κ·ΈμΈμ„ μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

configs/eslint-config/base.js
// ... λ‹€λ₯Έ import λ¬Έλ“€ import samplePlugin from "@repo/eslint-plugin-sample"; /** * A shared ESLint configuration for the repository. * * @type {import("eslint").Linter.Config[]} * */ export const config = [ // ... λ‹€λ₯Έ ESLint Config λ“€ { plugins: { sample: samplePlugin, }, rules: { "sample/no-new-date": "warn", // eslint-plugin-sample νŒ¨ν‚€μ§€μ—μ„œ μž‘μ„±ν•œ κ·œμΉ™μ„ 적용 }, }, ];

Last updated on