모듈 정리하기

김동현·2024년 4월 16일

회고

목록 보기
2/4
post-thumbnail

eslint-plugin-import는 eslint plugin으로 import/export 구문의 Liting을 지원해주고 import 철자가 틀린 것을 찾거나 가져오기 경로를 보장해준다.

최근 새로운 팀원이 합류하고 여러 프로젝트의 import/export 구문의 코드를 작성할 때 규약을 정립하기 위해 해당 Plugin을 사용하기로 했다.

린트

린트(Lint)나 린터(Linter)로 불리는 이 녀석은 컴퓨터 과학 용어로, 소스 코드 내의 프로그래밍 오류, 스타일 오류, 의심스러운 구조 등을 표시하는 데 사용되는 정적 코드 분석 도구이다.

린트는 C언어 소스 코드를 검사하는 유닉스 유틸리티에서 유래했다.
벨 연구소의 컴퓨터 과학자 스티픈 C.존슨이 1987년에 린트(lint)라는 용어를 창안하였다.
C언어 전용으로 작성하던 Yacc 문법을 디버깅하고, 유닉스 운영체제를 32비트 컴퓨터로 이식할 때 발생하던 이식성 문제를 해결하기 위해 린트라는 용어를 생각해냈다고 한다.
자신이 작성한 명령이 의류 건조기의 린트 트랩처럼 작동하여 전체 옷감은 그대로 두고 폐섬유를 포집하는 역할을 기대해 린트라는 단어를 차용해 왔다고 한다.

import/order

일단, npm install eslint-plugin-import --save-dev 명령어를 활용하여 eslint-plugin-import 라이브러리를 설치한다.

이후 .eslintrc.{js|yaml|json}에 코드를 추가해준다.
여기서는 .eslintrc.js를 기준으로 설명한다!

module.exports = {
	//...
  	plugins: ['import'] // 'import' 추가
  	// ...
  	rules: {
	  'import/order': [
            'error',
            {
                groups: [],
                pathGroups: [
                    {
                        pattern: 'SRC/**',
                        group: 'unknown',
                        position: 'before',
                    },
                    {
                        pattern: '**/*.{service,controller,module}',
                        group: 'unknown',
                        position: 'before',
                    },
                    {
                        pattern: '**/*.{type,interface,const,enum}',
                        group: 'unknown',
                        position: 'before',
                    },
                    {
                        pattern: '**/*.dto',
                        group: 'unknown',
                        position: 'before',
                    },
                    //...
                ],
                pathGroupsExcludedImportTypes: [],
                'newlines-between': 'always',
                alphabetize: {
                    order: 'asc',
                    caseInsensitive: true,
                },
            },
        ],
	}
}

위는 .eslintrc.js에 들어가는 import/order의 예시 코드이다.
각의 property들이 어떤 역할을 하는 지 살펴보자

groups: [string]

groups 프로퍼티는 문자열 배열 값을 가져야 한다.
허용되는 문자열은 ["buildtin", "external", "internal", "unknown", "parent", "sibling", "index", "object", "type"]으로 9개의 문자열만 들어올 수 있다.

9개의 문자열이 전부 들어올 필요는 없으며 생략된 유형은 암시적으로 마지막 요소로 함께 그룹화가 이루어진다.

{
	groups: [
      'builtin', // 빌트인 모듈
      ['parent', 'sibling'], // 상대 경로(부모 경로, 자식 경로)
      'index', // index.{js|ts} 파일
      // Then the rest: internal and external type
    ]
}

아래와 같은 예시 코드로 활용할 수 있다.

"import/order": [
  "error",
  {
    "groups": [
      "index",
      "sibling",
      "parent",
      "internal",
      "external",
      "builtin",
      "object",
      "type"
    ]
  }
]

pathGroups: [object]

pathGroups는 기본적으로 제공되는 그룹화가 아닌 경로별로 추가로 그룹화가 가능하게하는 프로퍼티이다.

{
  pathGroups: [
    {
    	pattern: string,			// 이 그룹에 포함될 최소한의 경로
      	patternOptions: object,		// 최소 일치 옵션: default {nocomment: true}
      	group: string,				// groups 내의 그룹을 선택 <- 해당 그룹의 기준으로 위치가 결정된다.
      	position: string,			// group에 정의된 그룹을 기준으로 앞/뒤에 경로한다.
      								// 제공되지 않으면 같은 위치에 그룹화 된다.
    }
  ]
}
{
  pattern: 'SRC/**',    // SRC로 시작하는 모든 경로 ex)SRC/api/... SRC/libs/...
  group: 'unknown',		// 기준 그룹: unknown
  position: 'before',	// unknown 그룹 앞에 그룹화
},

newlines-between: string

import 그룹 간에 새 줄을 적용할지의 여부를 결정하는 프로퍼티입니다.

  1. ignore: 그룹 사이의 새 줄은 무시하고 오류를 표시하지 않는다.
  2. always: 그룹 사이에 새 줄을 삽입하고 그룹 내 새 줄은 허용하지 않는다.
  3. never: 새 줄을 허용하지 않는다.
  4. always-and-inside-groups: 그룹 내, 그룹 사이 새로운 줄을 허용합니다.

alphabetize: {order: string, orderImportKind: string, caseIntensitive: boolean}

import 그룹 내 순서를 알파벳순으로 정렬하도록 한다.

  1. order: asc, desc
  2. orderImportKind: 다양한 import 경로를 정렬합니다.(type/typeof 같은 접두사!)
  3. caseInsensitive: 대소문자 구분(T/F)

📝 더 자세한 설명은 eslint-plugin-import /docs/rules/order.md를 참고하자!

Typescript 절대 경로

{
  "compilerOptions": {
    "paths": {
        "app/*": ["./src/app/*"],
        "config/*": ["./src/app/_config/*"],
        "environment/*": ["./src/environments/*"],
        "shared/*": ["./src/app/_shared/*"],
        "helpers/*": ["./src/helpers/*"],
        "tests/*": ["./src/tests/*"]
    },
}

😀 만약 위와 같이 타입스크립트의 경로 매핑 기능을 사용하고 있다면, 절대 경로 그룹마다 새 줄 추가를 하고 싶을 수 있다.
하지만, 아래와 같이 추가로 그룹화를 해주더라도 각 매핑 경로마다 새 줄 추가가 되지 않고, group에 선언된 특정 그룹에 속해 함께 매핑해 버린다.

pathGroups: [
  {
    pattern: 'app/**',
    group: 'unknown',
    position: 'before',
  },
  {
    pattern: 'config/**',
    group: 'unknown',
    position: 'before',
  },
  {
    pattern: 'environment/**',
    group: 'unknown',
    position: 'before',
  },
  'newlines-between': 'always',
  

eslint-plugin-import Resolvers 탭을 보면 이유가 나와 있다.
모듈 번들러의 출현과 모듈 구문 사양으로 module에서 필요한 기능을 가져 올 때 모듈 뒤에 있는 파일의 위치가 명확하지 않는다고 한다.
v0.10 버전까지는 resolve 플러그 인을 직접 구현하여 사용했지만 웹팩과 타입스크립트의 import 경로 별칭 체계들이 Node가 허용하지 않는 기능들이 있어 두 가지 모두를 지원하기 위해 v0.11부터는 리졸버를 도입했다고 한다.

🔥 이를 해결하기 위해 typescript resolver를 활용하여 타입스크립트에서 제공하는 경로 별칭들도 그룹화 해보자

아래 패키지가 없다면 다운로드 해주자!
1. @typescript-eslint/parser
2. typescript-eslint/eslint-plugin
3. eslint-import-resolver-typescript
4. eslint-plugin-import

1, 2번 라이브러리는 타입스크립트 lint를 더 잘 사용하게 해주는 라이브러리이다. 해당 링크를 참조하자
실제로 경로 resolver를 활용하기 위해서는 eslint-import-resolver-typescript가 필요하다.

추가로 아래와 같이 eslint settings 구문을 추가해 주면 정상적으로 해당 경로 별칭에 대해서도 그룹화가 진행되는 것을 확인할 수 있다!!

// .eslintrc.js
settings: {
  'import/resolver': {					// import 문을 사용 할 때 어떻게 찾을 건지에 대한 설정
      typescript: {
        alwaysTryTypes: true,          // 타입 정의 파일(.d.ts)을 찾는다. 
        project: './tsconfig.json',    // tsconfig.json 파일의 경로
      },
    },
  },

import 구문이 정리되니 무엇보다 보기가 좋아져서 기분이 좋았다 :)

profile
달려보자

0개의 댓글