yarn add -dev prettier
상세내용:
💡 잠깐) npx?
npx를 통한 패키지 다운: https://velog.io/@pjh1011409/npm-yarn
루트 디렉토리에 .prettierrc 파일 생성 후 컨벤션 코드 작성
✏️ .prettierrc
{
"singleQuote": true, // 쌍따옴표가 아닌 홑따옴표를 사용
"semi": true, // statement 마지막에 세미콜론을 찍음
"useTabs": false, // 탭을 사용하지 않고 스페이스를 사용
"tabWidth": 2, // 탭을 할 경우 2 스페이스
"trailingComma": "all", // 선호되는 한 줄의 길이, 줄바꿈 한폭 길이
"printWidth": 120, // 여러줄로 나뉘었을 때는 쉼표를 사용
"arrowParens": "avoid", // 화살표 함수에서 괄호 사용 의무화
"endOfLine": "auto" // 파일의 마지막에는 EOL을 보장
}
✏️ 직접 실행
npx prettier --write "파일.js"
✏️ 자동 실행
1. 설정에 prettier를 검색하고, setting.json을 연다.
2. setting.json 마지막에 다음과 같은 코드 추가
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true
또는 직접 설정 창에서 다음과 같이 선택
앞서 설정한 과정으로 코드 작성 후 저장(command + S) 시 자동으로 prettier의 조건에 따라 코드 정렬이되는 것을 확인 가능
자바스크립트에서 문법적 에러를 표시해주는 도구
yarn add -D eslint
추가 설치
yarn add -D eslint-plugin-import
yarn add -D eslint-config-airbnb-base
yarn add -D eslint-config-prettier eslint-plugin-prettier
간편하게 설치 : https://wookgu.tistory.com/31
💡 잠깐) npx?
npx를 통한 패키지 다운: https://velog.io/@pjh1011409/npm-yarn
✏️ .eslintrc.json
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"plugin:react/recommended",
"airbnb"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
}
}
코드 설명
- env: 프로젝트의 사용 환경이다.
- parserOptions: 자바스크립트 버전, 모듈 사용 여부 등을 설정할 수 있다.
- extends: 확장 설정을 넣어준다. airbnb사의 코딩 스타일을 따르며 prettier을 반영하도록 설정했다.
- rules extends와 plugins에 대한 세부 설정을 변경하는 코드를 넣을 수 있다. 값을 0으로 주면 에러 검출을 하지 않고, 1로 주면 경고, 2로 주면 에러를 표시한다. 상세한 룰은 문서 에 담겨져 있다.
✏️ 직접 실행
npx eslint ./src/파일.js
✏️ 간편 실행
1. package.json의 scripts옵션에 eslint . 추가
"scripts": {
...
"lint": "eslint ." // 프로젝트 전체 파일을 상대로 ESLint 실행
},
2. lint가 불필요한 것들을 .eslintrc.json의 ignorePatterns에 추가
-> ex) node_modules 디렉터리의 경우 외부 라이브러리의 소스 코드를 담고 있기 때문에 린트를 해줄 필요가 없기 때문
{
...
"ignorePatterns": ["build", "dist", "public"]
}
✏️ 실행 시 다음과 같이 lint가 에러를 발생시킨다.
✏️ -- fix 옵션을 붙여서 실행하면 자동으로 코드를 수정
npx eslint src/pages/App.js --fix
✏️ import과 같은 코드에 대한 오류가 발생하면
ESLint는 코드의 에러와 문제점을 검사하였고, Prettier는 코드의 모습을 깔끔하고 가독성이 좋게 만든다.
ESLint는 코드의 구조을 바꿔주는 기능도 포함하지만, 오류 검출을 하는 것에 특화되어 사용해서 Prettier가 이를 해결한다. 하지만, 앞서 말한 것과 같이 ESLint에도 코드의 구조에 대한 기능도 포함되어있다보니 Prettier의 설정과 충돌이 일어날수 있다. 🥲
간단한 예로는 Prettier에서는 들여쓰기를 2칸으로 설정하였는데, ESLint의 포맷팅 기능으로는 들여쓰기 2칸이 오류라고 생각할 수도 있는 것이다.
따라서 이 둘을 통합하는 방법을 알아보자!😃
✏️ 둘의 충돌을 막기 위한 패키지를 설치.
eslint-config-prettier
eslint-plugin-prettier
yarn add --dev eslint-config-prettier eslint-config-prettier
✏️ .eslintrc의 extends 옵션에 다음과 같은 코드 추가.
만약, 다른 값들이 존재할 경우 기존 설정보다 우선하기 위해서는 배열 내의 맨뒤에 위치시킨다.
[...,...,"plugin:prettier/recommended"]
✏️ 따라서, 파일 저장 시 prettier에 의한 formatting이 진행되고, eslint 실행 시 prettier의 rule을 감안하여 오류를 검출하게 된다.
💡 잠깐) prettier & ESLint의 실행 ~ 수정
현재 내가 formatter와 linter를 실행하는 방법은 다음과 같다.
- editor.formatOnSave의 true 설정으로 파일 저장 시 prettier가 실행 및 자동 수정
- npx eslint 파일명.js로 ESlint 실행.
- npx eslint 파일명.js --fix를 통해 오류가 있는 코드가 자동 수정.
이 방법의 경우 eslint-plugin-prettier를 사용하지 않은 경우다. 이 패키지는 ESLint 실행 시 자동으로 prettier의 formatting까지 진행되는 것이지만, 나는 파일 저장 시 formatting이 자동으로 수정되게 설정하였기 때문이다.
만약 위와 같은 순서의 방법이 귀찮다면 eslint-plugin-prettier를 사용할 수는 있다.
setting.json에 다음과 같이 코드를 작성한다."editor.codeActionsOnSave": { "source.fixAll.eslint": true, }, "editor.formatOnSave": false,
즉, Prettier에 대한 formatting에 대한 자동수정은 false로 설정하고, ESLint에 대한 codeAction을 true로 하는 것이다. 앞서 말했듯이 ESLint 실행 시 Prettier도 자동 실행되기 때문에 가능한 설정이다. 하지만, 이럴 경우 Lint와 formatting에 대한 오류가 모두 발생하고 속도가 지연되는 단점이 있으므로 그냥 내가 하던 방법으로 사용하자! 😃
프로젝트 진행 시 앞서 설정한 lint를 수행하게 되는데, lint가 모두 완료되지 않은 상태로 commit을 할 수 있는 상황이 발생할 수 있다. 이는 협업을 할 경우에는 문제점이 될 수 있다.
따라서, commit을 할 경우 ESLint를 강제로 실행시켜 만약 문제가 있다면 repository에 유입되는 것을 차단하는 방법을 사용해보자.
yarn add -dev lint-staged husky
package.json에 다음과 같은 설정 추가
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
Prettier의 경우 파일 저장 시 자동 수정이 되므로 해결되었고, ESLint의 경우도 --fix의 기능으로 코드 수정이 가능하다. 하지만 그래도 발생하는 에러들이 있다. 그 에러들에 알아보고, ESLint의 에러에는 어떤 것들이 있는지 알아보자!
✏️ error JSX not allowed in files with extension '.js'
➡️ js파일에 JSX를 작성하는 것에 대한 에러를 해결하기 위해 rules에 다음과 같은 코드 추가
"rules": {
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
}
✏️ error 'React' must be in scope when using JSX
➡️ JSX는 자바스크립트 문법의 확장으로 ECMA 표준이 아니다. 따라서 바벨과 같은 툴로 JSX를 자바스크립트로 변환해야 한다. 바벨이 JSX가 사용되었음을 알고 이를 변환하도록 하기 위해서는 React를 import해주어야 한다ㅣ
import React from 'react';
✏️ error Unable to resolve path to module
➡️ 다음과 같이 rules에 추가한다. 정확히는 import로 가져온 파일 혹은 모듈이 unresolved가 되지 않도록(no)하는 옵션이다. 절대경로를 사용하기 때문에 발생한 것 같은데 그에 대한 것을 off로 설정하면 에러가 사라진다.
"rules": {
...
"import/no-unresolved": "off"
},
✏️ error img elements must have an alt prop, either with meaningful text, or an empty string for decorative images
➡️ img태그의 alt값을 작성하지 않아서 일어나는 에러
<img alt="추가" src={...}/>
✏️ error '
can be escaped with '
, ‘
, '
, ’
➡️ HTML에 특수문자를 작성할 시 대체할 문자가 필요하다.
변경 전
site's personal blog
변경 후
site ' s personal blog
HTML 특수문자 리스트: http://kor.pe.kr/util/4/charmap2.htm
✏️ error Missing an explicit type attribute for button
➡️ button의 타입에는 세가지(submit,reset,button)가 존재한다. 만약 값을 지정하지 않으면 기본값으로 sumit이 설정된다. 따라서, form 태그로 감싸져 있는 button태그의 타입을 지정하지 않으면 자동적으로 submit 기능이 실행되버린다. 따라서, button태그라 하더라도 반드시 type을 작성해주자.
<button type="button" className={styles.signUpBtn}>버튼</button>
button 타입별 좋은 예시: https://nykim.work/96
✏️ error Arrow function should not return assignment
➡️ JS는 거의 모든 시점에서 할당이 가능하기 때문에, 특히 return문 사용 시 더 그렇다.
= 의미가 모호성을 가지기 때문에 ===을 작성하여 확실성을 준다.
변경 전
<div ref={el => (NavRef.current[0] = el)}>
변경 후
<div ref={el => NavRef.current[0] === el}>
no-return-assign: https://runebook.dev/ko/docs/eslint/rules/no-return-assign
✏️ error Missing radix parameter
➡️ 문자열을 정수로 변환할 때 parseInt의 두번째 파라미터에 radix(진수) 표시를 한다.
변경 전
const postId = parseInt(id);
변경 후
const postId = parseInt(id, 10);
✏️ warning Unexpected confirm, alert
➡️ 대력적 이유 : https://eslint.org/docs/latest/rules/no-alert
변경 전
if (window.confirm('삭제하시겠습니까?')) dispatch(deletePost(data.id));
alert('삭제되었습니다');
변경 후
// eslint-disable-next-line no-alert
if (window.confirm('삭제하시겠습니까?')) dispatch(deletePost(data.id));
// eslint-disable-next-line no-alert
alert('삭제되었습니다');
ESLint 비활성화: https://ahnanne.tistory.com/18
✏️ assigned a value but never used
➡️ state 변수를 선언하였고 그 변수를 할당하는 코드는 있지만, setState는 선언만 되었고 사용되지 않았다. ESLint는 이 것을 보고 선언만 되고 사용되지 않고 있다는 에러를 보인다. 따라서, 해당 코드에 대해서만 에러를 없애는 코드를 작성할 수 있고, .eslintrc.json의 rules에 off값을 줄 수도 있다.
방법1)
// eslint-disable-line no unused-vars
const [limit, setLimit] = useState();
방법2)
"rules": {
...
"no-unused-vars": "off"
},
✏️ error Default parameters should be last
➡️ ESLint에서 rule의 경우 두 매개 변수에 기본값을 지정하거나 특정 줄에 대한 규칙을 비활성화해야 함을 의미한다. rule에 따르면 ({type, payload}, state = initialState) 과 같이 기본 매개변수를 마지막에 넣으면 함수 호출에서 선택적 꼬리 인수를 생략할 수 있다고 한다.
하지만, 이 에러를 무시하고 싶다면 .enlintrc.json의 rules에 다음과 같이 추가자.
"default-param-last": 0
상세내용: https://eslint.org/docs/latest/rules/default-param-last
✏️ error Unexpected lexical declaration in case block
➡️ ESLint에서는 변수가 현재 범위 외부에 있기 때문에 권장하지 않으므로 {}를 통해 블록 범위를 만들면 해결이 가능하다.
...
case DELETE_POST: {
const inputData = [...state.inputData];
const dataIndex = inputData.findIndex(post => post.id === action.payload);
inputData.splice(dataIndex, 1);
return { ...state, inputData };
}
✏️ error Identifier 'ㅁㅁㅁ' is not in camel case
➡️ 변수 이름 지정과 관련하여서는 camelCase로 작성하는 rule을 갖는다
변경전) import jwt_decode from 'jwt-decode';
변경전) import jwtDecode from 'jwt-decode';
✏️ error objectpassed as the value prop to the Context provider changes every render. To fix this consider wrapping it in a useMemo hook
➡️ context provider의 value로 전달될 object가 렌더링될때마다 변경됨으로 useMemo 후크로 래핑하면 해결
const contextData = useMemo(
{
user,
authTokens,
setAuthTokens,
setUser,
loginUser,
logoutUser,
},
[user, authTokens, loginUser, logoutUser],
);
✏️error 'children' is missing in props validation
➡️ children에 대한 값이 어떤 타입을 가지는지 모르기 때문에 ESLint의 유효성검사를 통해 에러가 발생.
따라서, 두가지 방법으로 해결 가능.
1. prop-types라는 패키지를 설치하고 props의 타입을 명시해주느 것이다.
import {PropsTypes} from 'prop-types'
const AuthContext = ({children}) => {
return(<div>{children}</div>)
}
AuthContext.propsTypes = {
children: PropTypes.node.isRequired,
};
Prop Type의 종류: https://haerim95.tistory.com/41
"rules":{
...
"react/prop-types": "off"
}
✏️ error Visible, non-interactive elements with click handlers must have at least one keyboard listener
✏️ error Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element
➡️ HTML의 요소들에는 각각 고유한 역할이있다. a태그는 링크, img태그는 이미지를 삽입하는 것처럼 역할이 있는데 햄버거 토글바를 동적인 버튼으로 반들기 위해 각각 세개의 줄을 div태그로 만들어서 구현하였다. 따라서, div의 역할에 대한 맞지 않는 역할을 갖고 있어서 발생하는 에러인 것 같다. 따라서 그러한 마크엄 요소의 의미를 제거하고 내용만을 전달하는 속성(persentation)을 작성하여서 해결하였다.
<div
role="presentation"
className={!menuToggle ? `${styles.burgerMenu}` : `${styles.xMenu}`}
onClick={menuToggleHandler}
>
...
</div>
위의 에러코드를 해석하면 다음과 같다.
'클릭 핸들러가 있는 표시되는 비대화형 요소에는 하나 이상의 키보드 리스너가 있어야 합니다.'
'기본이 아닌 대화형 요소를 피하십시오. 기본 HTML을 사용할 수 없는 경우 대화형 콘텐츠 요소에 탭 이동, 마우스, 키보드 및 터치 입력에 대한 적절한 역할 및 지원을 추가합니다.'
presentation의 역할: https://heewon26.tistory.com/82
참고자료: https://inswave.com/confluence/pages/viewpage.action?pageId=19076563
✏️ error Must use destructuring props assignment
➡️ 반드시 props를 비구조화 할당하라는 뜻이다. 다른 컴포넌트로 받아온 props를 비구조화 할당하여 받아오는 것이다.
변경전)
function Header(props) {
const onClickNav = i => {
props.handleIndexClick(i);
};
};
변경후)
function Header({handleIndexClick}) {
const onClickNav = i => {
handleIndexClick(i);
};
};
또는
function Header(props) {
const {handleIndexClick} =. props;
const onClickNav = i => {
handleIndexClick(i);
};
};
구조분해할당: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
✏️ error Prop spreading is forbidden
➡️ props를 컴포넌트에 전달할 때 props 객체의 압축을 풀기 위해 spread 구문을 사용할 때 에러가 발생한다. 이 경우에는 에러를 비활성화하는 것이 제일 낫다.
rules: {
...
"react/jsx-props-no-spreading": "off",
}
✏️ Because the unary ++ and -- operators are subject to automatic semicolon insertion, differences in whitespace can change semantics of source code.
➡️ 단항 ++ 및 -- 연산자는 자동 세미콜론 삽입의 대상이 되기 때문에 공백의 차이는 소스 코드의 의미를 변경할 수 있기 때문에 다음과 같이 변경.
변경전)
for (let i = 0; i < cookies.length; i ++) {
변경후)
for (let i = 0; i < cookies.length; i += 1) {
참조