Skip to content

Latest commit

ย 

History

History
1129 lines (1059 loc) ยท 36.7 KB

File metadata and controls

1129 lines (1059 loc) ยท 36.7 KB

@itcode-dev/eslint-config

  • itcode.dev ์ „์—ญ ESLint ์„ค์ •

์‚ฌ์šฉ ๋””ํŽœ๋˜์‹œ


์‚ฌ์šฉ๋ฒ•

  • npm
# npm
npm add -D @itcode-dev/eslint-config
  • yarn
# yarn
yarn add -D @itcode-dev/eslint-config
  • yarn berry
# yarn berry
yarn add -D @itcode-dev/eslint-config eslint-plugin-better-tailwindcss eslint-plugin-import eslint-plugin-jest @next/eslint-plugin-next eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-sort-keys-fix @tanstack/eslint-plugin-query typescript-eslint eslint-plugin-unused-imports
  • pnpm
# pnpm
pnpm add -D @itcode-dev/eslint-config

์„ค์ • ๋ฐฉ๋ฒ•

name description
baseConfig ESLint ์„ค์ •
importConfig eslint-plugin-import ์„ค์ •
jestConfig eslint-plugin-jest ์„ค์ •
nextConfig @next/eslint-plugin-next ์„ค์ •
reactConfig eslint-plugin-react ์„ค์ •
reactHooksConfig eslint-plugin-react-hooks ์„ค์ •
sortKeysFixConfig eslint-plugin-sort-keys-fix ์„ค์ •
stylisticConfig @stylistic/eslint-plugin ์„ค์ •
tanstackConfig @tanstack/eslint-plugin-query ์„ค์ •
tseslintConfig typescript-eslint ์„ค์ •
unusedImportsConfig eslint-plugin-unused-imports ์„ค์ •
import itcodeConfig from '@itcode-dev/eslint-config'

// eslint.config.js
module.expors = [
  itcode.configs.baseConfig
]
import baseConfig from '@itcode-dev/eslint-config/base'

// eslint.config.js
module.expors = [
  baseConfig
]

์„ค์ • ์„ธ๋ถ€์‚ฌํ•ญ

ESLint ๊ธฐ๋ณธ ์„ค์ •

import type { Linter } from 'eslint';

export default {
	// Override or add rules here
	rules: {
		// ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, ์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ๋Š” ์ค‘๊ด„ํ˜ธ๋ฅผ ๋ฌถ์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•จ
		'arrow-body-style': [
			'error',
			'as-needed'
		],
		// ๊ตฌ๋ฌธ ์ค‘๊ด„ํ˜ธ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋ช…์‹œํ•˜๋„๋ก ๊ฐ•์ œ
		curly: 'error',
		// switch์˜ default ๊ตฌ๋ฌธ์ด ๋ฐ˜๋“œ์‹œ ์ตœํ•˜๋‹จ์— ์œ„์น˜ํ•˜๋„๋ก ๊ฐ•์ œ
		'default-case-last': 'error',
		// ๊ธฐ๋ณธ๊ฐ’์ด ๋ช…์‹œ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์œ„์น˜ํ•˜๋„๋ก ๊ฐ•์ œ
		'default-param-last': 'error',
		// === ๋น„๊ต ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		eqeqeq: 'error',
		// alert(๊ธˆ์ง€)
		'no-alert': 'error',
		// ๋ฐ˜๋ณต๋ฌธ์—์„œ์˜ ๋ฐ˜๋ณต๋ฌธ์—์„œ์˜ ๋ฐ˜๋ณต๋ฌธ์—์„œ์˜ await ํ—ˆ์šฉ
		'no-await-in-loop': 'off',
		// console ์‚ฌ์šฉ ๋ฐฉ์ง€, ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ผ์ธ๋ณ„ ์˜ต์…˜ ๋น„ํ™œ์„ฑํ™”๋กœ ๋Œ€์‘. ๋‹จ, warn๊ณผ error๋Š” ํ—ˆ์šฉ
		'no-console': [
			'error',
			{
				allow: [
					'warn',
					'error'
				]
			}
		],
		// ์กฐ๊ฑด๋ฌธ์—์„œ์˜ ๋ถˆํ•„์š”ํ•œ return ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-else-return': 'error',
		// ๋นˆ ๋ฉ”์„œ๋“œ ๋ฐฉ์ง€
		'no-empty-function': 'error',
		// null ๋น„๊ต์— eq ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-eq-null': 'error',
		// eval is evil
		'no-eval': 'error',
		// ๋ถˆํ•„์š”ํ•œ bool ์บ์ŠคํŒ… ๋ฐฉ์ง€
		'no-extra-boolean-cast': 'error',
		// ๋‚œํ•ดํ•œ ์บ์ŠคํŒ… ์—ฐ์‚ฐ ๋ฐฉ์ง€
		'no-implicit-coercion': 'error',
		// eval ์œ ์‚ฌ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-implied-eval': 'error',
		// ์ฝ”๋“œ ๋„์ค‘์˜ ์ธ๋ผ์ธ ์ฃผ์„ ๋ฐฉ์ง€
		'no-inline-comments': 'error',
		// __iterator__ ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-iterator': 'error',
		// ๋ฌด์˜๋ฏธํ•œ ๋ธ”๋ก๋ฌธ ๋ฐฉ์ง€
		'no-lone-blocks': 'error',
		// ๋ถˆํ•„์š”ํ•œ ์กฐ๊ฑด๋ฌธ ์ค‘์ฒฉ ๋ฐฉ์ง€
		'no-lonely-if': 'error',
		// ํ• ๋‹น์ด๋‚˜ ๋น„๊ต๊ฐ€ ์•„๋‹Œ ๋‹จ๋… new ์—ฐ์‚ฐ์ž ๋ฐฉ์ง€
		'no-new': 'error',
		// Function์„ ํ™œ์šฉํ•œ ๋ฉ”์„œ๋“œ ์ƒ์„ฑ ๋ฐฉ์ง€
		'no-new-func': 'error',
		// ๋ถˆํ•„์š”ํ•œ ๋ž˜ํ•‘์— new ์—ฐ์‚ฐ์ž ๋ฐฉ์ง€
		'no-new-wrappers': 'error',
		// ๋ถˆํ•„์š”ํ•œ Object ์ƒ์„ฑ์ž ๋ฐฉ์ง€
		'no-object-constructor': 'error',
		// 8์ง„์ˆ˜ ์ด์Šค์ผ€์ดํ”„ ๋ฐฉ์ง€
		'no-octal-escape': 'error',
		// ํŒŒ๋ผ๋ฏธํ„ฐ ์žฌํ• ๋‹น ๋ฐฉ์ง€
		'no-param-reassign': 'error',
		// __proto__ ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-proto': 'error',
		// javascript:console.log(์‚ฌ์šฉ๊ธˆ์ง€)
		'no-script-url': 'error',
		// ์ƒ์œ„ ์Šค์ฝ”ํ”„์—์„œ ์‚ฌ์šฉ๋œ ๋ณ€์ˆ˜๋ช…์„ ํ•˜์œ„ ์Šค์ฝ”ํ”„์—์„œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-shadow': 'error',
		// ๋ฆฌํ„ฐ๋Ÿด ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๋ฐฉ์ง€
		'no-throw-literal': 'error',
		// ํ• ๋‹น๋˜์ง€ ์•Š์€ ์ดˆ๊ธฐ ๋ณ€์ˆ˜์— undefined ํ• ๋‹น ๋ฐฉ์ง€
		'no-undef-init': 'error',
		// ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ‘œํ˜„ ๋ฐฉ์ง€
		'no-unused-expressions': 'error',
		// ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ณ€์ˆ˜๋ช… ๋ฐฉ์ง€
		'no-unused-vars': 'error',
		// ๋ถˆํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ call, bind ์‚ฌ์šฉ ๋ฐฉ์ง€
		'no-useless-call': 'error',
		// "๋ถˆํ•„์š”ํ•œ" + "๋ฌธ์ž์—ด" + "์ด์–ด์“ฐ๊ธฐ" + "๋ฐฉ์ง€"
		'no-useless-concat': 'error',
		// ๋ถˆํ•„์š”ํ•œ return ๋ฐฉ์ง€
		'no-useless-return': 'error',
		// 'var' is banned by Developer
		'no-var': 'error',
		// 'void' you too
		'no-void': 'error',
		// ๊ฐ„๊ฒฐํ•œ ํ˜•์‹ ํ‘œํ˜„ ๊ฐ•์ œ
		'object-shorthand': 'error',
		// ์ถ•์—ฐ๊ฐ• (์ถ•์•ฝํ˜• ์—ฐ์‚ฐ์ž ๊ฐ•์ œ๋ผ๋Š” ๋œป)
		'operator-assignment': 'error',
		// (์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ) => ์‚ฌ์šฉ ๊ถŒ์žฅ
		'prefer-arrow-callback': 'error',
		// const ์‚ฌ์šฉ ๊ถŒ์žฅ
		'prefer-const': 'error',
		// { ๊ตฌ์กฐ, ํŒŒ๊ดด } = ํ˜•์‹ ๊ฐ•์ œ
		'prefer-destructuring': 'error',
		// ์ง€์ˆ˜ ํ•จ์ˆ˜ ๋Œ€์‹  ** ์—ฐ์‚ฐ์ž ๊ฐ•์ œ
		'prefer-exponentiation-operator': 'error',
		// `${string} ํ…œํ”Œ๋ฆฟ ๊ฐ•์ œ`
		'prefer-template': 'error',
		// await ์ง€์‹œ์ž๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฉ”์„œ๋“œ์˜ asyncํ™” ๋ฐฉ์ง€
		'require-await': 'error',
		// ํ‚ค ์ •๋ ฌ ๊ฐ•์ œ
		'sort-keys': 'error',
		// ๋ฐฉ์ง€ํ•จ, ์š”๋‹ค ํ‘œํ˜„์„
		yoda: 'error'
	}
} satisfies Linter.Config;

eslint-plugin-better-tailwindcss ์„ค์ •

import pluginBetterTailwindcss from 'eslint-plugin-better-tailwindcss';

import type { Linter } from 'eslint';

export default {
	plugins: { '@better-tailwindcss': pluginBetterTailwindcss },
	rules: {
		// ์ผ๊ด€๋œ ํด๋ž˜์Šค ์ •๋ ฌ ๊ฐ•์ œ
		'@better-tailwindcss/enforce-consistent-class-order': 'error',
		// ์ผ๊ด€๋œ ํด๋ž˜์Šค ๋ž˜ํ•‘ ๊ฐ•์ œ
		'@better-tailwindcss/enforce-consistent-line-wrapping': 'error',
		// ์ผ๊ด€๋œ CSS ๋ณ€์ˆ˜ ํ˜ธ์ถœ ๋ฐฉ์‹ ๊ฐ•์ œ
		'@better-tailwindcss/enforce-consistent-variable-syntax': 'error',
		// ์ถœ๋™ํ•˜๋Š” ํด๋ž˜์Šค ์Šคํƒ€์ผ๋ง ๋ฐฉ์ง€
		'@better-tailwindcss/no-conflicting-classes': 'error',
		// ์ค‘๋ณต๋œ ํด๋ž˜์Šค ๋ฐฉ์ง€
		'@better-tailwindcss/no-duplicate-classes': 'error',
		// ๋ถˆํ•„์š” ๊ณต๋ฐฑ ์ œ๊ฑฐ
		'@better-tailwindcss/no-unnecessary-whitespace': 'error'
	}
} satisfies Linter.Config;

eslint-plugin-import ์„ค์ •

import pluginImport from 'eslint-plugin-import';

import type { Linter } from 'eslint';

export default {
	plugins: { '@import': pluginImport },
	rules: {
		// ๊ธฐ๋ณธ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ
		'@import/default': 'error',
		// export ์œ ํšจ์„ฑ ๊ฒ€์ฆ
		'@import/export': 'error',
		// export๋Š” ๋ฐ˜๋“œ์‹œ ํ•˜๋‹จ์— ์œ„์น˜
		'@import/exports-last': 'error',
		// import๋Š” ๋ฐ˜๋“œ์‹œ ์ƒ๋‹จ์— ์œ„์น˜
		'@import/first': 'error',
		// ๋ช…๋ช…๋œ import๊ฐ€ ์ •ํ™•ํžˆ export ์ธ์ง€ ๊ฒ€์ฆ
		'@import/named': 'error',
		// namespace๊ฐ€ ์‹ค์ œ๋กœ export ๋˜๋Š”์ง€ ๊ฒ€์ฆ
		'@import/namespace': 'error',
		// import ๊ตฌ๋ฌธ ๋งˆ์ง€๋ง‰์—” ๋ฐ˜๋“œ์‹œ ์ค„๋ฐ”๊ฟˆ
		'@import/newline-after-import': 'error',
		// ์ ˆ๋Œ€๊ฒฝ๋กœ import ๋ฐฉ์ง€
		'@import/no-absolute-path': 'error',
		// deprecated import ๋ฐฉ์ง€
		'@import/no-deprecated': 'error',
		// ์ค‘๋ณต๋œ import ๋ฐฉ์ง€
		'@import/no-duplicates': 'error',
		// ๋นˆ import ๋ฐฉ์ง€
		'@import/no-empty-named-blocks': 'error',
		// ๊ธฐ๋ณธ import๋Š” ์ด๋ฆ„๊ณผ ๋™์ผํ•˜๋„๋ก ๊ฐ•์ œ
		'@import/no-named-as-default': 'error',
		// ๊ธฐ๋ณธ export์—์„œ ๊ฐ™์€ ์ด๋ฆ„์˜ ์†์„ฑ ์ ‘๊ทผ ๋ฐฉ์ง€
		'@import/no-named-as-default-member': 'error',
		// ์ •๋ ฌ ๊ฐ•์ œ
		'@import/order': [
			'error',
			{
				alphabetize: {
					caseInsensitive: true,
					order: 'asc'
				},
				groups: [
					'builtin',
					'external',
					'internal',
					'parent',
					'sibling',
					'index',
					'type',
					'unknown'
				],
				named: true,
				'newlines-between': 'always',
				pathGroups: [ {
					group: 'internal',
					pattern: '@kapoo/**',
					position: 'before'
				} ],
				pathGroupsExcludedImportTypes: [ 'builtin' ],
				warnOnUnassignedImports: true
			}
		]
	}
} satisfies Linter.Config;

eslint-plugin-jest ์„ค์ •

import pluginJest from 'eslint-plugin-jest';

import type { Linter } from 'eslint';

export default {
	files: [
		'**/*.spec.[jt]s?(x)',
		'**/*.test.[jt]s?(x)'
	],
	languageOptions: { globals: pluginJest.environments.globals.globals },
	plugins: { '@jest': pluginJest },
	rules: {
		// ์ง€์‹œ์–ด๋Š” it์œผ๋กœ ๊ฐ•์ œํ•จ
		'@jest/consistent-test-it': [
			'error',
			{ fn: 'it' }
		],
		// ํ…Œ์ŠคํŠธ ๊ตฌ๋ฌธ์—์„œ ์–ด์ฐ์…˜ ๋ˆ„๋ฝ ๋ฐฉ์ง€
		'@jest/expect-expect': 'error',
		// ๋ณ„์นญ ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹Œ ๊ณต์‹ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๊ฐ•์ œ
		'@jest/no-alias-methods': 'error',
		// ํ…Œ์ŠคํŠธ ๊ตฌ๋ฌธ ์ฃผ์„ ์ฒ˜๋ฆฌ ๋ฐฉ์ง€
		'@jest/no-commented-out-tests': 'error',
		// ์กฐ๊ฑด๋ถ€ ํ˜ธ์ถœ ๋ฐฉ์ง€
		'@jest/no-conditional-expect': 'error',
		// ํ˜ผ๋ž€์Šค๋Ÿฐ setTimeout ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@jest/no-confusing-set-timeout': 'error',
		// deprecated ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@jest/no-deprecated-functions': 'error',
		// ๋น„ํ™œ์„ฑํ™”๋œ ํ…Œ์ŠคํŠธ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@jest/no-disabled-tests': 'error',
		// ๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ์—์„œ ์ฝœ๋ฐฑ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@jest/no-done-callback': 'error',
		// ์ค‘๋ณต๋œ ํ…Œ์ŠคํŠธ ํ›… ๋ฐฉ์ง€
		'@jest/no-duplicate-hooks': 'error',
		// export ๋ฐฉ์ง€๋ฅผ ํ†ตํ•ด ์ด ๊ทธ์ง€๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ์˜ ํ…Œ์ŠคํŠธ ์œ ์ถœ ๋ฐฉ์ง€
		'@jest/no-export': 'error',
		// ํ…Œ์ŠคํŠธ์— only ์ง€์‹œ์ž ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@jest/no-focused-tests': 'error',
		// ๋™๋ช…์ดํ…Œ์ŠคํŠธ ๋ฐฉ์ง€
		'@jest/no-identical-title': 'error',
		// ์Šค๋ƒ…์ƒท์—์„  ๋ฆฌํ„ฐ๋Ÿด ๋ณ€์ˆ˜ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@jest/no-interpolation-in-snapshots': 'error',
		// jasmine ์ „์—ญ ๋ณ€์ˆ˜ ์„ ์–ธ ๋ฐฉ์ง€
		'@jest/no-jasmine-globals': 'error',
		// mock ์ง์ ‘ import ๋ฐฉ์ง€
		'@jest/no-mocks-import': 'error',
		// ๋‹จ๋… expect ๋ฐฉ์ง€
		'@jest/no-standalone-expect': 'error',
		// ๋ถˆํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ prefix ๋ฐฉ์ง€
		'@jest/no-test-prefixes': 'error',
		// ํ…Œ์ŠคํŠธ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Œ
		'@jest/no-test-return-statement': 'error',
		// mock์€ ๋ฐ˜๋“œ์‹œ ํƒ€์ž…์„ ์ง€์ •ํ•ด์•ผํ•จ (JS์—์„  ๋น„ํ™œ์„ฑํ™” ํ•„์š”)
		'@jest/no-untyped-mock-factory': 'error',
		// ๊ฐ ๋ธ”๋ก ์ฃผ์œ„๋กœ ๊ฒฐ๊ณ„ ํ˜•์„ฑ
		'@jest/padding-around-all': 'error',
		// mock.calls ๋Œ€์‹  toHaveBeenCalledWith() ์‚ฌ์šฉ
		'@jest/prefer-called-with': 'error',
		// ๋‚ด์žฅ๋œ ๋น„๊ต ๋งค์ฒ˜ ์‚ฌ์šฉ
		'@jest/prefer-comparison-matcher': 'error',
		// for๋ฌธ์„ ์ง์ ‘ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค .each ์‚ฌ์šฉ
		'@jest/prefer-each': 'error',
		// ๋‚ด์žฅ ๋™๋“ฑ ๋น„๊ต ๋งค์ฒ˜ ์‚ฌ์šฉ
		'@jest/prefer-equality-matcher': 'error',
		// Promise๋ฅผ ํ†ตํ•œ ๋น„๋™๊ธฐ ๋น„๊ต๋Š” resolves ์‚ฌ์šฉ
		'@jest/prefer-expect-resolves': 'error',
		// ํ›… ์ •๋ ฌ
		'@jest/prefer-hooks-in-order': 'error',
		// jest.mocked ์‚ฌ์šฉํ•˜์—ฌ mocking ๊ฐ•์ œ
		'@jest/prefer-jest-mocked': 'error',
		// ๊ฐ„๊ฒฐํ•œ Promise mock ์„ ์–ธ ๊ฐ•์ œ
		'@jest/prefer-mock-promise-shorthand': 'error',
		// snapshot ํžŒํŠธ ๊ฐ•์ œ
		'@jest/prefer-snapshot-hint': 'error',
		// ์—„๊ฒฉํ•œ ๋น„๊ต์ธ toStrictEqual ์‚ฌ์šฉ
		'@jest/prefer-strict-equal': 'error',
		// ์›์‹œ๊ฐ’ ๋น„๊ต ์‹œ toBe ๊ธฐ๋ฐ˜ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
		'@jest/prefer-to-be': 'error',
		// ํฌํ•จ ์—ฌ๋ถ€ ๋น„๊ต ์‹œ toContain ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
		'@jest/prefer-to-contain': 'error',
		// ๊ธธ์ด ๋น„๊ต ์‹œ toHaveLength ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
		'@jest/prefer-to-have-length': 'error',
		// ๋นˆ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์ธ todo ์‚ฌ์šฉ
		'@jest/prefer-todo': 'error',
		// throws ์‹œ ์œ ์˜๋ฏธํ•œ ๋ฉ”์‹œ์ง€ ์ง€์ • ๊ฐ•์ œ
		'@jest/require-to-throw-message': 'error',
		// ์œ ํšจํ•œ describe ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
		'@jest/valid-describe-callback': 'error',
		// expect๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜๋„๋ก ๊ฐ•์ œ
		'@jest/valid-expect': 'error',
		// Promise๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜๋„๋ก ๊ฐ•์ œ
		'@jest/valid-expect-in-promise': 'error',
		// ํ…Œ์ŠคํŠธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ดํ‹€์„ ๊ฐ–๋„๋ก ๊ฐ•์ œ
		'@jest/valid-title': 'error'
	}
} satisfies Linter.Config;

eslint-plugin-next ์„ค์ •

import pluginNext from '@next/eslint-plugin-next';

import type { Linter } from 'eslint';

export default {
	plugins: { '@next': pluginNext },
	rules: {
		// font-display ์„ค์ •์„ ํ†ตํ•œ ํฐํŠธ ๋กœ๋“œ ์ œ์–ด ๋ฐฉ์‹ ๊ฐ•์ œ
		'@next/google-font-display': 'error',
		// Google Fonts pre-load ๊ฐ•์ œ
		'@next/google-font-preconnect': 'error',
		// ์ธ๋ผ์ธ script id ๊ฐ•์ œ
		'@next/inline-script-id': 'error',
		// GA ์ ์šฉ ์‹œ next/script ์‚ฌ์šฉ ๊ฐ•์ œ
		'@next/next-script-for-ga': 'error',
		// ๋ชจ๋“ˆ ๋ณ€์ˆ˜ ํ• ๋‹น ๋ฐฉ์ง€
		'@next/no-assign-module-variable': 'error',
		// ๋น„๋™๊ธฐ CSR ์ปดํฌ๋„ŒํŠธ ๋ฐฉ์ง€
		'@next/no-async-client-component': 'error',
		// beforeInteractive ์Šคํฌ๋ฆฝํŠธ๋Š” head ์•ˆ์— ์œ„์น˜ํ•˜๋„๋ก ๊ฐ•์ œ
		'@next/no-before-interactive-script-outside-document': 'error',
		// style ํƒœ๊ทธ ๋ฐฉ์ง€
		'@next/no-css-tags': 'error',
		// document ์ปดํฌ๋„ŒํŠธ import ๋ฐฉ์ง€
		'@next/no-document-import-in-page': 'error',
		// ํ—ค๋“œ ์ค‘๋ณต ๋ฐฉ์ง€
		'@next/no-duplicate-head': 'error',
		// head ๋Œ€์‹  next/head ์‚ฌ์šฉ ๊ฐ•์ œ
		'@next/no-head-element': 'error',
		// _document์—์„œ next/head ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@next/no-head-import-in-document': 'error',
		// next/link ์‚ฌ์šฉ ๊ฐ•์ œ
		'@next/no-html-link-for-pages': 'error',
		// next/image ์‚ฌ์šฉ ๊ฐ•์ œ
		'@next/no-img-element': 'error',
		// ํŽ˜์ด์ง€ ๋‚ด ํฐํŠธ ์„ค์ • ๋ฐฉ์ง€
		'@next/no-page-custom-font': 'error',
		// next/head ๋‚ด์— next/script ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@next/no-script-component-in-head': 'error',
		// _document ๋‚ด์— styled-jsx ์„ ์–ธ ๋ฐฉ์ง€
		'@next/no-styled-jsx-in-document': 'error',
		// ๋น„๋™๊ธฐ script ๊ฐ•์ œ
		'@next/no-sync-scripts': 'error',
		// ํŽ˜์ด์ง€๋ณ„ title์€ next/head๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@next/no-title-in-document-head': 'error',
		// ์˜คํƒ€ ๋ฐฉ์ง€
		'@next/no-typos': 'error',
		// ๋ถˆํ•„์š”ํ•œ Polyfill.io ์Šคํฌ๋ฆฝํŠธ ๋ฐฉ์ง€
		'@next/no-unwanted-polyfillio': 'error'
	}
} satisfies Linter.Config;

eslint-plugin-react ์„ค์ •

import pluginReact from 'eslint-plugin-react';

import type { Linter } from 'eslint';

export default {
	plugins: { '@react': pluginReact },
	rules: {
		// boolean์€ ๋ฐ˜๋“œ์‹œ isBoolean ํ˜•ํƒœ๋กœ ํ‘œ๊ธฐ๋จ
		'@react/boolean-prop-naming': 'error',
		// ๋ฒ„ํŠผ์€ ๋ฐ˜๋“œ์‹œ ๋ช…์‹œ์ ์ธ ํƒ€์ž…์„ ๊ฐ€์ง€๋„๋ก ๊ฐ•์ œ
		'@react/button-has-type': 'error',
		// ์ปดํฌ๋„ŒํŠธ๋Š” ํ•ญ์ƒ DisplayName๋ฅผ ๊ฐ€์ง
		'@react/display-name': 'error',
		// ๊ธฐ๋ช… ์ปดํฌ๋„ŒํŠธ๋Š” ํ•จ์ˆ˜ํ˜•์œผ๋กœ, ์ต๋ช… ์ปดํฌ๋„ŒํŠธ๋Š” ์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ๋กœ ๊ฐ•์ œ
		'@react/function-component-definition': [
			'error',
			{
				namedComponents: 'function-declaration',
				unnamedComponents: 'arrow-function'
			}
		],
		// useState์˜ get, set ๊ฐ์ฒด๋ช…์€ ๋Œ€์นญ์ ์œผ๋กœ ๊ตฌ์„ฑ๋จ
		'@react/hook-use-state': 'error',
		// boolean ํ”„๋กœํผํ‹ฐ๋Š” ์•”์‹œ์  ํ‘œํ˜„ ๊ฐ•์ œ
		'@react/jsx-boolean-value': [
			'error',
			'never'
		],
		// JSX๋Š” ๋ฐ˜๋“œ์‹œ .jsx ํ˜น์€ .tsx ํ™•์žฅ์ž๋ฅผ ๊ฐ€์ง
		'@react/jsx-filename-extension': [
			'error',
			{
				extensions: [
					'.jsx',
					'.tsx'
				]
			}
		],
		// ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ ๋ฐ ํ”„๋กœํผํ‹ฐ ๋ช…์นญ ๊ทœ์•ฝ ๊ฐ•์ œ
		'@react/jsx-handler-names': 'error',
		// JSX ๋ฐ˜๋ณต๋ฌธ ์‹œ, key ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•˜๋„๋ก ๊ฐ•์ œ
		'@react/jsx-key': 'error',
		// ์ฃผ์„์ด ํ…์ŠคํŠธ ๋…ธ๋“œ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•จ
		'@react/jsx-no-comment-textnodes': 'error',
		// ๋ถˆํ•„์š”ํ•œ ์žฌ๋ Œ๋”๋ง์„ ์œ ๋ฐœํ•˜๋Š” ๊ฐ’ ํ• ๋‹น ๋ฐฉ์ง€
		'@react/jsx-no-constructed-context-values': 'error',
		// ์ค‘๋ณต ํ”„๋กœํผํ‹ฐ ๋ฐฉ์ง€
		'@react/jsx-no-duplicate-props': 'error',
		// aํƒœ๊ทธ ์ทจ์•ฝ์  ๋ฐฉ์ง€
		'@react/jsx-no-target-blank': 'error',
		// ์ •์˜๋˜์ง€ ์•Š์€ ์ปดํฌ๋„ŒํŠธ ํ˜ธ์ถœ ๋ฐฉ์ง€
		'@react/jsx-no-undef': 'error',
		// ๋ถˆํ•„์š”ํ•œ ํ”„๋ž˜๊ทธ๋จผํŠธ ๋ฐฉ์ง€
		'@react/jsx-no-useless-fragment': 'error',
		// ์ค‘๋ณต ์Šคํ”„๋ ˆ๋“œ ํ”„๋กœํผํ‹ฐ ํ• ๋‹น ๋ฐฉ์ง€
		'@react/jsx-props-no-spread-multi': 'error',
		// ๋ฏธ์‚ฌ์šฉ JSX ๋ฐฉ์ง€
		'@react/jsx-uses-vars': 'error',
		// ์ด์ „ ์ƒํƒœ ๊ธฐ๋ฐ˜ ๋ณ€๊ฒฝ ์‹œ, ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ ์ฐธ์กฐํ•˜๋„๋ก ๊ฐ•์ œ
		'@react/no-access-state-in-setstate': 'error',
		// children์„ ํ”„๋กœํผํ‹ฐ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€
		'@react/no-children-prop': 'error',
		// dangerouslySetInnerHTML ํ”„๋กœํผํ‹ฐ์™€ children ๋ณ‘ํ–‰ ๊ธˆ์ง€
		'@react/no-danger-with-children': 'error',
		// deprecated ๋ฌธ๋ฒ• ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@react/no-deprecated': 'error',
		// state ์ง์ ‘ ๋ณ€๊ฒฝ ๋ฐฉ์ง€
		'@react/no-direct-mutation-state': 'error',
		// findDOMNode ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@react/no-find-dom-node': 'error',
		// ์œ ํšจํ•˜์ง€ ์•Š์€ HTML ์†์„ฑ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@react/no-invalid-html-attribute': 'error',
		// isMount ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@react/no-is-mounted': 'error',
		// ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ๋ฐฉ์ง€
		'@react/no-render-return-value': 'error',
		// ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ this ํ˜ธ์ถœ ๋ฐฉ์ง€
		'@react/no-this-in-sfc': 'error',
		// ์˜คํƒ€ ๋ฐฉ์ง€
		'@react/no-typos': 'error',
		// ๋ฌธ์ž์—ด ์ด์Šค์ผ€์ดํ•‘ ๊ฐ•์ œ
		'@react/no-unescaped-entities': 'error',
		// ์•Œ๋ ค์ง€์ง€ ์•Š์€ ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@react/no-unknown-property': 'error',
		// ๋ฏธ์‚ฌ์šฉ ํ”„๋กœํผํ‹ฐ ์„ ์–ธ ๋ฐฉ์ง€
		'@react/no-unused-prop-types': 'error',
		// ๋ฏธ์‚ฌ์šฉ ์ƒํƒœ ์„ ์–ธ ๋ฐฉ์ง€
		'@react/no-unused-state': 'error',
		// ํ”„๋กœํผํ‹ฐ ์ˆ˜์ • ๋ฐฉ์ง€
		'@react/prefer-read-only-props': 'error',
		// ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ ๊ฐ•์ œ
		'@react/prefer-stateless-function': 'error',
		// ํ”„๋กœํผํ‹ฐ ํƒ€์ž… ์„ ์–ธ ๊ฐ•์ œ
		'@react/prop-types': 'error',
		// children์„ ๊ฐ€์งˆ ์ˆ˜ ์—†๋Š” DOM์— children ํ• ๋‹น ๋ฐฉ์ง€
		'@react/void-dom-elements-no-children': 'error'
	}
} satisfies Linter.Config;

eslint-plugin-react-hooks ์„ค์ •

import pluginReactHooks from 'eslint-plugin-react-hooks';

import type { Linter } from 'eslint';

export default {
	files: [
		'**/*.jsx',
		'**/*.tsx'
	],
	plugins: { '@react-hooks': pluginReactHooks },
	rules: {
		// ์ฒ ์ €ํ•œ ๋””ํŽœ๋˜์‹œ ๋ฐฐ์—ด ๊ถŒ๊ณ 
		'@react-hooks/exhaustive-deps': 'error',
		// react hook ๊ทœ์น™์— ์œ„๋ฐฐ๋˜๋Š” ์ฝ”๋“œ ๋ฐฉ์ง€
		'@react-hooks/rules-of-hooks': 'error',
		// ๋ถˆ์•ˆ์ •ํ•œ ๋””ํŽœ๋˜์‹œ ๋ฐฐ์—ด ์„ ์–ธ ๋ฐฉ์ง€
		'@tanstack/query/no-unstable-deps': 'warn',
		// ์•ˆ์ •์ ์ธ QueryClient ์„ ์–ธ ๊ฐ•์ œ
		'@tanstack/query/stable-query-client': 'error'
	}
} satisfies Linter.Config;

eslint-plugin-sort-keys-fix ์„ค์ •

import pluginSortKeysFix from 'eslint-plugin-sort-keys-fix';

import type { Linter } from 'eslint';

export default {
	plugins: { '@sort-keys-fix': pluginSortKeysFix },
	rules: { '@sort-keys-fix/sort-keys-fix': 'error' }
} satisfies Linter.Config;

@stylistic/eslint-plugin ์„ค์ •

import stylistic from '@stylistic/eslint-plugin';

import type { Linter } from 'eslint';

export default {
	plugins: { '@stylistic': stylistic },
	rules: {
		// ๋ฐฐ์—ด ์•„์ดํ…œ์ด 3๊ฐœ ์ด์ƒ์ด๋ผ๋ฉด, ๋ธŒ๋ผ์ผ“์— ์ค„๋ฐ”๊ฟˆ ์ถ”๊ฐ€
		'@stylistic/array-bracket-newline': [
			'error',
			{
				minItems: 2,
				multiline: false
			}
		],
		// ๋ฐฐ์—ด ๋ธŒ๋ผ์ผ“์— ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/array-bracket-spacing': [
			'error',
			'always'
		],
		// ๋ฐฐ์—ด ์•„์ดํ…œ์ด 3๊ฐœ ์ด์ƒ์ด๋ผ๋ฉด, ๊ฐ ์š”์†Œ์— ์ค„๋ฐ”๊ฟˆ ์ถ”๊ฐ€
		'@stylistic/array-element-newline': [
			'error',
			{
				minItems: 2,
				multiline: false
			}
		],
		// ์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์–ด๋– ํ•œ ๊ฒฝ์šฐ์—๋„ ์†Œ๊ด„ํ˜ธ๋กœ ๊ฐ์Œˆ
		'@stylistic/arrow-parens': [
			'error',
			'always'
		],
		// ์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ์˜ ํ™”์‚ดํ‘œ ์–‘ ์˜†์— ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/arrow-spacing': 'error',
		// { ์ธ๋ผ์ธ ๋ธ”๋ก ๋ธŒ๋ผ์ผ“ }์— ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/block-spacing': 'error',
		// allman ์Šคํƒ€์ผ ๊ฐ•์ œ
		'@stylistic/brace-style': [
			'error',
			'allman'
		],
		// ๋ถˆํ•„์š”ํ•œ ์ปด๋งˆ ๋Œ•๊ธ€๋ง ๋ฐฉ์ง€
		'@stylistic/comma-dangle': 'error',
		// ์ปด๋งˆ๋Š” ๋ฐ˜๋“œ์‹œ, ์ด๋ ‡๊ฒŒ
		'@stylistic/comma-spacing': 'error',
		// ํ”„๋กœํผํ‹ฐ ๋ธŒ๋ผ์ผ“์— ์ŠคํŽ˜์ด์‹ฑ ์ œ๊ฑฐ
		'@stylistic/computed-property-spacing': 'error',
		// ๋ธ”๋ก ๋ธŒ๋ผ์ผ“์— ํ•ญ์ƒ ์ค„๋ฐ”๊ฟˆ ๊ฐ•์ œ
		'@stylistic/curly-newline': [
			'error',
			'always'
		],
		// ์ฒด์ด๋‹ ์‹œ .์€ ํ•ญ์ƒ ํ”„๋กœํผํ‹ฐ์— ์ถ”๊ฐ€๋จ
		'@stylistic/dot-location': [
			'error',
			'property'
		],
		// EOL ์ œ๊ฑฐ
		'@stylistic/eol-last': [
			'error',
			'never'
		],
		// ๋Œ€์ฒด ๋ˆ„๊ฐ€ ๋ฉ”์„œ๋“œ๋ฅผ fn ()๋กœ ํ˜ธ์ถœํ•จ?
		'@stylistic/function-call-spacing': 'error',
		// ํŒŒ๋ผ๋ฏธํ„ฐ ํ•œ ์ค„ ๊ฐ•์ œ
		'@stylistic/function-paren-newline': [
			'error',
			'never'
		],
		// *๋ฉ”์„œ๋“œ ๊ณต๋ฐฑ ์ œ๊ฑฐ
		'@stylistic/generator-star-spacing': 'error',
		// ์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ์˜ ์ธ๋ผ์ธ ๋ฐ˜ํ™˜๊ฐ’ ํ•œ ์ค„ ๊ฐ•์ œ
		'@stylistic/implicit-arrow-linebreak': 'error',
		// ์ธ๋ดํŠธ ํƒญ ๊ฐ•์ œ
		'@stylistic/indent': [
			'error',
			'tab'
		],
		// ๋ฐ”์ด๋„ˆ๋ฆฌ ์—ฐ์‚ฐ์ž ์ธ๋ดํŠธ ์ง€์ •
		'@stylistic/indent-binary-ops': [
			'error',
			'tab'
		],
		// JSX ํด๋กœ์ง• ๋ธŒ๋ผ์ผ“ ์œ„์น˜๊ฐ€ ํƒœ๊ทธ ์œ„์น˜์— ์˜์กดํ•˜๋„๋ก ๊ฐ•์ œ
		'@stylistic/jsx-closing-bracket-location': 'error',
		// JSX ํด๋กœ์ง• ํƒœ๊ทธ ์œ„์น˜๊ฐ€ ํƒœ๊ทธ ์œ„์น˜์— ์˜์กดํ•˜๋„๋ก ๊ฐ•์ œ
		'@stylistic/jsx-closing-tag-location': 'error',
		// JSX ํ• ๋‹น ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/jsx-curly-brace-presence': [
			'error',
			{
				children: 'never',
				props: 'never'
			}
		],
		// JSX ํ…œํ”Œ๋ฆฟ ์ค„๋ฐ”๊ฟˆ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/jsx-curly-newline': [
			'error',
			{
				multiline: 'consistent',
				singleline: 'consistent'
			}
		],
		// JSX ํ…œํ”Œ๋ฆฟ ๊ณต๋ฐฑ ํ•ญ์ƒ ์ œ๊ฑฐ
		'@stylistic/jsx-curly-spacing': [
			'error',
			{
				children: true,
				when: 'never'
			}
		],
		// JSX ํ”„๋กœํผํ‹ฐ ํ• ๋‹น ๊ธฐํ˜ธ ๊ณต๋ฐฑ ์ œ๊ฑฐ
		'@stylistic/jsx-equals-spacing': 'error',
		// JSX ์ฒซ ํ”„๋กœํผํ‹ฐ ์ค„๋ฐ”๊ฟˆ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/jsx-first-prop-new-line': 'error',
		// JSX ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/jsx-function-call-newline': 'error',
		// JSX ํ”„๋กœํผํ‹ฐ ์ค„๋ฐ”๊ฟˆ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/jsx-max-props-per-line': [
			'error',
			{ when: 'multiline' }
		],
		// JSX ํ”„๋กœํผํ‹ฐ ๋‹ค์ค‘ ๊ณต๋ฐฑ ์ œ๊ฑฐ
		'@stylistic/jsx-props-no-multi-spaces': 'error',
		// JSX ํ”„๋กœํผํ‹ฐ ๋”ฐ์˜ดํ‘œ ๋”๋ธ” ๊ฐ•์ œ
		'@stylistic/jsx-quotes': 'error',
		// JSX ์…€ํ”„ ํด๋กœ์ง• ๊ฐ•์ œ
		'@stylistic/jsx-self-closing-comp': [
			'error',
			{
				component: true,
				html: true
			}
		],
		// JSX ํ”„๋กœํผํ‹ฐ ํ‚ค ์ •๋ ฌ ๊ฐ•์ œ
		'@stylistic/jsx-sort-props': [
			'error',
			{
				callbacksLast: true,
				shorthandLast: true
			}
		],
		// JSX ์…€ํ”„ ํด๋กœ์ง• ์‹œ ํด๋กœ์ง• ํƒœ๊ทธ ์ „์œ„ ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/jsx-tag-spacing': [
			'error',
			{ beforeSelfClosing: 'always' }
		],
		// JSX ๋ž˜ํ•‘ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/jsx-wrap-multilines': [
			'error',
			{
				arrow: 'parens-new-line',
				assignment: 'parens-new-line',
				condition: 'parens-new-line',
				declaration: 'parens-new-line',
				logical: 'parens-new-line',
				prop: 'parens-new-line',
				propertyValue: 'parens-new-line',
				return: 'parens-new-line'
			}
		],
		// ํ‚ค ์ฝœ๋ก  ๊ณต๋ฐฑ์€ ํ•ญ์ƒ ํ›„์œ„์—๋งŒ ๋ถ™๋„๋ก ๊ฐ•์ œ
		'@stylistic/key-spacing': 'error',
		// ํ‚ค์›Œ๋“œ์˜ ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/keyword-spacing': 'error',
		// ๋ฉค๋ฒ„ ๊ตฌ๋ถ„์ž ๊ฐ•์ œ
		'@stylistic/member-delimiter-style': 'error',
		// ์ฃผ์„ ๊ตฌ๋ถ„ ๋ผ์ธ ๊ธฐ์ค€์œผ๋กœ ๊ฐ•์ œ
		'@stylistic/multiline-comment-style': [
			'error',
			'separate-lines'
		],
		// new ํด๋ž˜์Šค์—” ๋ฐ˜๋“œ์‹œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ด„ํ˜ธ ๋™๋ฐ˜ ๊ฐ•์ œ
		'@stylistic/new-parens': 'error',
		// ๋‘๋ฒˆ์งธ ์ฒด์ด๋‹๋ถ€ํ„ฐ ์ค„๋กœ ๊ตฌ๋ถ„ํ•˜๋„๋ก ๊ฐ•์ œ
		'@stylistic/newline-per-chained-call': [
			'error',
			{ ignoreChainWithDepth: 1 }
		],
		// ์• ๋กœ์šฐ ๋ฉ”์„œ๋“œ ๋ฐ˜ํ™˜๊ฐ’ ํ˜ผ๋™ ๋ฐฉ์ง€
		'@stylistic/no-confusing-arrow': 'error',
		// ๋ถˆํ•„์š”ํ•œ ์„ธ๋ฏธ์ฝœ๋ก  ๋ฐฉ์ง€
		'@stylistic/no-extra-semi': 'error',
		// ํ”Œ๋กœํŒ… ์ˆซ์ž ๋ฐฉ์ง€
		'@stylistic/no-floating-decimal': 'error',
		// ํƒญ๊ณผ ๊ณต๋ฐฑ ํ˜ผ์šฉ ๋ฐฉ์ง€
		'@stylistic/no-mixed-spaces-and-tabs': 'error',
		// ๋‹ค์ค‘ ๊ณต๋ฐฑ ๋ฐฉ์ง€
		'@stylistic/no-multi-spaces': 'error',
		// ๋‹ค์ค‘ ์ค„๋ฐ”๊ฟˆ ๋ฐฉ์ง€
		'@stylistic/no-multiple-empty-lines': 'error',
		// ํŠธ๋ฆด๋ง ๊ณต๋ฐฑ ๋ฐฉ์ง€
		'@stylistic/no-trailing-spaces': 'error',
		// ํ”„๋กœํผํ‹ฐ ์ฝค๋งˆ์— ์ „์œ„ ๊ณต๋ฐฑ ๋ฐฉ์ง€
		'@stylistic/no-whitespace-before-property': 'error',
		// ์˜ค๋ธŒ์ ํŠธ ๋ธŒ๋ผ์ผ“ ์ค„๋ฐ”๊ฟˆ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/object-curly-newline': [
			'error',
			{
				minProperties: 2,
				multiline: true
			}
		],
		// ์˜ค๋ธŒ์ ํŠธ ๋ธŒ๋ผ์ผ“ ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/object-curly-spacing': [
			'error',
			'always'
		],
		// ์˜ค๋ธŒ์ ํŠธ ์š”์†Œ ์ค„๋ฐ”๊ฟˆ ๊ทœ์น™ ๊ฐ•์ œ
		'@stylistic/object-property-newline': [
			'error',
			{ allowAllPropertiesOnSameLine: false }
		],
		// ๋ถˆํ•„์š”ํ•œ ๋ธ”๋Ÿญ ํŒจ๋”ฉ ๋ฐฉ์ง€
		'@stylistic/padded-blocks': [
			'error',
			'never'
		],
		// ํ‚ค์›Œ๋“œ๋ณ„ ํŒจ๋”ฉ ๊ทœ์น™ ์ง€์ •
		'@stylistic/padding-line-between-statements': [
			'error',
			{
				blankLine: 'always',
				next: 'return',
				prev: '*'
			},
			{
				blankLine: 'always',
				next: '*',
				prev: [
					'const',
					'let',
					'var'
				]
			},
			{
				blankLine: 'any',
				next: [
					'const',
					'let',
					'var'
				],
				prev: [
					'const',
					'let',
					'var'
				]
			},
			{
				blankLine: 'always',
				next: '*',
				prev: 'directive'
			},
			{
				blankLine: 'any',
				next: 'directive',
				prev: 'directive'
			},
			{
				blankLine: 'always',
				next: '*',
				prev: [
					'case',
					'default'
				]
			},
			{
				blankLine: 'always',
				next: [
					'enum',
					'interface',
					'type'
				],
				prev: '*'
			}
		],
		// ํ”„๋กœํผํ‹ฐ ๋”ฐ์˜ดํ‘œ๋Š” ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์ง€์ •
		'@stylistic/quote-props': [
			'error',
			'as-needed'
		],
		// ๋ฌธ์ž์—ด์€ ์‹ฑ๊ธ€ ๋”ฐ์˜ดํ‘œ ๊ฐ•์ œ
		'@stylistic/quotes': [
			'error',
			'single'
		],
		// ๊ฐ€๋ณ€ ์Šคํ”„๋ ˆ๋“œ์— ๊ณต๋ฐฑ ์ œ๊ฑฐ
		'@stylistic/rest-spread-spacing': 'error',
		// ์„ธ๋ฏธ์ฝœ๋ก  ๊ฐ•์ œ
		'@stylistic/semi': 'error',
		// ์„ธ๋ฏธ์ฝœ๋ก  ์ „์œ„ ๊ณต๋ฐฑ ๋ฐฉ์ง€
		'@stylistic/semi-spacing': 'error',
		// ์„ธ๋ฏธ์ฝœ๋ก ์€ ๋ฐ˜๋“œ์‹œ ๊ตฌ๋ฌธ ๋์— ์œ„์น˜
		'@stylistic/semi-style': 'error',
		// ๋ธ”๋ก ์ „์œ„ ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/space-before-blocks': 'error',
		// ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ด„ํ˜ธ ๊ณต๋ฐฑ ๋ฐฉ์ง€
		'@stylistic/space-before-function-paren': [
			'error',
			'never'
		],
		// ์—ฐ์‚ฐ์ž ๊ณต๋ฐฑ ๊ฐ•์ œ
		'@stylistic/space-infix-ops': 'error',
		// ๋‹จํ•ญ์—ฐ์‚ฐ์ž ๊ณต๋ฐฑ ์ œ๊ฑฐ
		'@stylistic/space-unary-ops': 'error',
		// ์ฃผ์„ ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ
		'@stylistic/spaced-comment': 'error',
		// case ์ฝœ๋ก  ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ
		'@stylistic/switch-colon-spacing': 'error',
		// ํ…œํ”Œ๋ฆฟ ๋ฌธ๋ฒ• ๋‚ด๋ถ€ ๊ณต๋ฐฑ ๋ฐฉ์ง€
		'@stylistic/template-curly-spacing': 'error',
		// ํ…œํ”Œ๋ฆฟ ํƒœ๊ทธ ๊ณต๋ฐฑ ์ถ”๊ฐ€
		'@stylistic/template-tag-spacing': [
			'error',
			'never'
		],
		// ํƒ€์ž… ์–ด๋…ธํ…Œ์ด์…˜ ํ›„ํ–‰ ๊ณต๋ฐฑ๋งŒ ํ—ˆ์šฉ
		'@stylistic/type-annotation-spacing': 'error',
		// <์ œ๋„ค๋ฆญ, ํƒ€์ž…> ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ
		'@stylistic/type-generic-spacing': 'error',
		// ํŠœํ”Œ ํƒ€์ž… ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ
		'@stylistic/type-named-tuple-spacing': 'error',
		// (IIFE ํ˜ธ์ถœ ๋ฐฉ์‹)() ๊ฐ•์ œ
		'@stylistic/wrap-iife': [
			'error',
			'inside'
		]
	}
} satisfies Linter.Config;

@tanstack/eslint-plugin-query ์„ค์ •

import pluginQuery from '@tanstack/eslint-plugin-query';

import type { Linter } from 'eslint';

export default {
	plugins: { '@tanstack/query': pluginQuery },
	rules: {
		// ์ฒ ์ €ํ•œ ๋””ํŽœ๋˜์‹œ ๊ฐ•์ œ
		'@tanstack/query/exhaustive-deps': 'error',
		// ๋ถˆํ•„์š”ํ•œ ๊ตฌ์กฐ ํŒŒ๊ดด ๊ฐ์ฒด ์„ ์–ธ ๋ฐฉ์ง€
		'@tanstack/query/no-rest-destructuring': 'error',
		// ๋ถˆ์•ˆ์ •ํ•œ ๋””ํŽœ๋˜์‹œ ์„ ์–ธ ๋ฐฉ์ง€
		'@tanstack/query/no-unstable-deps': 'warn',
		// ์•ˆ์ •์ ์ธ QueryClient ์„ ์–ธ ๊ฐ•์ œ
		'@tanstack/query/stable-query-client': 'error'
	}
} satisfies Linter.Config;

typescript-eslint ์„ค์ •

import tseslint from 'typescript-eslint';

import type { ConfigArray } from 'typescript-eslint';

const config: ConfigArray[number] = {
	files: [
		'**/*.ts',
		'**/*.tsx',
		'**/*.cts',
		'**/*.mts'
	],
	plugins: { '@typescript-eslint': tseslint.plugin },
	rules: {
		// ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ๋ฉ”์„œ๋“œ๋Š” ๋‚˜๋ž€ํžˆ ๋ฐฐ์น˜ํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/adjacent-overload-signatures': 'error',
		// ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ๊ตฌ์„ฑํ•  ๋•Œ ์„ ์–ธํ• ๋‹น๋ถ€์— ๋ช…์‹œํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/consistent-generic-constructors': 'error',
		// ํƒ€์ž… ์„ ์–ธ ์‹œ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ, interface๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/consistent-type-definitions': 'error',
		// type export ๋ช…์‹œ ๊ฐ•์ œ
		'@typescript-eslint/consistent-type-exports': 'error',
		// export ํ‚ค์›Œ๋“œ ๋’ค์— type ๋ช…์‹œ ๊ฐ•์ œ
		'@typescript-eslint/consistent-type-imports': 'error',
		// return ํƒ€์ž… ๋ช…์‹œ ๊ฐ•์ œ
		'@typescript-eslint/explicit-function-return-type': 'error',
		// ์ ‘๊ทผ์ž ๋ช…์‹œ ๊ฐ•์ œ
		'@typescript-eslint/explicit-member-accessibility': 'error',
		// ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ return ํƒ€์ž… ๋ช…์‹œ ๊ฐ•์ œ
		'@typescript-eslint/explicit-module-boundary-types': 'error',
		// ๋ฉค๋ฒ„ ์ •๋ ฌ ๊ฐ•์ œ
		'@typescript-eslint/member-ordering': 'error',
		// ํƒ€์ž…์˜ ๋ฉ”์„œ๋“œ๋Š” ํ”„๋กœํผํ‹ฐ ํ˜•ํƒœ๋กœ ํ‘œ๊ธฐ
		'@typescript-eslint/method-signature-style': 'error',
		// ๋ฐฐ์—ด์— delete ๋ช…๋ น์–ด ๊ธˆ์ง€
		'@typescript-eslint/no-array-delete': 'error',
		// toString์„ ํ™œ์šฉํ•œ ๋ฌธ์ž์—ด ๋ณ€ํ™˜ ๋ฐฉ์ง€
		'@typescript-eslint/no-base-to-string': 'error',
		// null ๋ณ‘ํ•ฉ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-confusing-non-null-assertion': 'error',
		// ํ˜ผ๋™๋˜๋Š” void ํ‘œํ˜„ ๋ฐฉ์ง€
		'@typescript-eslint/no-confusing-void-expression': 'error',
		// @deprecated ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๊ธˆ์ง€
		'@typescript-eslint/no-deprecated': 'error',
		// ์ค‘๋ณต๋œ enum ๊ฐ’ ๋ฐฉ์ง€
		'@typescript-eslint/no-duplicate-enum-values': 'error',
		// ์ค‘๋ณต๋œ ํƒ€์ž… ํ• ๋‹น ๋ฐฉ์ง€
		'@typescript-eslint/no-duplicate-type-constituents': 'error',
		// any ํƒ€์ž… ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-explicit-any': 'error',
		// non null ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-extra-non-null-assertion': 'error',
		// Promise ๊ฐ์ฒด์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋‹ค๋ฃจ๋„๋ก ๊ตฌ์„ฑ
		'@typescript-eslint/no-floating-promises': 'error',
		// for in ๋ฌธ๋ฒ• ๋ฐฉ์ง€
		'@typescript-eslint/no-for-in-array': 'error',
		// ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ์œ ๋ฐœํ•˜๋Š” import ๋ฐฉ์ง€
		'@typescript-eslint/no-import-type-side-effects': 'error',
		// ์œ ํšจํ•˜์ง€ ์•Š์€ void ํƒ€์ž… ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-invalid-void-type': 'error',
		// ์˜๋ฏธ์—†๋Š” void ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-meaningless-void-operator': 'error',
		// ์˜ค์šฉ๋œ new ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-misused-new': 'error',
		// ์˜ค์šฉ๋œ promise ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-misused-promises': [
			'error',
			{ checksVoidReturn: false }
		],
		// ์˜ค์šฉ๋œ ์Šคํ”„๋ ˆ๋“œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-misused-spread': 'error',
		// !์™€ ?? ์—ฐ์‚ฐ์ž ๋™์‹œ ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
		// ๋ถˆํ•„์š”ํ•œ ํƒ€์ž… ์ค‘๋ณต ์„ ์–ธ ๋ฐฉ์ง€
		'@typescript-eslint/no-redundant-type-constituents': 'error',
		// ๋ถˆํ•„์š”ํ•œ boolean ๋น„๊ต ๋ฐฉ์ง€
		'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
		// ๋ถˆํ•„์š”ํ•œ ์กฐ๊ฑด ๋ฐฉ์ง€
		'@typescript-eslint/no-unnecessary-condition': 'error',
		// ํด๋ž˜์Šค ์ƒ์„ฑ์ž์—์„œ์˜ ๋ถˆํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ ์–ธ ๋ฐฉ์ง€
		'@typescript-eslint/no-unnecessary-parameter-property-assignment': 'error',
		// ๋ถˆํ•„์š”ํ•œ ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ƒ๋žต
		'@typescript-eslint/no-unnecessary-type-arguments': 'error',
		// ์ œ๋„ค๋ฆญ ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜์— ๋ถˆํ•„์š”ํ•œ extends ๋ฐฉ์ง€
		'@typescript-eslint/no-unnecessary-type-constraint': 'error',
		// ๋ถˆ์•ˆ์ „ํ•œ ์„ ์–ธ ๋ณ‘ํ•ฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-unsafe-declaration-merging': 'error',
		// ๋ถˆ์•ˆ์ „ํ•œ enum ๋น„๊ต ๋ฐฉ์ง€
		'@typescript-eslint/no-unsafe-enum-comparison': 'error',
		// ๋ถˆ์•ˆ์ „ํ•œ ํ•จ์ˆ˜ ํƒ€์ž… ๋ฐฉ์ง€
		'@typescript-eslint/no-unsafe-function-type': 'error',
		// ๋ถˆ์•ˆ์ „ํ•œ ๋ฐ˜ํ™˜ ๋ฐฉ์ง€
		'@typescript-eslint/no-unsafe-return': 'error',
		// ๋ถˆ์•ˆ์ „ํ•œ ํƒ€์ž… ํ™•์ธ ๋ฐฉ์ง€
		'@typescript-eslint/no-unsafe-type-assertion': 'error',
		// ๋ถˆํ•„์š”ํ•œ - ์—ฐ์‚ฐ์ž ๋ฐฉ์ง€
		'@typescript-eslint/no-unsafe-unary-minus': 'error',
		// ๋ฏธ์‚ฌ์šฉ ๋ณ€์ˆ˜ ๋ฐฉ์ง€
		'@typescript-eslint/no-unused-vars': 'error',
		// ๋ฌด์˜๋ฏธํ•œ ๋นˆ export ๋ฐฉ์ง€
		'@typescript-eslint/no-useless-empty-export': 'error',
		// ๋ž˜ํผ ๊ฐ์ฒด ํƒ€์ž… ์‚ฌ์šฉ ๋ฐฉ์ง€
		'@typescript-eslint/no-wrapper-object-types': 'error',
		// filter์—์„œ ํ•˜๋‚˜์˜ ๊ฐ’๋งŒ ์ ‘๊ทผํ•  ๊ฒฝ์šฐ, find๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-find': 'error',
		// ์š”์†Œ์˜ ํฌํ•จ ์—ฌ๋ถ€๋Š” includes๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-includes': 'error',
		// enum ๊ฐ’์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-literal-enum-member': 'error',
		// module ์„ ์–ธ ๋Œ€์‹  namespace ํ‚ค์›Œ๋“œ๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-namespace-keyword': 'error',
		// || ๋Œ€์‹  ?? ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ๊ฐ•์ œ
		'@typescript-eslint/prefer-nullish-coalescing': 'error',
		// && ๋Œ€์‹  ?. ๋ฐฉ์‹์œผ๋กœ ๊ฐ’์— ์ ‘๊ทผํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-optional-chain': 'error',
		// reduce ๋ฉ”์„œ๋“œ๊ฐ€ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ๊ฐ€์ง€๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-reduce-type-parameter': 'error',
		// ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ, startsWith, endsWith๋ฅผ ํ™œ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/prefer-string-starts-ends-with': 'error',
		// Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ async ํ‚ค์›Œ๋“œ ๊ฐ•์ œ
		'@typescript-eslint/promise-function-async': 'error',
		// ์ˆซ์ž ๋ฐฐ์—ด์—์„œ ์ •๋ ฌ ์‹œ, ๋น„๊ต ๋ฉ”์„œ๋“œ๋ฅผ ๋„ฃ๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/require-array-sort-compare': 'error',
		// ์ดํ˜•์˜ ํƒ€์ž…์— + ์—ฐ์‚ฐ ๋ฐฉ์ง€
		'@typescript-eslint/restrict-plus-operands': 'error',
		// ํ…œํ”Œ๋ฆฟ ๋ฌธ์ž์—ด์—์„œ ์•ˆ์ „ํ•œ ํƒ€์ž…๋งŒ ํ™œ์šฉ๋˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/restrict-template-expressions': 'error',
		// boolean ๋น„๊ต ์‹œ, ๋ช…์‹œ์  ๋น„๊ต๋งŒ์„ ํ—ˆ์šฉํ•จ
		'@typescript-eslint/strict-boolean-expressions': 'error',
		// ๋ฉ”์„œ๋“œ๋ฅผ ๋ณ€์ˆ˜์— ํ• ๋‹นํ•  ๋•Œ, this๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐ”์ธ๋”ฉ๋˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/unbound-method': 'error',
		// catch์—์„œ ์—๋Ÿฌ๋Š” unknown ํƒ€์ž…์„ ํ™œ์šฉํ•˜๋„๋ก ๊ฐ•์ œ
		'@typescript-eslint/use-unknown-in-catch-callback-variable': 'error',
		// TS๋Š” @typescript-eslint/no-unused-vars ๊ทœ์น™์œผ๋กœ ์ฒ˜๋ฆฌ
		'no-unused-vars': 'off'
	}
};

export default config;

eslint-plugin-unused-imports ์„ค์ •

import pluginUnusedImports from 'eslint-plugin-unused-imports';

import type { Linter } from 'eslint';

export default {
	plugins: { '@unused-imports': pluginUnusedImports },
	rules: { '@unused-imports/no-unused-imports': 'error' }
} satisfies Linter.Config;