๐ Project ํ ๋๋ง๋ค eslint, prettier ์ธํ ์ ๋ชจํธํ๋ ๊ฒ ๋ค์ ํ ๋ฒ ์ ๋ฆฌํด ๋ณด๋ ค๊ณ ํ๋ค.
Create React App์ ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ๋ณ๋์ ์ค์ ์์ด ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ค.
๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ๋ฉด TypeScript๋ฅผ ์ง์ํ๋ ์๋ก์ด ํ๋ก์ ํธ๋ฅผ ์์ฑํ ์ ์๋ค.
npx create-react-app my-app --template typescript
์์ ๋ช
๋ น์ด๋ก ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ฉด ์๋์ผ๋ก tsconfig.json ์ด ์์ฑ๋จ
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
์ ๋ง์ ์ปดํ์ผ๋ฌ ์ต์
์ ๋ณผ ์ ์์
๋ ์ข์ ์ธํ
์ ํ๊ธฐ์ํด ์๋ ๋ถ๋ถ์ ์ง์ด๋ค.
CRA๋ก ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ฉด package.json์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธํ
๋์ด์์
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
npm install eslint --save-dev
npx eslint --init
์์ ๊ฐ์ ์ ์ฐจ๋ฅผ ๋ฐ๋ผ์ฃผ๋ฉด ํ์ํ package๋ค์ด ์ค์น๋๋ค.
.eslintrc.json์ ํ์ธ ํด๋ณด๋ฉด ์๋์ ๊ฐ์ด ์๋์ผ๋ก ์ธํ
๋๋ค.
ํฐ๋ฏธ๋์ ์ด๊ณ ์๋์ ๊ฐ์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ๋ค...!
npx eslint src/* --fix
์๋ฌ๊ฐ ์ค์คํ ๋ฌ๋ค...ใ
ใ
ใ
๋๋ถ๋ถ์ ์๋ฌ๋ ํ์ฅ ํ์ผ ์ค๋ฅ๋ React ์์ฒด์ ์ฌ์ฉ๊ณผ ๊ฐ์ด ์๋ฏธ๊ฐ ์๋ค.
'React' was used before it was defined no-use-before-define
JSX not allowed in files with extension.'tsx' react/jsx-filename-extension
๐ problem: "react/jsx-filename-extension"
๐ sample: JSX not allowed in files with extension '.tsx'
(ํ์ฅ์๊ฐ '.tsx' ์ธ ํ์ผ์์๋ jsx๊ฐ ํ์ฉ๋์ง ์์ต๋๋ค)
๐ solution
eslintrc.json
"rules":{
"react/jsx-filename-extension":["warn",{"extensions":[".tsx"]} }
}
์ถ์ธก jsx๋ฌธ๋ฒ์ .tsx์์๋ ์ฌ์ฉํ ์ ์๋๋ก ํด์ฃผ๋๊ฑฐ ๊ฐ์(?)
๐ problem: "import/no-unresolved"
๐ sample: Unable to resolve path to module './App'
(๋ชจ๋ './App'์ ๋ํ ๊ฒฝ๋ก๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.)
๐ solution
ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ ๋ด์์ ํฐ๋ฏธ๋์ ์ด๊ณ eslint-import-resolver-typescript ํจํค์ง๋ฅผ ์ค์นํ๋ค.
npm install eslint-import-resolver-typescript --save-dev
eslintrc.json์ "settings"๋ฅผ ์ถ๊ฐํ๊ณ ์๋์ ๊ฐ์ด ์ถ๊ฐํด์ค๋ค.
"settings":{
"import/resolver":{
"typescript":{}
}
}
๐ problem: "import/extensions"
๐ sample: Missing file extension 'tsx' from './App'
์๋ ์ด๋ ๊ฒ ๋์ด์๊ธธ๋ App.tsx๋ผ๊ณ ๋ถ์๋๋ฐ ๊ฐ์ ธ์ค๊ธฐ ๊ฒฝ๋ก๋ '.tsx' ํ์ฅ์ผ๋ก ๋๋ ์ ์์ผ๋ 'App.js'๋ฅผ ๊ณ ๋ ค ํ๋ผ๋ค..? js๋ ํ์ฅ์ ๋ถ์ด๋ฉด (lint์๋ฌ ์์ด)๊ฐ์ ธ์์ง!
๐ solution
eslintrc.json์ผ๋ก๊ฐ์ rules์ ์๋์ ๊ฐ์ด ๋ฃ์ด์ค๋ค.
"rules":{
"import/extensions":[
"error",
"ignorePackages",
{
"ts":"never",
"tsx":"never"
}
]
}
๐ problem: "no-undef"
๐ sample:"test" is not defined
๐ solution
eslintrc.json, extends์ ์๋์ ๊ฐ์ด ์ถ๊ฐํด์ค๋ค.
"extends":[
"plugin:@typescript-eslint/recommended"
]
"rules":{
"@typescript-eslint/explicit-function-return-type":[
"error",
{
"allowExpressions":true
}
]
"rules":{
"max-len":["warn",{"code":80}]
}
"plugins":[
"react-hooks"
]
eslintrc.json rules์ ์๋์ ๊ฐ์ด ์ถ๊ฐ
"rules":{
"react-hooks/rules-of-hooks":"error",
"react-hooks/exhaustive-deps":"warn"
}
npm install --save-dev --save-exact prettier
{
"trailingComma":"es5",
"tabWidth":4,
"semi":false,
"singleQuote":true
}
.prettierignore file ์์ฑ!
node_modules
npx prettier --write src/App.tsx
์ฝ๋ ํฌ๋งทํ ์ด ๋๋ค! ์ผํธ^0^
๐ก ๋ฐ์์ ๋ณด๋ฉด ์๊ฒ ์ง๋ง eslint formatting rules์ prettier ๊ฐ ์ถฉ๋ํจ
eslint ์ prettier์ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒฝ์ฐ prettier ๊ท์น๋ง ์ฌ์ฉํ์ฌ ์๋ ์์ ์ ์ค์ ํ๊ณ prettier ๋ฐ linter ๊ท์น์ ๋ชจ๋ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ์ง ์๋๋ค!
ํ๋ก์ ํธ ์ต์์ ๊ฒฝ๋ก์ .vscode ํด๋๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ settings.json ํ์ผ์ ๋ง๋ ๋ค. ๊ทธ๋ฆฌ๊ณ ์๋์ ๋ด์ฉ์ ๋ฃ๋๋ค.
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
์ด๋ ๊ฒ ์ค์ ํด์ฃผ๊ณ ์ ์ฅ์ ํด๋ณด๋ฉด prettierrc์ ์ ์๋ ๋ฃฐ์ ๋ฐ๋ผ ์๋์ผ๋ก ์ฝ๋๊ฐ ํฌ๋งทํ ๋๋ค.
์ธํ
์ ๋ค ํ๊ณ ๋์ ๋๋ค ์คํํ๊ฒ ๋๋ฉด formatting ๋ฃฐ์ ๋ฐ๋ผ ์๋ก ์ถฉ๋์ด ์ผ์ด๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด formatting ๊ท์น์๋ Prettier๋ง ์ฌ์ฉํ๋๋ก ESLint๋ฅผ ์ค์ ํ์ฌ formatting ๊ท์น์ด ์ถฉ๋ ํ์ง ์๋๋ก ํ ์ ์๋ค.
npm install --save-dev eslint-config-prettier
"extends":[
...
"prettier",
"prettier/prettier",
]
npx eslint src/App.tsx ---quiet --fix
ESLint๊ฐ formatting rules์ฌ์ฉ์ ์ค๋จํ ๊ฒ์ ๋ณผ ์ ์๋ค.
npm install --save-dev eslint-plugin-prettier
"extends":[
"plugin:prettier/recommended"
]
note1: ์ด๊ฒ์ ESLint ๋ด์์ Prettier๋ฅผ ์คํํ๋ eslint-plugin-prettier ํ๋ฌ๊ทธ์ธ์์ ์ ๊ณตํ๋ ๊ท์น์ ์ผ ๋ค.
note2: "plugin:prettier/recommended"๊ฐ ์ด๋ฏธ ๋ค์์ ์ํํ๋ฏ๋ก "extends"์์ "prettier"๋ฅผ ์ ๊ฑฐ ํ ์๋ ์๋ค.
npx eslint src/App.tsx --quiet --fix
ESLint๋ formatting rules ๋ก Prettier ๊ตฌ์ฑ์ ์ฌ์ฉํ๋ค.
.vscode/settings.json์ ์๋์ ๊ฐ์ด ์ถ๊ฐํด์ค๋ค.
{
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true,
"eslint.alwaysShowStatus": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
์๋ฃ! ์ด์ ์ ์ฅ๋ ๋ชจ๋ ํ์ผ์ ์ฝ๋๋ฅผ ์์ ํ๊ณ ์๋์ผ๋ก ์์ ๋ ์ ์๋ ESLint + Prettier ๊ท์น์ผ๋ก ํ์์ ์ง์ ํ๋ค.
JavaScript ์ฝ๋์์ ๋ฌธ์ ๋ฅผ ์ฐพ์์ฃผ๊ณ ๊ณ ์ณ์ฃผ๋ ์ ์ ์ฝ๋ ๋ถ์ ๋๊ตฌ์ด๋ค. create-react-app ์ผ๋ก ๋ฆฌ์กํธ ์ฑ์ ์์ฑํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก eslint-config-react-app ์ด๋ผ๋ eslint ์ค์ ์ด ์ธํ ๋์ด์๋ค.
plugin์ ๋ฃฐ์ ์ ์ํ ๊ฒ์ผ๋ก, ์๋ฅผ ๋ค๋ฉด esling-plugin-react๋ ๋ฆฌ์กํธ์ ๊ด๋ จ๋ ๋ฃฐ์ ์ ์ํ ํจํค์ง ์ด๋ค.
{
"plugins":["react"]
}
์ด๋ ๊ฒ๋ง ๋ฃ์ด๋๋ฉด ์๋ฌด๋ฐ ๋์๋ ํ์ง ์๋๋ค. ์ด ์ํ๋ eslint-plugin-react์ ์กด์ฌํ๋ ๋ฃฐ์ ์ฌ์ฉํ ์ ์๊ฒ ๋๊ฒ์ด๋ค! ๋ง์ฝ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ ์ํด์ผํจ
{
"plugins":["react"],
"rules":{
"react/jsx-uses-react":"error",
"react/jsx-uses-vars":"error
}
}
์ด๋ฐ์์ผ๋ก ๋งค๋ฒ ๋ชจ๋ ๋ฃฐ์ ๋ํด ๋ถ์ํ๊ณ ํ์
ํด์ ์ผ์ผํ ์์ฑํ๊ธฐ์๋ ๋๋ฌด ๊ท์ฐฎ๋ค. ๋๋ฌธ์ ๋๋ถ๋ถ์ ํ๋ฌ๊ทธ์ธ์ recommened๋ strict, all ๋ฑ์ ์์ฒด ์ค์ ์ ์ ๊ณตํ๋ค.
eslint-plugin-react์ ๊ฒฝ์ฐ recommended์ all ๋๊ฐ์ง์ config๋ฅผ ์ ๊ณตํ๋๋ฐ ์๋์ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
{
"extends":["plugin:react/recommended"]
}
์์ ์ค์ ์ด ์ด๋ฏธ pluging ์ ์ธ์ ํฌํจํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก eslint์ ์ ์ง ์์๋๋๋ค. ๋๋ถ๋ถ์ recommended config๊ฐ ์ด๋ฌํ ํํ๋ฅผ ์ทจํ๊ณ ์๋ค.
์ด๋ฌํ esling-plugin- ํจํค์ง๋ค์ด๋ ๋ฃฐ๋ค์ ๋ชจ์์ ์ค์ ์ผ๋ก ๋ง๋ ๊ฒ์ด eslint-config- ํจํค์ง๋ค. ์๋ฅผ๋ค๋ฉด, esling-config-airbnb๋ eslint, eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-jsx-a11y์ ๋ฃฐ๋ค์ ์กฐํฉํ ์ค์ ํจํค์ง ์ด๊ณ ์๋์ ๊ฐ์ด ์ ์ํด์ ์ฌ์ฉํ๋ค.
{
"extends":["airbnb"]
}
eslint-plugin- ํจํค์ง์ ์ค์ ์ extends์์ plugin:ํจํค์ง๋ค์/์ค์ ๋ค์์ผ๋ก ์ฌ์ฉํ ์ ์๋๋ฐ eslint-confing- ํจํค์ง์ ์ค์ ์ ๋ฐ๋ก *๋ฅผ ์จ์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค. ํ๋ฌ๊ทธ์ธ ํจํค์ง๋ฅผ plugins์ ๋จ์ถ์ด๋ก ์ฐ๋ ๊ฒ๊ณผ ๋์ผํ๋ค.
์ฝ๋๋ฅผ ๋ถ์ํ๊ธฐ ์ํ ํ์ฑํด, ๊ธฐ๋ณธ๊ฐ์ espree์ด๋ค. ํ์ง๋ง ๋ณดํต js์ํฌ์คํ์ด์ค์์๋ @babel/eslint-parser๋ฅผ ์ฌ์ฉํ๊ณ ts์ํฌ์คํ์ด์ค์ธ ๊ฒฝ์ฐ @typescript-eslint/parser๋ฅผ ์ฌ์ฉํ๋ค.
plugin:@typescript-eslint/recommended๋ฅผ ํฌํจ์ํค๋ฉด @typescript-eslint/parser๊ฐ ์๋์ผ๋ก ํฌํจ๋๋ค.
"parser":"@typescript-eslint/parser"
ESLint๊ฐ formatting rules๋ฅผ ์ฌ์ฉํ๋๊ฒ์ ์ค๋จํ ์ ์๋ค
์ฌ์ ์ ์๋ ์ ์ญ ๋ณ์๋ฅผ ์ ๊ณตํ๋ค. node ํ๊ฒฝ์ธ ์ํฌ์คํ์ด์ค๋ฉด node:true๋ฅผ ์ถ๊ฐํด์ผํ๊ณ , ์น ํ๊ฒฝ์ด๋ผ๋ฉด browser:true, es6:true ๋ฑ์ ์ถ๊ฐํด์ผํ๋ค. files๋ overrides๋ฅผ ์ฌ์ฉํด์ ํ์ผ ํจํด๋จ์๋ก ์ ์ฉํ ์ ์ญ๋ณ์๋ฅผ ๋๋ ์๋ ์๋ค
์ฝ๋๋ฅผ ๋ณด๊ธฐ์ข๊ฒ ํฌ๋งทํ ํด์ค๋ค. ์ค์ ํ์ผ์ ์ ํด๋ ๊ท์น์ผ๋ก ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ์์ ํ ์๋ก ์์ฑํด์ค!
์ฐธ๊ณ
https://yrnana.dev/post/2021-09-02-eslint
https://javascript.plainenglish.io/setting-eslint-and-prettier-on-a-react-typescript-project-2021-22993565edf9
๋๋ถ์ ์ ์ธํ ํ์ด์~
๊ฐ์ฌํฉ๋๋ค ใ ใ