포스트

파일 import 통제하기

파일 import 통제하기

순서까지 통제해야 하는가?

1) git diff 오염

“이전 커밋과 현재 코드의 달라진 부분”을 보여주는 기능.
파일 수정과 무관한 import 순서만 바뀌어도 diff가 덩어리째 변해 중요하지 않은 부분이 강조됨.

git diff noise

이 불필요한 노이즈 변경이 히스토리를 더럽힘


2) merge conflict 발생

두 개발자가 같은 파일을 다른 시점에 수정하는 상황.

  • A는 상단 import를 하나 추가했음
  • B는 같은 상단에서 import 순서를 살짝 손봤음

이렇게만 해도 Git은 “같은 라인 또는 인접한 라인을 동시에 고쳤다”고 판단하고 conflict를 만들어버린다.


3) 일단 마음이 편하다

1
2
3
4
5
6
7
import { Something } from '../../shared/Something';
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@/components/Button';
import { useState } from 'react';
import { COLUMNS } from "../constants/table";
import { helper } from '../utils/helper';
1
2
3
4
5
6
7
8
9
import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';

import { Something } from '@/shared/Something';
import { helper } from '@/utils/helper';
import { COLUMNS } from "@/constants/table";

import { Button } from '@/components/Button';


적용

1) 절대 경로 적용

import 경로를 @/~형태로 통일하는 방법.

- Vite 설정: 실제 빌드/개발 서버에서 경로 해석

1
2
3
4
5
6
7
8
9
10
11
12
13
// vite.config.ts
import react from "@vitejs/plugin-react";
import path from "path";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

- TypeScript 설정: IDE 자동완성 및 타입 체크

1
2
3
4
5
6
7
8
9
10
// tsconfig.app.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src"]
}


2) import 순서 제어

- 패키지 설치

1
pnpm add -D eslint-plugin-simple-import-sort eslint-plugin-import

eslint-plugin-simple-import-sort: eslint-plugin-import보다 단순하고 강제력 강한 자동정렬.
설정은 최소로 하고 자동 정렬은 강력해야 하는 경우 simple-import-sort가 더 유리하다.

- eslint config 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// eslint.config.ts
// ...
import simpleImportSort from 'eslint-plugin-simple-import-sort'
import importPlugin from 'eslint-plugin-import'

export default defineConfig([
  globalIgnores(['dist']),
  {
    // ...
    plugins: {
      'simple-import-sort': simpleImportSort,
      'import': importPlugin,
    },
    rules: {
        "simple-import-sort/imports": [
        "error",
        {
          groups: [
            // 1. side effect import
            ["^\\u0000"],

            // 2. React & 외부 라이브러리
            ["^react", "^@?\\w"],

            // 3. 내부 핵심 모듈 (의존성 계층)
            [
              "^@/types(/|$)",
              "^@/(constants)(/|$)",
              "^@/(lib)(/|$)",
              "^@/(utils)(/|$)",
              "^@/(api)(/|$)",
              "^@/(stores)(/|$)",
              "^@/(hooks)(/|$)",
            ],

            // 4. 컴포넌트 + 기타 내부 모듈
            ["^@/components(/|$)", "^@/app(/|$)"],

            // 5. 상대 경로
            [
              "^\\.\\.(?!/?$)",
              "^\\.\\./?$",
              "^\\./(?=.*/)(?!/?$)",
              "^\\.(?!/?$)",
              "^\\./?$",
            ],

            // 6. 스타일 & Assets
            [
              "^@/styles(/|$)",
              "^.+\\.(css|s[ac]ss)$",
              "^@/(assets)(/|$)",
              "^.+\\.(svg|png|jpe?g|gif|webp)(\\?react)?$",
            ],

            // 7. 테스트 & 스토리북 & 모의 데이터
            [
              "^@/test-utils(/|$)",
              "^@/storybook(/|$)",
              "^@/mocks(/|$)",
              "^.+\\.(stories|story)\\.[tj]sx?$",
            ],
          ],
        },
      ],
      "simple-import-sort/exports": "error", // export 구문도 알파벳 순으로 정렬
      "import/first": "error", // import 구문을 파일 최상단에
      "import/newline-after-import": "error", // import 블록 다음은 빈 줄
      "import/no-duplicates": "error", // import 구문은 중복되지 않음
    },
  },
])

순서를 정하는 규칙에 대해서는 후술한다.

- IDE 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}

이후 formatOnSave(저장 시에 lint 규격 적용)가 동작한다.

- 전체 파일에 일괄 적용

1
pnpm lint --fix


3) 순서를 정하는 규칙

simple-import-sort가 그룹 안에서 알파벳까지 자동 정렬을 수행하기 때문에,
이 import 구문이 어느 그룹에 속하는지만 정하면 된다.

핵심 1: 멀리 있는 것 → 가까운 것 → 사소한 것
핵심 2: 의존성이 적은 것 → 많은 것

변경이 발생해도 import 블록의 하단부에서 발생하는게 유리하다.
따라서 의존성이 많은 것을 하단부에 위치시킨다.

예시 1: 라이브러리(외부 세계) → 우리 앱의 큰 구조(alias) → 스타일/이미지(꾸밈 요소)
예시 2: 상수 → 유틸리티 → 외부 통신 → 상태 관리 → React 계층

특이사항은, 타입도 코어 의존성의 한 부분으로 취급하여 ^@/types(/|$)로 정렬을 수행하는 구조.
타입 정의가 프로젝트에서 꽤 중요한 역할을 한다면 별도로 관리하는 것도 괜찮을 듯.


이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.