eslint์ prettier
๐ ESLint๋?
Eslint๋ ES์ Lint๊ฐ ํฉ์ณ์ง ๋จ์ด์ ๋๋ค. ES๋ ECMAScript๋ก ์๋ง๋ ์ ์๊ณ ๊ณ์์ง๋ง, Lint๋ ์ฒ์ ๋ดค์์๋ ์๋๋ฐ์. wikipedia์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ํฉ๋๋ค.
๋ฆฐํธ(lint) ๋๋ย ๋ฆฐํฐ(linter)๋ย ์์ค ์ฝ๋ ๋ฅผ ๋ถ์ํ์ฌ ํ๋ก๊ทธ๋จ ์ค๋ฅ,ย ๋ฒ๊ทธ, ์คํ์ผ ์ค๋ฅ, ์์ฌ์ค๋ฌ์ด ๊ตฌ์กฐ์ฒด์ ํ์(flag)๋ฅผ ๋ฌ์๋๊ธฐ ์ํ ๋๊ตฌ๋ค์ ๊ฐ๋ฆฌํจ๋ค.
๋๋์ด ์ฌ๋ฏ ๋ง๋ฏโฆ ๊ตฌ๊ธ์ Lint๋ฅผ ๊ฒ์ํด๋ด ๋๋ค.
์์ ๊ฐ์ด lint roller๊ฐ ๋์ค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ์ค๋๋ ์ค์จํฐ๋ค์ ๋ณด๋ฉด ์ท์ ์์ ธ๋์จ ๋ณดํ๋ผ๊ธฐ๋ฅผ ๋๋ผ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด์ฃ . ์ด๋ป๊ฒ ์ข ๋๋์ด ์ค์๋์? Lint๋ฅผ ๊ฐ๋จํ ์ ์ํ๋ฉด ์ฝ๋ ๊ฐ์ ์ ๋๋ ๋๊ตฌ ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ESLint๋ ์ ์ ์ฝ๋ ๋ถ์๊ธฐ(Static Code Analyzer)๋ก, ํน์ ํ ์ฝ๋ ์คํ์ผ์ ๋ฐ๋ฅด์ง ์๋ ์ฝ๋์ ๋ฌธ๋ฒ์ ์ธ ์๋ฌ๋ฅผ ์ฐพ์์ฃผ๊ณ , ๋ถ๋ถ์ ์ผ๋ก ์์ ๊น์ง ํด์ฃผ๋ ๋๊ตฌ ์ ๋๋ค. ์ ์ ์ฝ๋ ๋ถ์์ด๋ ํ๋ก๊ทธ๋จ ์คํ ์์ด ์ํํธ์จ์ด๋ฅผ ๋ถ์ํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๐ ์ฝ๋ ํ์ ๊ท์น๊ณผ ์ฝ๋ ํ์ง ๊ท์น
๋จผ์ Linting(์ฝ๋ ๊ฐ์ )์๋ ํฌ๊ฒ ๋ ๊ฐ์ง ๋ฒ์ฃผ๊ฐ ์กด์ฌํฉ๋๋ค. ์ฒซ ๋ฒ์งธ๋ ์ฝ๋ ํ์ ๊ท์น์ด๊ณ , ๋ ๋ฒ์งธ๋ ์ฝ๋ ํ์ง ๊ท์น์ ๋๋ค.
์ฝ๋ ํ์ ๊ท์น์ ์ฝ๋์ ํ์์ ๊ดํ ๊ท์น์ ๋๋ค. ๋ค์ฌ์ฐ๊ธฐ์ tab์ด๋ space์ ํผ์ฉ์ ๋ง๋ ESLint์ โno-mixed-spaces-and-tabsโ ๊ท์น์ด ๊ทธ ์์ ๋๋ค.
์ฝ๋ ํ์ง ๊ท์น์ ์ฝ๋ ํ์ง์ ๊ดํ ๊ท์น์ผ๋ก, ๋ฒ๊ทธ๋ฅผ ์ฐพ๊ฑฐ๋ ์๋ฐฉํ ์ ์๋ ๊ท์น๋ค์ ๋๋ค. ๊ฐ๋ น ESLint์ โno-implict-globalsโ๋ ์ ์ญ ์ค์ฝํ์ ๋ณ์ ์ ์ธ์ ๊ธ์งํจ์ผ๋ก์จ ๋ณ์์ ์ถฉ๋์ ์๋ฐฉํ๋ ์ญํ ์ ํ์ฃ .
ESLint๋ ์ฝ๋ ํ์ ๊ท์น๊ณผ ์ฝ๋ ํ์ง ๊ท์น ๋ชจ๋ ๋ค๋ฃจ์ง๋ง, Prettier๋ ์ฝ๋ ํ์ ๊ท์น๋ง ๋ค๋ฃน๋๋ค. ๊ทธ๋ฌ๋ฉด ESLint๋ง ์ฐ๋ฉด ๋์ง ์ Prettier๊น์ง ๊ฐ์ด ์ฐ๋๊ฑธ๊น์?
์ด์ ๋ Prettier๊ฐ ESLint๋ณด๋ค ์ฝ๋ ํ์ ๊ท์น์ ๋ ์ ์ ์ฉํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์กฐ๊ธ ๋ ์์ธํ ๋ด์ฉ์ ์๋์์ ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
๐ ๋ฒ์ธ๋ก EditorConfig๋?
์ฝ๋์ ์ผ๊ด์ฑ์ ์ํด์ ESLint, Prettier ๋ฟ๋ง ์๋๋ผ EditorConfig ์ญ์๋ ๋ง์ด ์ฐ์ ๋๋ค. EditorConfig๋ ์ฝ๋ ํ์ ๊ท์น์ด๋ ์ฝ๋ ํ์ง ๊ท์น์ ๊ด์ฌํ์ง ์์ต๋๋ค. EditorConfig๋ ํ ๋ด์ ์ฌ๋ฌ IDE ํด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ์ฝ๋ ์คํ์ผ ํต์ผ์ด ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์ด์ค๋๋ค
๐ Prettier๊ฐ ESLint๋ณด๋ค ์ฝ๋ ํ์ ๊ท์น์ ์ด๋ป๊ฒ ๋ ์ ์งํฌ๊น?
์๋์ ๊ฐ์ด ์์ ์ฝ๋๋ฅผ ํ๋ ์ค๋นํ์ต๋๋ค.
function printUser(firstName, lastName, number, street, code, city, country) {
console.log(
`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`,
)
}
printUser(
'John',
'Doe',
48,
'998 Primrose Lane',
53718,
'Madison',
'United States of America',
)
ESLint๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํฉ๋๋ค.
{
"extends": ["eslint:recommended"],
"env": {
"es6": true,
"node": true
},
"rules": {
"max-len": ["error", {"code": 80}],
"indent": ["error", 2]
}
}
์ ์ฝ๋์ ์ ์ฉ๋ ๊ท์น๋ค์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- console ๋ฌธ ํ์ฉ ๊ธ์ง ( eslint์์ ์ถ์ฒํ๋ ๊ท์น๋ค์ ํฌํจ๋์ด ์์ต๋๋ค. = eslint:recommended )
- ์ฝ๋ ์ต๋ ๋ฌธ์์ด ๊ธธ์ด 80
- ๋ค์ฌ์ฐ๊ธฐ๋ 2์นธ
๊ทธ๋ฆฌ๊ณ ๋งจ ์ฒ์ ์ฝ๋๋ฅผ ์คํํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
์ฝ๋ ์ต๋ ๋ฌธ์์ด ๊ธธ์ด๊ฐ 80์ ๋์๊ณ , console ๋ฌธ์ด ์กด์ฌํ๋ฉฐ, ๋ค์ฌ์ฐ๊ธฐ๊ฐ ์ ๋๋ก ์๋์ด์๋ค๊ณ error๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์์ต๋๋ค.
ESLint์์ ์ ๊ณตํ๋ ์๋ฌ ์์ ํ๋๊ทธ(โfix)์ ํจ๊ป ESLint๋ฅผ ์คํํ์ ๋๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์ค๊ฒ ๋ฉ๋๋ค.
ESLint๊ฐ max-len๊ณผ console๋ฌธ ์๋ฌ๋ ์์ ํ์ง ๋ชปํ์ง๋ง, ๋ค์ฌ์ฐ๊ธฐ ์๋ฌ๋ ๋ถ๋ถ์ ์ผ๋ก ์์ ํ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ค์ฌ์ฐ๊ธฐ 2์นธ, ์ฝ๋ ์ต๋ ๋ฌธ์์ด ๊ธธ์ด 80์ ๊ท์น์ ์ค์ ํ Prettier๋ฅผ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๊ฐ ์๋ ๋ณํํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
function printUser(firstName, lastName, number, street, code, city, country) {
console.log(
`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`,
)
}
printUser(
'John',
'Doe',
48,
'998 Primrose Lane',
53718,
'Madison',
'United States of America',
)
ESLint๋ ํ์ง ๋ชปํ๋ max-len ์์ ์ด ๊ฐ๋ฅํด์ง๋ ๊ฒ์ด์ฃ . ํ์ง๋ง Prettier๋ ESLint์ฒ๋ผ ์ฝ๋ ํ์ง์ ์ํฅ์ ์ค ์ ์๋ ์ฝ๋๋ค(console.log)์ ๋ํด์ ์ด๋ ํ ๊ฒฝ๊ณ ๋ ๋ณด์ฌ์ฃผ์ง ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฝ๋ ํ์๊ณผ ์ฝ๋ ํ์ง ๋ ๋ค ์ก๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ESLint์ Prettier๋ฅผ ๋์์ ์ฌ์ฉํ๋ ๊ฒ์์ ์ ์ ์์ต๋๋ค.
๋ง์ฝ ESLint์ Prettier, EditorConfig ์ค ํ๋๋ง์ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด, ์ด๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ ํ์ ๋ฌ๋ ค์์ต๋๋ค. ํ์ง๋ง ๋ช ์ฌํ์ธ์. ์์์ ๋ณด์๋ฏ์ด Prettier๋ ์ฝ๋ ํ์ ๊ท์น๋ง์ ์งํฌ ๋ฟ, ํ์ง ๊ท์น์ ์ ๊ณตํ์ง ์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ Prettier๋ฅผ ๋จผ์ ๊ณ ๋ คํ๊ธฐ ๋ณด๋ค๋, ESLint๋ฅผ ๋จผ์ ๊ณ ๋ คํ๋ ๊ฒ์ ์ถ์ฒ ๋๋ฆฝ๋๋ค
๐ ESLint์ Prettier์ ์ถฉ๋
ESLint์ Prettier์๋ ์๋์ ๊ฐ์ด ๊ท์น์ด ์ถฉ๋ํ๋ ๋ถ๋ถ์ด ์กด์ฌํฉ๋๋ค.
์ฐ๋ฆฌ๋ ์ด ์ถฉ๋์ ๋ง์์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ ์ฅํ ๋๋ง๋ค issue์ ์ฌ๋ผ์จ ๋ฌดํ ๋ฅดํ์ ๋น ์ง๊ฒ ๋ฉ๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ Prettier๋ ์ฝ๋ ํ์ ๊ท์น๋ง์, ESlint๋ ์ฝ๋ ํ์ง ๊ท์น๋ง์ ๋ค๋ฃจ๊ฒ ํ๊ฒฝ์ ๊ตฌ์ฑํฉ๋๋ค. ๋ฌผ๋ก ๊ฒน์น๋ ๊ฒ ์ค์ ์ด๋ ํ์ชฝ์ผ๋ก ๋ถ๋ฅํ๊ธฐ ์ ๋งคํ ๊ฒ๋ค๋ ์กด์ฌํ์ง๋ง, ๋๋ฌด ์ธ์ธํ ๊ฒ ๊น์ง๋ ๊ณ ๋ คํ์ง ์์๋ ๊ด์ฐฎ์ต๋๋ค. ์ฐ๋ฆฌ์ ๊ด์ฌ์ฌ๋ ์ค์ง Prettier์ ESLint๊ฐ ์ถฉ๋ ์์ด ํ๋์ ๊ท์น๋ง ๋ค๋ฃจ๋ ๊ฒ ์ ๋๋ค. ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋ง์ด์ฃ .
๐ ESLint์ Prettier์ ์ถฉ๋์ ๋ง๊ธฐ์ํ ๋ฐฉ๋ฒ
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด, ESLint์ Prettier ์ด์ธ์ ๋ค์ ๋ ๊ฐ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ ํ์ํฉ๋๋ค.
- eslint-config-prettier
- eslint-plugin-prettier
ESLint์ Prettier๊ฐ ๊ณต์กดํ๋ ค๋ฉด, ESLint์์ Prettier์ ์ถฉ๋์ด ๋ฐ์ํ๋ ๊ท์น๋ค์ ๋ชจ๋ ๋ฌด๋ ฅํ ์ํค๋ฉด ๋ฉ๋๋ค. ์ด ์ญํ ์ 1๋ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ํํด ์ฃผ๋ ๊ฒ์ด์ฃ .
eslint-config-prettier ์ค์น ํ ESLint๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํฉ๋๋ค.
{
"extends": ["eslint:recommended", "prettier"],
"env": {
"es6": true,
"node": true
}
}
์ค์ํ ๊ฒ์ extends ๋ฐฐ์ด์ ๋์ค ์์๊ฐ, ์ผ์ชฝ ์์์ ์ค์ ๋ด์ฉ ์ค ๊ณ์น๋ ๋ถ๋ถ์ ๋ฎ์ด์ฐ๊ธฐ ๋๋ฌธ์, prettier์๊ฒ ์ฝ๋ ํ์ ๊ท์น ์ ์ฉ์ 100% ์์ํ๋ ค๋ฉด, ๋ฐฐ์ด์ ๋ง์ง๋ง ํญ๋ชฉ์ prettier๋ฅผ ๊ธฐ์ ํด์ผ ํฉ๋๋ค. 1๋ฒ์ ๋ํ ์ค์ ์ ์ฌ๊ธฐ๊น์ง์ ๋๋ค.
์ถ๊ฐ์ ์ผ๋ก, ์ฝ๋ ํ์ ๊ท์น ์ ์ฉ ๋ฐ ์ฝ๋ ํ์ง ๊ท์น ์ ์ฉ์ ์ํด ESLint์ Prettier๋ฅผ ๊ฐ๊ฐ ์คํํ๋ ๊ฒ์ ๋นํจ์จ์ ์ ๋๋ค. ์ด๋ 2๋ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ ํ ๋ฒ์ ์คํ์ผ๋ก ESLint์ Prettier๊ฐ ์ ์ฉ๋๊ฒ ์ค์ ์ด ๊ฐ๋ฅํฉ๋๋ค.
eslint-plugin-prettier ์ค์น ํ ๋ค์๊ณผ ๊ฐ์ด ์ถ๊ฐ์ ์ผ๋ก ์ค์ ํด์ค๋๋ค.
{
"extends": ["eslint:recommended", "prettier"],
"env": {
"es6": true,
"node": true
},
"rules": {
"prettier/prettier": "error"
},
"plugins": [
"prettier"
]
}
๐ ESLint์ Prettier์์ ์ค์ ํ ์ ์๋ ๊ท์น์ ๋ฌด์์ด ์์๊น?
ESLint์ Prettier์ ์ฐจ์ด์ ์ ๋ํด์ ์ง๊ธ๊น์ง ์ค๋ช ํด์๋๋ฐ, ๊ทธ๋ฌ๋ฉด ์ค์ ๊ฐ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ์ฉ ๊ฐ๋ฅํ ๊ท์น์ด ๋ฌด์์ด ์๋์ง ๋๋ต์ ์ผ๋ก ์์๋ด ์๋ค.
ESLint์ ๊ฒฝ์ฐ, ESLint ๊ณต์ ๋ฌธ์์์ ์ถ์ฒํ๋ ์ค์ ๊ท์น๋ค์ ๋ช ๊ฐ ๋ณด์๋ฉด,
// comma-dangle: [2, "never"] ์ค์ ์
// Error๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ,
var foo = {
bar: 'baz',
qux: 'quux', /*error Unexpected trailing comma.*/
}
var arr = [1, 2], /*error Unexpected trailing comma.*/
foo({
bar: 'baz',
qux: 'quux', /*error Unexpected trailing comma.*/
})
// Error๊ฐ ๋ฐ์ํ์ง ์๋ ๊ฒฝ์ฐ,
var foo = {
bar: 'baz',
qux: 'quux'
}
var arr = [1, 2]
foo({
bar: 'baz',
qux: 'quux'
})
// eslint no-dupe-args: 2 ์ค์ ์
function foo(a, b, a) {
/*error Duplicate param 'a'.*/
console.log('which a is it?', a)
}
// eslint no-extra-semi: 2 ์ค์ ์
// Error๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ
var x = 5 /*error Unnecessary semicolon.*/
function foo() {
// code
} /*error Unnecessary semicolon.*/
// Error๊ฐ ๋ฐ์ํ์ง ์๋ ๊ฒฝ์ฐ
var x = 5
var foo = function () {
// code
}
module.exports = {
trailingComma: 'all',
// trailingComma๋ ํํ์ผํ๋ผ๊ณ ๋ถ๋ฆฝ๋๋ค.
// all์ ํ๋ ๊ฒฝ์ฐ, ๊ฐ์ฒด์ ๋ง์ง๋ง ์์ ๋ค์ comma๋ฅผ ์ฝ์
ํฉ๋๋ค.
// const obj = {
// a:1,
// b:2,
// }
// none์ ํ๋ ๊ฒฝ์ฐ, comma๊ฐ ์ฌ๋ผ์ง๋๋ค.
// ํํ์ผํ์ ๋ํด์๋ ์๋์์ ์ถ๊ฐ์ ์ผ๋ก ์ค๋ช
ํ ๋ด์ฉ์ด ์์ต๋๋ค.
bracketSpacing: true,
// true์ธ ๊ฒฝ์ฐ, ์ค๊ดํธ ์ฌ์ด์ ์คํ์ด์ค๋ฅผ ๋ถ์ฌํฉ๋๋ค.
// { foo: bar }
// false์ธ ๊ฒฝ์ฐ, ์ค๊ดํธ ์ฌ์ด์ ์คํ์ด์ค๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
// {for: bar}
arrowParens: 'always',
// 'always'์ธ ๊ฒฝ์ฐ, ํญ์ parenthesis๋ฅผ ํฌํจํฉ๋๋ค.
// (x) => x;
// 'avoid'์ธ ๊ฒฝ์ฐ, ๊ฐ๋ฅํ๋ค๋ฉด parenthesis๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
// x => x;
}
โป trailingComma์ ๋ํด์ (Trailing comma after last line in object)
๋ฒ์ ๊ด๋ฆฌ ํด์ ์ํด ๊ด๋ฆฌ๋๋ ์ฝ๋(version controlled code)๋ผ๋ฉด, trailingComma๋ฅผ ๊ฐ๊ธ์ ์ฝ์ ํฉ๋๋ค. ์ด๋ ๊ฐ์ง ๋ณ๊ฒฝ์ (spurious difference)์ ๋ง๊ธฐ ์ํด์์ธ๋ฐ์. ๋ง์ฝ trailingComma: โnoneโ์ธ ์ํ์์ ์ obj์ ์๋ก์ด ํ๋กํผํฐ๋ฅผ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ, ๋๊ฐ์ ๋ผ์ธ์ด ๋ณ๊ฒฝ๋์๋ค๊ณ ํ๋จํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
// ์๋ก์ด ํ๋กํผํฐ ์ถ๊ฐ ์
const obj = {
a: 1,
b: 2,
}
// ์๋ก์ด ํ๋กํผํฐ ์ถ๊ฐ ํ
const obj = {
a: 1,
b: 2, // ๋ณ๊ฒฝ๋ Line 1
c: 3, // ๋ณ๊ฒฝ๋ Line 2
}
๐ ์ฐธ๊ณ ๋ฌธํ
๋ฆฐํธ(ESLint)์ ํ๋ฆฌํฐ์ด(Prettier)๋ก ํ์ ํ๊ฒฝ ์ธํ ํ๊ธฐ
Why You Should Use ESLint, Prettier & EditorConfig
What Is a Linter? Hereโs a Definition and Quick-Start Guide