안녕하세요

eslint error 10가지 원인과 해결 방법 본문

Next13 블로그 프로젝트

eslint error 10가지 원인과 해결 방법

sakuraop 2023. 10. 15. 01:41

목차
1. 수정할 error 목록

2. 수정 내용

1. import/named
원인: 리팩토링 과정에서 type의 경로가 잘못 지정되었다.
해결: 경로를 올바르게 수정.

2. import/order
원인: 규칙에 따라 order가 재정렬 된 모양이 error 조건과 다른 듯 하다.
해결: NavHead 위로 Inter와 ReactNode 객체를 이동시킨다.

3. react/no-danger
원인: 태그에 존재하는 위험한 속성이니 조심해야한다. JS 스크립트를 삽입하는 XSS 공격에 노출될 수 있다.
해결:
1. 규칙을 warning하지 않도록 바꾼다.
2. 해당 라인을 무시하도록 한다.

4. react/jsx-no-useless-fragment
원인: 불필요한 fragment가 존재한다. child를 하나 이상 가지도록 해야한다.
해결: fragment가 부모가 아닌 형태로 만들어 준다.

5. react/no-unused-prop-types
원인: 사용되지 않은 prop-types 가 존재한다.
해결: 해당 props 타입을 조절해준다.

6. @typescript-eslint/no-unused-vars
원인: styles 를 import 해 놓고 사용하지 않았따.
해결: 해당 import 라인을 삭제해주면 된다.

7. consistent-return
원인: 함수가 아무것도 반환하지 않은 경우에 발생. 
해결: return 을 빠뜨렸다면 return; 처럼 뭐가 됐든 함수를 return하여 종료시키도록 한다.

8. jsx-a11y/no-static-element-interactions
원인: div 태그에 onClick 이벤트를 주었다.
해결: onClick 이벤트는 button 태그에 적합하다. div를 button으로 바꾸고, type='button' 을 추가해준다.

9. no-redeclare
원인: 컴포넌트 이름과 타입 이름을 동일하게 작성했다.
해결: 타입 명은 모두 끝에 'Interface'를 붙이는 방식으로 통일한다.

10. import/no-cycle
원인:
=> props 타입을 해당 props를 사용하는 1번 컴포넌트 내에서 생성하고 export 하였다.
=> 2번 컴포넌트에서 해당 props를 import 하였다.
=> 2번 컴포넌트에서 1번 컴포넌트를 사용 중이다.
해결: type.ts 파일을 생성하여 props interface를 컴포넌트 외부로 빼준다.
- 그런데 근본적인 원인은 이게 아니다.
- type 을 어디에 저장하면 좋을지 몰라서 생긴 문제다.
진짜 해결: type 을 어떻게 관리해야하는지 공부해야 한다. 
공부한 관리 방법 대로만 작성하면 이런 문제가 생기지 않을 것이다.


1. 수정할 error 목록

설정을 바꿨더니 수정할 내용이 많다. 하나씩 수정해보자.

./src/app/api/manage/category/route.ts
5:10 Error: CommonCategoryType not found in '@/containers/Editor/PostEditor' import/named

./src/app/layout.tsx
9:1 Error: next/font/google import should occur before import of @/containers/NavHead/NavHead import/order
13:1 Error: react import should occur before import of @/containers/NavHead/NavHead import/order

./src/components/buttons/GoPostCommentButton/GoPostCommentButton.tsx
6:61 Error: 'postId' PropType is defined but prop is never used react/no-unused-prop-types

./src/components/inputs/CustomInputs/CustomInputs.tsx
6:3 Error: 'className' PropType is defined but prop is never used react/no-unused-prop-types
27:3 Error: 'type' PropType is defined but prop is never used react/no-unused-prop-types
44:7 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. jsx-a11y/no-static-element-interactions

./src/containers/CategoryEdit/components/AddMainCategoryForm/AddMainCategoryForm.tsx
9:8 Warning: 'styles' is defined but never used. @typescript-eslint/no-unused-vars

./src/containers/CategoryEdit/components/AddSubCategoryForm/AddSubCategoryForm.tsx
3:10 Error: CommonCategoryType not found in '@/containers/Editor/PostEditor' import/named

./src/containers/Comment/components/CommentForm/CommentForm.tsx
14:7 Error: 'CommentForm' is already defined. no-redeclare
14:7 Error: 'CommentForm' is read-only. no-import-assign
59:63 Error: Expected to return a value at the end of async arrow function. consistent-return

./src/containers/Editor/components/CategorySelector/CategorySelector.tsx
5:1 Error: Dependency cycle detected. import/no-cycle
5:33 Error: SubCategoryType not found in '../../PostEditor' import/named

./src/containers/Editor/PostEditor.tsx
8:1 Error: Dependency cycle via @/services/categoryFetch:3 import/no-cycle
11:1 Error: Dependency cycle detected. import/no-cycle
111:5 Error: Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all. react/jsx-no-useless-fragment

./src/containers/MainPage/components/Slider/Slider.tsx
9:1 Error: Dependency cycle detected. import/no-cycle

./src/containers/MainPage/components/SliderItem/SliderItem.tsx
6:1 Error: Dependency cycle detected. import/no-cycle

./src/containers/ManageLikes/components/ManageLikeItem/ManageLikeItem.tsx
6:1 Error: 'dompurify' should be listed in the project's dependencies. Run 'npm i -S dompurify' to add it import/no-extraneous-dependencies
42:19 Warning: Dangerous property 'dangerouslySetInnerHTML' found react/no-danger

./src/containers/NavHead/components/UserMenu/UserMenu.tsx
36:7 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. jsx-a11y/no-static-element-interactions

./src/hooks/useCategoryList.tsx
3:1 Error: Dependency cycle via @/containers/Editor/PostEditor:1 import/no-cycle

./src/services/categoryFetch.ts
1:1 Error: Dependency cycle via @/hooks/useCategoryList:8 import/no-cycle
1:10 Error: CommonCategoryType not found in '@/containers/Editor/PostEditor' import/named

2. 수정 내용

import/named

./src/app/api/manage/category/route.ts
5:10 Error: CommonCategoryType not found in '@/containers/Editor/PostEditor' import/named

./src/containers/CategoryEdit/components/AddSubCategoryForm/AddSubCategoryForm.tsx
3:10 Error: CommonCategoryType not found in '@/containers/Editor/PostEditor' import/named

./src/containers/Editor/components/CategorySelector/CategorySelector.tsx
5:33 Error: SubCategoryType not found in '../../PostEditor' import/named

1:1 Error: Dependency cycle via @/hooks/useCategoryList:8 import/no-cycle
1:10 Error: CommonCategoryType not found in '@/containers/Editor/PostEditor' import/named

 

원인: 리팩토링 과정에서 type의 경로가 잘못 지정되었다.

 

해결: 경로를 올바르게 수정.

 

import/order

./src/app/layout.tsx
9:1 Error: next/font/google import should occur before import of @/containers/NavHead/NavHead import/order
13:1 Error: react import should occur before import of @/containers/NavHead/NavHead import/order

원인: 규칙에 따라 order가 재정렬 된 모양이 error 조건과 다른 듯 하다.

 

해결: NavHead 위로 Inter와 ReactNode 객체를 이동시킨다.

// 이동 전 error가 발생한 지점
import NavHead from '@/containers/NavHead/NavHead';

import { Inter } from 'next/font/google';

import Footer from '@/containers/Footer/Footer';

import { ReactNode } from 'react';
// 이동 후 error 해결
import { Inter } from 'next/font/google';
import { ReactNode } from 'react';

import NavHead from '@/containers/NavHead/NavHead';

react/no-danger

./src/containers/ManageLikes/components/ManageLikeItem/ManageLikeItem.tsx
43:19  Warning: Dangerous property 'dangerouslySetInnerHTML' found  react/no-danger

원인: 태그에 존재하는 위험한 속성이니 조심해야한다. 스크립트를 삽입하는 XSS 공격에 노출될 수 있다.

 

나는 dangerouslySetInnerHTML을 사용하여야 했기 때문에 XSS에 대비하기 위해서
dompurify의 sanitaizer 함수로 스크립트 실행을 방지하고 있지만, lint는 이를 인식하지 못하고 있다.

위험한 경고는 아닌 셈이다.

 

해결:
1. 규칙을 warning하지 않도록 바꾼다.

// .eslintrc.json

  "rules": {
    "react/no-danger": "off",
  },

- 그러나 다른 사람이 이를 인지하지 못하고 작업을 dangerously 하게 만들 수도 있다.


2. 해당 라인을 무시하도록 한다.

// eslint-disable-next-line

주석을 추가하여 다음 라인의 경고를 무시하도록 할 수 있다.

const SanitizedInnerHTML = ({ contents }: { contents: string }) => {
  return (
    <div
      // eslint-disable-next-line
      dangerouslySetInnerHTML={{
        __html: sanitize(contents.substring(0, 200)),
      }}
    />
  );
};

- html 태그 안에 직접 주석을 넣는 것이 바람직하진 않지만 적어도 나머지는 관리 대상에 포함된다.

react/jsx-no-useless-fragment

./src/containers/Editor/PostEditor.tsx
111:5  Error: Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all.  react/jsx-no-useless-fragment

원인: 불필요한 fragment가 존재한다. child를 하나 이상 가지도록 해야한다.

    <>
      {postId && loading ? ( 
        // ...
      )}
    </>

- 이런 식으로 fragment 내에 곧바로 삼항연산자를 이용하려고 할 경우 나타나는 에러다.

 

해결: fragment가 부모가 아닌 형태로 만들어 준다.

    <div className={styles.container}>
      {postId && loading ? (
        // ... 
      )}
    </div>

 

react/no-unused-prop-types

./src/components/buttons/GoPostCommentButton/GoPostCommentButton.tsx
6:61 Error: 'postId' PropType is defined but prop is never used react/no-unused-prop-types

./src/components/inputs/CustomInputs/CustomInputs.tsx
6:3 Error: 'className' PropType is defined but prop is never used react/no-unused-prop-types
27:3 Error: 'type' PropType is defined but prop is never used react/no-unused-prop-types

원인: 사용되지 않은 prop-types 가 존재한다.

 

해결: 해당 props 타입을 조절해준다.

const GoPostCommentButton = ({ children }: { children: any; postId: string }) => {

// postId 삭제
const GoPostCommentButton = ({ children }: { children: any }) => {

설계했던 기능이 필요한가? 싶어 구현하지 않았었다. 그 잔재인 postId를 제거해주면 된다.

@typescript-eslint/no-unused-vars

./src/containers/CategoryEdit/components/AddMainCategoryForm/AddMainCategoryForm.tsx
9:8  Warning: 'styles' is defined but never used.  @typescript-eslint/no-unused-vars

원인: styles 를 import 해 놓고 사용하지 않았다.

 

해결: 해당 import 라인을 삭제해주면 된다. 

import { CustomInput } from '@/components/inputs/CustomInputs/CustomInputs';
// import styles from './AddMainCategoryForm.module.scss';

어차피 scss는 나중에 사용할 것이기 때문에 주석 처리만 하고 남겨뒀다.

consistent-return

./src/containers/Comment/components/CommentForm/CommentForm.tsx
59:63  Error: Expected to return a value at the end of async arrow function.  consistent-return

원인: 함수가 아무것도 반환하지 않은 경우에 발생. 

 

해결: return 을 빠뜨렸다면 return; 처럼 뭐가 됐든 함수를 return하여 종료시키도록 한다.

setState를 하면 return할 값이 없는 게 맞지만,

함수형 프로그래밍에서는 무언가를 반드시 return 해주는게 원칙이다.

jsx-a11y/no-static-element-interactions

./src/components/inputs/CustomInputs/CustomInputs.tsx
44:7  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. 
 jsx-a11y/no-static-element-interactions

./src/containers/NavHead/components/UserMenu/UserMenu.tsx
36:7  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. 
 jsx-a11y/no-static-element-interactions

원인: div 태그에 onClick 이벤트를 주었다.

      <div className={`${styles.reset} ${value.length && styles.visible}`} onClick={clickInitialize}>
        ❌
      </div>

 

해결: onClick 이벤트는 button 태그에 적합하다. div를 button으로 바꾸고, type='button' 을 추가해준다.

      <button className={`${styles.reset} ${value.length && styles.visible}`} onClick={clickInitialize} type='button'>
        ❌
      </button>

 

no-redeclare

import { CommentFormProps, CommentForm } from '@/types/post';

const CommentForm = ({
  return //...    
})

원인: 컴포넌트 이름과 타입 이름을 동일하게 작성했다.

 

해결: 타입 명은 모두 끝에 'Interface'를 붙이는 방식으로 통일한다.

const CommentForm = (props: CommentFormPropsInterface) => {
  const { title, postId, userEmail, newUpdate, setNewUpdate, postCommentCount, setPostCommentCount } = props;

 

import/no-cycle

원인:

=> props 타입을 해당 props를 사용하는 1번 컴포넌트 내에서 생성하고 export 하였다.

=> 2번 컴포넌트에서 해당 props를 import 하였다.

=> 2번 컴포넌트에서 1번 컴포넌트를 사용 중이다.

 

해결: type.ts 파일을 생성하여 props interface를 컴포넌트 외부로 빼준다.

- 그런데 근본적인 원인은 이게 아니다.

- type 을 어디에 저장하면 좋을지 몰라서 생긴 문제다.

 

진짜 해결: type 을 어떻게 관리해야하는지 공부해야 한다. 

공부한 관리 방법 대로만 작성하면 이런 문제가 생기지 않을 것이다.


상기된 10가지의 eslint 에러 해결을 해보았다.

굿잡