ESLint로 import 순서 규칙 만들기

이종경·2024년 9월 8일
0
post-thumbnail

저는 선호하는 import 순서가 있습니다.
그 규칙은 다음과 같습니다.

1. 외부 라이브러리와 로컬에서의 작업은 공백(`\n`)으로 구분됩니다.
2. 외부 라이브러리를 작성할 땐, react와 tanstack이 가장 우선적으로 import 되어야합니다.
3. 로컬에서의 작업은 다음과 같은 순서로 import합니다.
  1. store
  2. context
  3. hook
  4. component
  5. util
  6. type
  7. constant
  8. asset
  9. etc

위 규칙을 적용하기 위해 eslint-plugin-import를 통해 설정을 해보았고 각 설정을 정리하고자 합니다.

eslint-plugin-import 설치 및 적용

설치

본인이 사용중인 패키지 매니저를 통해 esint-plugin-import를 설치해줍니다.

# npm
npm install -D eslint-plugin-import
# or yarn
yarn add -D eslint-plugin-import
# or pnpm
pnpm add -D eslint-plugin-import

적용

적용하는 방법은 다음과 같습니다.
eslint 설정 파일을 생성한 후 다음과 같이 작성합니다.
저희는 eslint.config.cjs를 사용하였고 다음과 같이 작성했습니다.

module.exports = {
  // ... 기타 설정
  plugins: ['import'],
}

규칙 설정하기

import와 소스코드 구분하기

해당 설정을 통해 import와 소스코드를 구분할 수 있습니다.

module.exports = {
  rules: {
    'import/newline-after-import': 'error',
    // ... 기타 규칙
  },
}

별칭(alias) 사용 강제하기

별칭을 사용하여 import를 하고 있다면 다음과 같은 규칙을 추가하여 상대 경로 import를 막음과 동시에 별칭 사용을 강제할 수 있습니다.

module.exports = {
  rules: {
    'no-restricted-imports': [
      'error',
      {
        patterns: ['./*'],
      },
    ],
    // ... 기타 규칙
  },
}

import 순서 강제하기

우선, 외장 라이브러리, 로컬 환경의 순서를 설정하기 위해 다음과 같이 규칙을 추가합니다.

module.exports = {
  rules: {
    'import/order': [
      'error',
      {
        groups: ['builtin', 'external', 'internal'],
      },
    ],
    // ... 기타 규칙
  },
}

groups를 통해 import 순서를 정할 수 있으며 builtin, external, internal은 각각 node 내장 라이브러리, 외부 라이브러리, 로컬 환경의 작업물을 의미합니다. 이외에도 다른 설정들이 있지만, 제일 많이 사용되는 세 설정만 작성하였습니다.

라이브러리별 순서 설정하기

이 부분이 가장 중요한 설정으로, 해당 설정을 통해 라이브러리별로 순서를 지정해줄 수 있습니다.

module.exports = {
  rules: {
    'import/order': [
      'error',
      {
		// ...
        pathGroups: [
          {
            pattern: 'react',
            group: 'external',
            position: 'before',
          },
          {
            pattern: '@tanstack/**',
            group: 'external',
            position: 'before',
          },
        ],
        pathGroupsExcludedImportTypes: ["builtin"],
        warnOnUnassignedImports: true,
      },
    ],
    // ... 기타 규칙
  },
}

pathGroups

property가 의미하는 바는 다음과 같습니다.

  • pattern: 라이브러리의 경로 형태를 정의합니다. 와일드카드를 작성하여 형태를 정의할 수 있습니다.
  • group: 해당 패턴의 그룹을 작성합니다.
  • postion: 해당 패턴의 그룹에서 상대적으로 어느 위치에 작성되어야할지 설정합니다.

예를 들어 위의 설정은 다음과 같은 차이를 보입니다.
규칙과 다른 경우

import { useState } from 'react';
import axios from 'axios';
import { useQuery } from '@tanstack/react-query' // axios보다 이전에 위치해야하며, react 다음에 위치해야합니다.

규칙에 맞는 경우

import { useState } from 'react';
import { useQuery } from '@tanstack/react-query'
import axios from 'axios';

pathGroupsExcludedImportTypes

해당 설정을 통해 특정 그룹이 pathGroups로 설정한 규칙을 따르지 않도록 합니다.

warnOnUnassignedImports

해당 설정을 통해 할당되지 않은 import가 순서에 맞지 않을 때 경고합니다.
예를 들어 다음과 같은 순서가 불가능 합니다.

import { useState } from 'react';
import './styles.css'; // useQuery보다 아래에 위치해야 합니다.
import axios from 'axios';

공백을 통한 라이브러리 그룹별 구분하기

module.exports = {
  // ...
  rules: {
    'import/order': [
      'error',
      {
        'newlines-between': 'always',
        distinctGroup: false,
      },
    ],
    // ... 기타 규칙
  },
}

newlines-between

각 라이브러리를 공백(\n)을 통해 구분하도록 합니다.

distinctGroup

정말 중요한 설정으로, eslint-plugin-import는 pathGroups을 생성하면 아예 새로운 group이 생성되었다고 판단하여 각각 다른 그룹으로 구분합니다.

예를 들어, pathGroups를 통해 react의 순서를 정의함과 동시에 react라는 독자적인 그룹이 생성이 되었다고 인식합니다.

따라서 해당 인식을 방지하기 위해 distinctGroup을 false로 설정합니다.

전체 코드

module.exports = {
  ...
  plugins: ['import'],
  rules: {
    'no-restricted-imports': [
      'error',
      {
        patterns: ['./*'],
      },
    ],
    'import/newline-after-import': 'error',
    'import/order': [
      'error',
      {
        groups: ['builtin', 'external', 'internal'],
        pathGroups: [
          {
            pattern: 'react',
            group: 'external',
            position: 'before',
          },
          {
            pattern: '@tanstack/**',
            group: 'external',
            position: 'before',
          },
          {
            pattern: '@/stores/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/contexts/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/hooks/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/components/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/utils/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/types/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/constants/**',
            group: 'internal',
            position: 'before',
          },
          {
            pattern: '@/assets/**',
            group: 'internal',
            position: 'before',
          },
        ],
        pathGroupsExcludedImportTypes: ['builtin'],
        warnOnUnassignedImports: true,
        'newlines-between': 'always',
        distinctGroup: false,
      },
    ],
  },
}
profile
작은 성취들이 모여 큰 결과를 만든다고 믿으며, 꾸준함을 바탕으로 개발 역량을 키워가고 있습니다

0개의 댓글