An open API service indexing awesome lists of open source software.

https://github.com/effozen/eslint-plugin-fsd-lint

ESLint plugin for enforcing Feature-Sliced Design architecture
https://github.com/effozen/eslint-plugin-fsd-lint

eslint feature-sliced-design fsd lint plugin

Last synced: 10 months ago
JSON representation

ESLint plugin for enforcing Feature-Sliced Design architecture

Awesome Lists containing this project

README

          

# πŸš€ eslint-plugin-fsd-lint πŸš€

[![npm version](https://img.shields.io/npm/v/eslint-plugin-fsd-lint)](https://www.npmjs.com/package/eslint-plugin-fsd-lint)
[![npm downloads](https://img.shields.io/npm/dt/eslint-plugin-fsd-lint)](https://www.npmjs.com/package/eslint-plugin-fsd-lint)
[![npm bundle size](https://img.shields.io/bundlephobia/min/eslint-plugin-fsd-lint)](https://bundlephobia.com/package/eslint-plugin-fsd-lint)
[![License](https://img.shields.io/npm/l/eslint-plugin-fsd-lint)](https://github.com/effozen/eslint-plugin-fsd-lint/blob/main/LICENSE)

[English](README.md) | [ν•œκ΅­μ–΄](README.ko.md)

> ESLint 9+와 μƒˆλ‘œμš΄ Flat Config μ‹œμŠ€ν…œμ„ μ§€μ›ν•©λ‹ˆλ‹€.

## πŸ“– μ†Œκ°œ

`eslint-plugin-fsd-lint`λŠ” **Feature-Sliced Design(FSD)** μ•„ν‚€ν…μ²˜λ₯Ό μ€€μˆ˜ν•˜λ„λ‘ κ°•μ œν•˜λŠ” ESLint ν”ŒλŸ¬κ·ΈμΈμž…λ‹ˆλ‹€.
μ΅œμ‹  **ESLint 9+** λ₯Ό μ™„λ²½νžˆ μ§€μ›ν•˜κ³ , **Flat Config** ν˜•μ‹μ„ λ”°λ₯΄κΈ° λ•Œλ¬Έμ— μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ νƒ€μž…μŠ€ν¬λ¦½νŠΈ ν”„λ‘œμ νŠΈμ— λ§€λ„λŸ½κ²Œ 톡합할 수 μžˆμŠ΅λ‹ˆλ‹€.

### ✨ μ™œ 이 ν”ŒλŸ¬κ·ΈμΈμ„ μ‚¬μš©ν•΄μ•Ό ν•˜λ‚˜μš”?

- **Flat Config 지원**: **ESLint 9+** 및 **μƒˆλ‘œμš΄ Flat Config μ‹œμŠ€ν…œ**κ³Ό μ™„λ²½ ν˜Έν™˜
- **μ—„κ²©ν•œ FSD μ€€μˆ˜**: κΈ°λŠ₯ 기반 ν”„λ‘œμ νŠΈ κ΅¬μ‘°μ—μ„œ μ•„ν‚€ν…μ²˜ μœ„λ°˜μ„ λ°©μ§€
- **μœ μ§€λ³΄μˆ˜μ„± ν–₯상**: λͺ…ν™•ν•œ λͺ¨λ“ˆ 뢄리와 μ˜μ‘΄μ„± 관리λ₯Ό μœ λ„
- **μΌκ΄€λœ μ½”λ“œ ν’ˆμ§ˆ 보μž₯**: import νŒ¨ν„΄κ³Ό λͺ¨λ²” 사둀λ₯Ό ν‘œμ€€ν™”
- **크둜슀 ν”Œλž«νΌ ν˜Έν™˜μ„±**: Windows와 Unix 기반 μ‹œμŠ€ν…œ λͺ¨λ‘μ—μ„œ μ›ν™œν•˜κ²Œ μž‘λ™
- **μœ μ—°ν•œ 폴더 이름 μ§€μ •**: μ‚¬μš©μž μ •μ˜ 폴더 이름 νŒ¨ν„΄(`1_app`, `2_pages` λ“±) 지원
- **λ‹€μ–‘ν•œ 별칭 ν˜•μ‹**: `@shared`와 `@/shared` λͺ¨λ‘ 지원
- **포괄적인 ν…ŒμŠ€νŠΈ 컀버리지**: μ‹€μ œ μ‹œλ‚˜λ¦¬μ˜€μ™€ μ—£μ§€ μΌ€μ΄μŠ€λ‘œ μ² μ €νžˆ ν…ŒμŠ€νŠΈλ¨

### πŸ” Feature-Sliced Designμ΄λž€?

Feature-Sliced Design(FSD)은 ν”„λ‘ νŠΈμ—”λ“œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΅¬μ‘°ν™”ν•˜λŠ” ν˜„λŒ€μ μΈ μ•„ν‚€ν…μ²˜ νŒ¨ν„΄μž…λ‹ˆλ‹€.
λ³Έ ν”ŒλŸ¬κ·ΈμΈμ€ **μ˜¬λ°”λ₯Έ λ ˆμ΄μ–΄ 뢄리, import μ œν•œ, μ˜μ‘΄μ„± 관리** λ“±μ˜ μ£Όμš” FSD 원칙을 μ μš©ν•˜κ³ ,
규λͺ¨κ°€ 컀져도 μœ μ§€λ³΄μˆ˜κ°€ μš©μ΄ν•œ μ½”λ“œλ² μ΄μŠ€λ₯Ό λ§Œλ“€λ„λ‘ λ•μŠ΅λ‹ˆλ‹€.

---

## πŸ“¦ μ„€μΉ˜

λ‹€μŒ λͺ…λ Ήμ–΄λ‘œ `eslint-plugin-fsd-lint`λ₯Ό **npm** λ˜λŠ” **pnpm**으둜 μ„€μΉ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

### npm μ‚¬μš© μ‹œ:

```shell
npm install --save-dev eslint-plugin-fsd-lint
```

### pnpm μ‚¬μš© μ‹œ:

```shell
pnpm add -D eslint-plugin-fsd-lint
```

### Peer Dependencies

이 ν”ŒλŸ¬κ·ΈμΈμ€ ESLint 9+ 버전을 ν•„μš”λ‘œ ν•©λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈμ— ESLintκ°€ μ„€μΉ˜λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”:

```shell
npm install --save-dev eslint
```

> πŸ’‘ 팁: μ—¬λŸ¬ νŒ¨ν‚€μ§€λ₯Ό κ°€μ§„ λͺ¨λ…Έλ ˆν¬ ν™˜κ²½μ—μ„œ μž‘μ—… 쀑이라면, ν”„λ‘œμ νŠΈ 루트 λ ˆλ²¨μ— `eslint-plugin-fsd-lint`λ₯Ό μ„€μΉ˜ν•΄ λͺ¨λ“  μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ—μ„œ 섀정을 κ³΅μœ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

---

## πŸš€ μ‚¬μš© 방법 & μ„€μ •

### πŸ”§ Flat Config μ„€μ • (`eslint.config.mjs`)

`eslint-plugin-fsd-lint`λŠ” **ESLint 9+** 버전에 맞좰 **Flat Config μ‹œμŠ€ν…œ**κ³Ό λ§€λ„λŸ½κ²Œ λ™μž‘ν•˜λ„λ‘ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” `eslint.config.mjs`에 λ‹€μŒκ³Ό 같은 섀정을 μΆ”κ°€ν•˜μ„Έμš”:

```js
import fsdPlugin from 'eslint-plugin-fsd-lint';

export default [
// ꢌμž₯ 프리셋 μ‚¬μš©
fsdPlugin.configs.recommended,

// λ˜λŠ” κ°œλ³„μ μœΌλ‘œ κ·œμΉ™ ꡬ성
{
plugins: {
fsd: fsdPlugin,
},
rules: {
// FSD λ ˆμ΄μ–΄ import κ·œμΉ™ κ°•μ œ (예: featuresλŠ” pagesλ₯Ό import λΆˆκ°€)
'fsd/forbidden-imports': 'error',

// 슬라이슀/λ ˆμ΄μ–΄ κ°„ μƒλŒ€ 경둜 import κΈˆμ§€, 별칭(@) μ‚¬μš©
// 기본적으둜 같은 슬라이슀 λ‚΄ μƒλŒ€ κ²½λ‘œλŠ” ν—ˆμš© (μ„€μ • κ°€λŠ₯)
'fsd/no-relative-imports': 'error',

// Public API (index 파일)λ₯Ό ν†΅ν•œ import만 ν—ˆμš©
'fsd/no-public-api-sidestep': 'error',

// 같은 λ ˆμ΄μ–΄ λ‚΄ 슬라이슀 κ°„ 직접 import λ°©μ§€
'fsd/no-cross-slice-dependency': 'error',

// λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 λ ˆμ΄μ–΄μ—μ„œ UI import λ°©μ§€
'fsd/no-ui-in-business-logic': 'error',

// μ „μ—­ μŠ€ν† μ–΄ 직접 import κΈˆμ§€
'fsd/no-global-store-imports': 'error',

// FSD λ ˆμ΄μ–΄ 기반으둜 import μˆœμ„œ κ°•μ œ
'fsd/ordered-imports': 'warn',
},
},
];
```

**기본적으둜 κ°•μ œλ˜λŠ” μ£Όμš” 원칙:**

- **λ ˆμ΄μ–΄ μž„ν¬νŠΈ**: μƒμœ„ λ ˆμ΄μ–΄λŠ” ν•˜μœ„ λ ˆμ΄μ–΄λ₯Ό μž„ν¬νŠΈν•  수 μ—†μŠ΅λ‹ˆλ‹€ (예: `features`λŠ” `pages`λ₯Ό μž„ν¬νŠΈν•  수 μ—†μŒ).
- **슬라이슀 격리**: 같은 λ ˆμ΄μ–΄ λ‚΄μ˜ μŠ¬λΌμ΄μŠ€λŠ” μ„œλ‘œ 직접 μž„ν¬νŠΈν•΄μ„œλŠ” μ•ˆ λ©λ‹ˆλ‹€ (`no-cross-slice-dependency`).
- **Public API μ‚¬μš©**: λ‹€λ₯Έ 슬라이슀/λ ˆμ΄μ–΄μ—μ„œμ˜ μž„ν¬νŠΈλŠ” ν•΄λ‹Ή 슬라이슀/λ ˆμ΄μ–΄μ˜ Public API(`index.js` λ˜λŠ” `index.ts`)λ₯Ό 톡해 이루어져야 ν•©λ‹ˆλ‹€. λ‚΄λΆ€ λͺ¨λ“ˆ 직접 μž„ν¬νŠΈλŠ” κΈˆμ§€λ©λ‹ˆλ‹€ (`no-public-api-sidestep`).
- **μ ˆλŒ€ 경둜 (별칭)**: μ„œλ‘œ λ‹€λ₯Έ μŠ¬λΌμ΄μŠ€λ‚˜ λ ˆμ΄μ–΄ κ°„μ˜ μž„ν¬νŠΈμ—λŠ” μ ˆλŒ€ 경둜(예: `@`와 같은 λ³„μΉ­μœΌλ‘œ 섀정됨)λ₯Ό μ‚¬μš©ν•˜μ„Έμš”. μƒλŒ€ κ²½λ‘œλŠ” 일반적으둜 κΈˆμ§€λ©λ‹ˆλ‹€ (`no-relative-imports`).
- **슬라이슀 λ‚΄ μƒλŒ€ 경둜**: _같은 슬라이슀 λ‚΄μ—μ„œμ˜_ μž„ν¬νŠΈμ—λŠ” μƒλŒ€ κ²½λ‘œκ°€ **ν—ˆμš©λ©λ‹ˆλ‹€** (예: feature λ‚΄μ—μ„œ `../lib`의 헬퍼λ₯Ό μž„ν¬νŠΈ). μ΄λŠ” `no-relative-imports`의 `allowSameSlice` μ˜΅μ…˜μœΌλ‘œ μ„€μ • κ°€λŠ₯ν•©λ‹ˆλ‹€ (κΈ°λ³Έκ°’: true).

### πŸ“Œ μ‚¬μš© κ°€λŠ₯ν•œ ꡬ성

이 ν”ŒλŸ¬κ·ΈμΈμ€ μ„Έ κ°€μ§€ λ‹€μ–‘ν•œ 엄격성 μˆ˜μ€€μ˜ 사전 μ •μ˜λœ ꡬ성을 μ œκ³΅ν•©λ‹ˆλ‹€:

```js
import fsdPlugin from 'eslint-plugin-fsd-lint';

export default [
// ν‘œμ€€ ꢌμž₯ ꡬ성
fsdPlugin.configs.recommended,

// μ—„κ²©ν•œ ꡬ성 (λͺ¨λ“  κ·œμΉ™μ΄ error)
// fsdPlugin.configs.strict,

// κΈ°λ³Έ ꡬ성 (덜 엄격함)
// fsdPlugin.configs.base,
];
```

### πŸ› οΈ κ³ κΈ‰ ꡬ성

κ³ κΈ‰ μ˜΅μ…˜μ„ 톡해 κ·œμΉ™μ˜ λ™μž‘μ„ μ»€μŠ€ν„°λ§ˆμ΄μ¦ˆν•  수 μžˆμŠ΅λ‹ˆλ‹€:

```js
import fsdPlugin from 'eslint-plugin-fsd-lint';

export default [
{
plugins: {
fsd: fsdPlugin,
},
rules: {
// 별칭 ν˜•μ‹ 및 폴더 νŒ¨ν„΄ ꡬ성
'fsd/forbidden-imports': [
'error',
{
// @shared λ˜λŠ” @/shared ν˜•μ‹ λͺ¨λ‘ 지원
alias: {
value: '@',
withSlash: false, // @/shared ν˜•μ‹μ„ μœ„ν•΄ true μ‚¬μš©
},
// λ²ˆν˜Έκ°€ 맀겨진 폴더 접두사 지원
folderPattern: {
enabled: true,
regex: '^(\\d+_)?(.*)',
extractionGroup: 2,
},
},
],

// 기타 κ·œμΉ™...
},
},
];
```

### πŸ“‚ μ˜ˆμ‹œ ν”„λ‘œμ νŠΈ ꡬ쑰

λ‹€μŒμ€ FSD 원칙을 μ€€μˆ˜ν•˜λŠ” ν”„λ‘œμ νŠΈ ꡬ쑰 μ˜ˆμ‹œμž…λ‹ˆλ‹€:

```plaintext
src/
β”œβ”€β”€ app/ (λ˜λŠ” 1_app/)
β”‚ β”œβ”€β”€ providers/
β”‚ β”œβ”€β”€ store.js
β”‚ β”œβ”€β”€ index.js // μ•±μ˜ Public API
β”‚
β”œβ”€β”€ processes/ (λ˜λŠ” 2_processes/)
β”‚ β”œβ”€β”€ auth/
β”‚ β”œβ”€β”€ onboarding/
β”‚
β”œβ”€β”€ pages/ (λ˜λŠ” 3_pages/)
β”‚ β”œβ”€β”€ HomePage/
β”‚ β”‚ β”œβ”€β”€ ui/ // HomePageλ₯Ό μœ„ν•œ UI μ»΄ν¬λ„ŒνŠΈ
β”‚ β”‚ └── index.ts // HomePage의 Public API
β”‚ β”œβ”€β”€ ProfilePage/
β”‚
β”œβ”€β”€ widgets/ (λ˜λŠ” 4_widgets/)
β”‚ β”œβ”€β”€ Navbar/
β”‚ β”‚ β”œβ”€β”€ ui/
β”‚ β”‚ └── index.ts // Navbar의 Public API
β”‚ β”œβ”€β”€ Sidebar/
β”‚
β”œβ”€β”€ features/ (λ˜λŠ” 5_features/)
β”‚ β”œβ”€β”€ login/
β”‚ β”‚ β”œβ”€β”€ ui/ // login featureλ₯Ό μœ„ν•œ UI μ»΄ν¬λ„ŒνŠΈ
β”‚ β”‚ β”œβ”€β”€ model/ // login을 μœ„ν•œ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
β”‚ β”‚ └── index.ts // login feature의 Public API
β”‚ β”œβ”€β”€ registration/
β”‚
β”œβ”€β”€ entities/ (λ˜λŠ” 6_entities/)
β”‚ β”œβ”€β”€ user/
β”‚ β”‚ β”œβ”€β”€ ui/
β”‚ β”‚ β”œβ”€β”€ model/
β”‚ β”‚ └── index.ts // user entity의 Public API
β”‚ β”œβ”€β”€ post/
β”‚
β”œβ”€β”€ shared/ (λ˜λŠ” 7_shared/)
β”‚ β”œβ”€β”€ ui/ // μž¬μ‚¬μš© κ°€λŠ₯ν•œ UI μ»΄ν¬λ„ŒνŠΈ
β”‚ β”‚ └── Button/ // μ˜ˆμ‹œ: Button μ»΄ν¬λ„ŒνŠΈ
β”‚ β”œβ”€β”€ lib/ // μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜, 헬퍼
β”‚ β”œβ”€β”€ config/ // 곡유 μ„€μ •
β”‚ └── index.ts // shared λ ˆμ΄μ–΄μ˜ Public API (선택 사항)

```

**ꡬ쑰 기반 Import μ˜ˆμ‹œ:**

- **ν—ˆμš©λ¨ (별칭 Import - 슬라이슀/λ ˆμ΄μ–΄ κ°„):**
```javascript
// features/login/ui/LoginForm.tsx
import { Button } from '@shared/ui/Button'; // OK: Featureκ°€ 별칭을 톡해 Shared UI μ‚¬μš©
import { getUser } from '@entities/user'; // OK: Featureκ°€ Public APIλ₯Ό 톡해 User μ—”ν‹°ν‹° μ‚¬μš©
```
- **ν—ˆμš©λ¨ (μƒλŒ€ 경둜 Import - 같은 슬라이슀 λ‚΄):**
```javascript
// features/login/ui/LoginForm.tsx
import { validateInput } from '../lib/validation'; // OK: 'login' feature 슬라이슀 λ‚΄μ—μ„œ μƒλŒ€ 경둜 import
```
- **κΈˆμ§€λ¨ (μƒλŒ€ 경둜 Import - 슬라이슀/λ ˆμ΄μ–΄ κ°„):**
```javascript
// features/login/ui/LoginForm.tsx
import { config } from '../../../app/config'; // BAD: λ‹€λ₯Έ λ ˆμ΄μ–΄λ‘œμ˜ μƒλŒ€ 경둜
import { User } from '../../entities/user/model/types'; // BAD: μƒλŒ€ 경둜 + Public API 우회
```
- **κΈˆμ§€λ¨ (Public API 우회):**
```javascript
// features/login/ui/LoginForm.tsx
import { userSlice } from '@entities/user/model/slice'; // BAD: λ‚΄λΆ€ λͺ¨λ“ˆ 직접 import
```
- **κΈˆμ§€λ¨ (슬라이슀 κ°„ μ˜μ‘΄μ„±):**
```javascript
// features/login/model/auth.ts
import { startRegistration } from '@features/registration'; // BAD: 'login' featureκ°€ 'registration' feature 직접 import
```

> πŸ’‘ 팁: μ΄λŸ¬ν•œ import κ·œμΉ™, 특히 별칭 μ‚¬μš©κ³Ό Public API μ€€μˆ˜λŠ” μ½”λ“œλ² μ΄μŠ€μ˜ λ¦¬νŒ©ν† λ§κ³Ό μœ μ§€λ³΄μˆ˜λ₯Ό 훨씬 μ‰½κ²Œ λ§Œλ“­λ‹ˆλ‹€.

---

## πŸ” μ§€μ›ν•˜λŠ” κ·œμΉ™(Rules)

이 ν”ŒλŸ¬κ·ΈμΈμ€ **Feature-Sliced Design(FSD)의 λͺ¨λ²” 사둀**λ₯Ό κ°•μ œν•˜λŠ” 일련의 ESLint κ·œμΉ™μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
각 κ·œμΉ™μ€ **λͺ…ν™•ν•œ λͺ¨λ“ˆ ꡬ쑰 μœ μ§€, import μ œμ•½, μ•„ν‚€ν…μ²˜ μœ„λ°˜ λ°©μ§€**에 도움이 λ©λ‹ˆλ‹€.

| κ·œμΉ™(Rule) | μ„€λͺ… |
| --------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **fsd/forbidden-imports** | μƒμœ„ λ ˆμ΄μ–΄μ—μ„œμ˜ importλ‚˜ 슬라이슀 κ°„ ꡐ차 importλ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€. |
| **fsd/no-relative-imports** | μ„œλ‘œ λ‹€λ₯Έ μŠ¬λΌμ΄μŠ€λ‚˜ λ ˆμ΄μ–΄ κ°„μ˜ μƒλŒ€ 경둜 importλ₯Ό κΈˆμ§€ν•©λ‹ˆλ‹€. 같은 슬라이슀 λ‚΄μ—μ„œλŠ” μƒλŒ€ 경둜 ν—ˆμš©. |
| **fsd/no-public-api-sidestep** | Public APIλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  λ‚΄λΆ€ λͺ¨λ“ˆμ„ 직접 importν•˜λŠ” 것을 λ°©μ§€ν•©λ‹ˆλ‹€. |
| **fsd/no-cross-slice-dependency** | 같은 λ ˆμ΄μ–΄ λ‚΄ μ„œλ‘œ λ‹€λ₯Έ 슬라이슀 κ°„μ˜ 직접 μ˜μ‘΄μ„±μ„ κΈˆμ§€ν•©λ‹ˆλ‹€ (features뿐만 μ•„λ‹ˆλΌ λͺ¨λ“  λ ˆμ΄μ–΄μ— 적용). |
| **fsd/no-ui-in-business-logic** | λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 λ ˆμ΄μ–΄(e.g., entities)μ—μ„œ UIλ₯Ό importν•˜μ§€ λͺ»ν•˜λ„둝 ν•©λ‹ˆλ‹€. |
| **fsd/no-global-store-imports** | μ „μ—­ μƒνƒœ(`store`)λ₯Ό 직접 importν•˜μ§€ λͺ»ν•˜λ„둝 ν•©λ‹ˆλ‹€. |
| **fsd/ordered-imports** | λ ˆμ΄μ–΄λ³„λ‘œ importλ₯Ό κ·Έλ£Ήν™”ν•˜λ„λ‘ κ°•μ œν•©λ‹ˆλ‹€. |

---

## πŸ“Œ κ·œμΉ™ 상세 & μ˜ˆμ‹œ

### **1️⃣ fsd/forbidden-imports**

**μƒμœ„ λ ˆμ΄μ–΄μ—μ„œμ˜ import와 슬라이슀 κ°„ ꡐ차 importλ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€.**
βœ… **ν—ˆμš©:** `features` β†’ `entities` λ˜λŠ” `shared`
❌ **κΈˆμ§€:** `features` β†’ `app` 직접 import

```js
// ❌ 잘λͺ»λœ 예 (featureμ—μ„œ app을 import)
import { config } from '../../app/config';

// βœ… μ˜¬λ°”λ₯Έ 예 (featureμ—μ„œ entities/sharedλ₯Ό import)
import { getUser } from '../../entities/user';
import { formatCurrency } from '../../shared/utils';
```


### 2️⃣ fsd/no-relative-imports

μ„œλ‘œ λ‹€λ₯Έ μŠ¬λΌμ΄μŠ€λ‚˜ λ ˆμ΄μ–΄ κ°„μ˜ μƒλŒ€ 경둜 importλ₯Ό κΈˆμ§€ν•©λ‹ˆλ‹€.
βœ… ν—ˆμš©: ν”„λ‘œμ νŠΈ 별칭을 μ‚¬μš©ν•˜κ±°λ‚˜ 같은 슬라이슀 λ‚΄μ—μ„œ μƒλŒ€ 경둜 μ‚¬μš©
❌ κΈˆμ§€: μ„œλ‘œ λ‹€λ₯Έ 슬라이슀 κ°„μ˜ μƒλŒ€ 경둜 import

```javascript
// ❌ 잘λͺ»λœ 예 (μ„œλ‘œ λ‹€λ₯Έ 슬라이슀 κ°„ μƒλŒ€ 경둜 import)
import { fetchUser } from '../another-slice/model/api';

// βœ… μ˜¬λ°”λ₯Έ 예 (같은 슬라이슀 λ‚΄μ—μ„œ μƒλŒ€ 경둜 import)
import { fetchData } from '../model/api';

// βœ… μ˜¬λ°”λ₯Έ 예 (μŠ¬λΌμ΄μŠ€λ‚˜ λ ˆμ΄μ–΄ κ°„μ—λŠ” 별칭 import)
import { Button } from '@shared/ui/Button';
// @/shared ν˜•μ‹λ„ 지원
import { Button } from '@/shared/ui/Button';
```

#### μ‚¬μš© κ°€λŠ₯ν•œ μ˜΅μ…˜:

```javascript
// eslint.config.js νŒŒμΌμ—μ„œ:
'fsd/no-relative-imports': ['error', {
// 같은 슬라이슀 λ‚΄μ—μ„œ μƒλŒ€ 경둜 import ν—ˆμš© (κΈ°λ³Έκ°’: true)
allowSameSlice: true,

// νƒ€μž… μ „μš© import에 μƒλŒ€ 경둜 μ‚¬μš© ν—ˆμš© (κΈ°λ³Έκ°’: false)
allowTypeImports: false,

// 이 κ·œμΉ™μ„ λ¬΄μ‹œν•  수 μžˆλŠ” ν…ŒμŠ€νŠΈ 파일 νŒ¨ν„΄
testFilesPatterns: ['\\.test\\.', '\\.spec\\.'],

// λ¬΄μ‹œν•  import νŒ¨ν„΄
ignoreImportPatterns: []
}]
```


### 3️⃣ fsd/no-public-api-sidestep

features, widgets, entities의 λ‚΄λΆ€ λͺ¨λ“ˆμ„ 직접 importν•˜μ§€ λͺ»ν•˜λ„둝 ν•©λ‹ˆλ‹€.
βœ… ν—ˆμš©: index.ts(곡개 API)λ₯Ό ν†΅ν•œ import
❌ κΈˆμ§€: feature λ‚΄λΆ€ νŒŒμΌμ— 직접 μ ‘κ·Ό

```javascript
// ❌ 잘λͺ»λœ 예 (feature λ‚΄λΆ€ 파일 직접 import)
import { authSlice } from '../../features/auth/slice.ts';

// βœ… μ˜¬λ°”λ₯Έ 예 (곡개 APIλ₯Ό 톡해 import)
import { authSlice } from '../../features/auth';
```


### 4️⃣ fsd/no-cross-slice-dependency

같은 λ ˆμ΄μ–΄ λ‚΄ μ„œλ‘œ λ‹€λ₯Έ 슬라이슀 κ°„μ˜ 직접 μ˜μ‘΄μ„±μ„ λ°©μ§€ν•©λ‹ˆλ‹€ (λͺ¨λ“  λ ˆμ΄μ–΄μ— 적용, features만 ν•΄λ‹Ήν•˜μ§€ μ•ŠμŒ).
βœ… ν—ˆμš©: ν•˜μœ„ λ ˆμ΄μ–΄λ₯Ό ν†΅ν•œ 톡신
❌ κΈˆμ§€: 같은 λ ˆμ΄μ–΄ λ‚΄ μ„œλ‘œ λ‹€λ₯Έ 슬라이슀 κ°„μ˜ 직접 import

```javascript
// ❌ 잘λͺ»λœ 예 (같은 λ ˆμ΄μ–΄μ—μ„œ λ‹€λ₯Έ 슬라이슀 import)
import { processPayment } from '../../features/payment';

// βœ… μ˜¬λ°”λ₯Έ 예 (entities/sharedλ₯Ό 쀑간에 μ‚¬μš©)
import { PaymentEntity } from '../../entities/payment';

// ❌ λ˜ν•œ 잘λͺ»λœ 예 (entities μŠ¬λΌμ΄μŠ€κ°€ λ‹€λ₯Έ entities 슬라이슀 import)
import { Product } from '../../entities/product';
// 이 κ·œμΉ™μ€ 이제 features뿐만 μ•„λ‹ˆλΌ λͺ¨λ“  λ ˆμ΄μ–΄μ— μ μš©λ©λ‹ˆλ‹€!
```


### 5️⃣ fsd/no-ui-in-business-logic

λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 λ ˆμ΄μ–΄(예: entities)μ—μ„œ UIλ₯Ό importν•˜μ§€ λͺ»ν•˜λ„둝 ν•©λ‹ˆλ‹€.
βœ… ν—ˆμš©: UIλŠ” widgetsλ‚˜ pages λ‚΄μ—μ„œλ§Œ μ‚¬μš©
❌ κΈˆμ§€: entitiesμ—μ„œ UI μ»΄ν¬λ„ŒνŠΈ import

```javascript
// ❌ 잘λͺ»λœ 예 (entityμ—μ„œ widget import)
import { ProfileCard } from '../../widgets/ProfileCard';

// βœ… μ˜¬λ°”λ₯Έ 예 (widgetμ—μ„œ entity 데이터λ₯Ό μ‚¬μš©)
import { getUser } from '../../entities/user';
```


### 6️⃣ fsd/no-global-store-imports

μ „μ—­ μƒνƒœ(store)λ₯Ό 직접 importν•˜μ§€ λͺ»ν•˜λ„둝 ν•©λ‹ˆλ‹€.
βœ… ν—ˆμš©: useStore λ˜λŠ” useSelector와 같은 ν›… μ‚¬μš©
❌ κΈˆμ§€: store 객체λ₯Ό 직접 import

```javascript
// ❌ 잘λͺ»λœ 예 (store 직접 import)
import { store } from '../../app/store';

// βœ… μ˜¬λ°”λ₯Έ 예 (ν›… μ‚¬μš©)
import { useStore } from 'zustand';
import { useSelector } from 'react-redux';
```


### 7️⃣ fsd/ordered-imports

λ ˆμ΄μ–΄λ³„λ‘œ importλ₯Ό κ·Έλ£Ήν™”ν•˜λ„λ‘ κ°•μ œν•©λ‹ˆλ‹€.
βœ… ν—ˆμš©: λ ˆμ΄μ–΄λ³„λ‘œ import μ •λ ¬
❌ κΈˆμ§€: λ¬΄μž‘μœ„ μˆœμ„œμ˜ import

```javascript
// ❌ 잘λͺ»λœ 예 (μž„μ˜μ˜ μˆœμ„œλ‘œ import)
import { processPayment } from '../features/payment';
import { getUser } from '../entities/user';
import { formatCurrency } from '../shared/utils';
import { loginUser } from '../features/auth';
import { Header } from '../widgets/Header';
import { useStore } from '../app/store';

// βœ… μ˜¬λ°”λ₯Έ 예 (λ ˆμ΄μ–΄λ³„ κ·Έλ£Ήν™”)
import { useStore } from '../app/store'; // App

import { loginUser } from '../features/auth'; // Features
import { processPayment } from '../features/payment';

import { getUser } from '../entities/user'; // Entities

import { formatCurrency } from '../shared/utils'; // Shared

import { Header } from '../widgets/Header'; // Widgets
```

> πŸ’‘ 팁: `npx eslint --fix` λͺ…령을 μ‚¬μš©ν•˜λ©΄, FSD λ ˆμ΄μ–΄ κ·œμΉ™μ— 따라 importλ₯Ό μžλ™ μ •λ ¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

---

## πŸ›  μžλ™ μˆ˜μ •(Auto-fix) 지원

`eslint-plugin-fsd-lint` λ‚΄ 일뢀 κ·œμΉ™μ€ ESLint의 `--fix` μ˜΅μ…˜μ„ 톡해 **μžλ™μœΌλ‘œ μˆ˜μ •**될 수 μžˆμŠ΅λ‹ˆλ‹€.
이λ₯Ό 톡해 κ°œλ°œμžλ“€μ€ **μˆ˜λ™μœΌλ‘œ μ½”λ“œλ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šμ•„λ„** λΉ λ₯΄κ²Œ κ·œμΉ™ μœ„λ°˜μ„ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

### βœ… μžλ™ μˆ˜μ •μ΄ μ§€μ›λ˜λŠ” κ·œμΉ™

λ‹€μŒ κ·œμΉ™μ€ μžλ™ μˆ˜μ •μ„ μ§€μ›ν•©λ‹ˆλ‹€:

| κ·œμΉ™(Rule) | μ„€λͺ… |
| ----------------------- | ------------------------------------------------------------------------- |
| **fsd/ordered-imports** | Feature-Sliced Design(FSD) λ ˆμ΄μ–΄ κΈ°μ€€μœΌλ‘œ import μˆœμ„œλ₯Ό μžλ™ μ •λ ¬ν•©λ‹ˆλ‹€. |

### πŸ”§ ESLintμ—μ„œ `--fix` μ‚¬μš©ν•˜κΈ°

ν”„λ‘œμ νŠΈμ˜ νŠΉμ • νŒŒμΌμ— λŒ€ν•œ μžλ™ μˆ˜μ •μ„ μ μš©ν•˜λ €λ©΄ λ‹€μŒκ³Ό 같이 μ‹€ν–‰ν•˜μ„Έμš”:

```shell
npx eslint --fix your-file.js
```

ν”„λ‘œμ νŠΈ λ‚΄ λͺ¨λ“  νŒŒμΌμ— λŒ€ν•΄ μžλ™ μˆ˜μ •μ„ μ μš©ν•˜λ €λ©΄:

```shell
npx eslint --fix .
```

### πŸ“Œ μžλ™ μˆ˜μ • μ „ν›„ μ˜ˆμ‹œ

❌ μˆ˜μ • μ „ (`fsd/ordered-imports` μœ„λ°˜)

```javascript
import { processPayment } from '../features/payment';
import { getUser } from '../entities/user';
import { formatCurrency } from '../shared/utils';
import { loginUser } from '../features/auth';
import { Header } from '../widgets/Header';
import { useStore } from '../app/store';
```


βœ… μˆ˜μ • ν›„ (`npx eslint --fix` 적용됨)

```javascript
import { useStore } from '../app/store'; // App

import { loginUser } from '../features/auth'; // Features
import { processPayment } from '../features/payment';

import { getUser } from '../entities/user'; // Entities

import { formatCurrency } from '../shared/utils'; // Shared

import { Header } from '../widgets/Header'; // Widgets
```

> πŸ’‘ 팁: `fsd/ordered-imports` κ·œμΉ™μ€ FSD λ ˆμ΄μ–΄λ₯Ό κΈ°μ€€μœΌλ‘œ κΉ”λ”ν•˜κ³  ꡬ쑰적인 import μˆœμ„œλ₯Ό μœ μ§€ν•©λ‹ˆλ‹€.

---

## πŸ†• μƒˆλ‘œμš΄ κΈ°λŠ₯

### 1. 크둜슀 ν”Œλž«νΌ ν˜Έν™˜μ„±

이 ν”ŒλŸ¬κ·ΈμΈμ€ 이제 λ‚΄λΆ€μ μœΌλ‘œ 파일 경둜λ₯Ό μ •κ·œν™”ν•˜μ—¬ Windows와 Unix 기반 μ‹œμŠ€ν…œ λͺ¨λ‘μ—μ„œ μ›ν™œν•˜κ²Œ μž‘λ™ν•©λ‹ˆλ‹€.

### 2. μœ μ—°ν•œ 폴더 이름 νŒ¨ν„΄

이제 폴더에 번호 접두사 λ“±μ˜ 이름 μ§€μ • κ·œμΉ™μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

```js
// 폴더 νŒ¨ν„΄ 지원 ꡬ성
"fsd/forbidden-imports": ["error", {
folderPattern: {
enabled: true,
regex: "^(\\d+_)?(.*)",
extractionGroup: 2
}
}]
```

이λ₯Ό 톡해 λ‹€μŒκ³Ό 같은 ꡬ쑰λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

```
src/
1_app/
2_pages/
3_widgets/
4_features/
5_entities/
6_shared/
```

### 3. λ‹€μ–‘ν•œ 별칭 ν˜•μ‹ 지원

이제 ν”ŒλŸ¬κ·ΈμΈμ€ `@shared`와 `@/shared` ν˜•μ‹ λͺ¨λ‘ μ§€μ›ν•©λ‹ˆλ‹€:

```js
// 별칭 ν˜•μ‹ ꡬ성
"fsd/forbidden-imports": ["error", {
alias: {
value: "@",
withSlash: false // @/shared ν˜•μ‹μ„ μœ„ν•΄ true μ‚¬μš©
}
}]
```

### 4. ν–₯μƒλœ cross-slice-dependency κ·œμΉ™

`no-cross-slice-dependency` κ·œμΉ™μ€ 이제 기본적으둜 features뿐만 μ•„λ‹ˆλΌ λͺ¨λ“  λ ˆμ΄μ–΄μ— μ μš©λ©λ‹ˆλ‹€:

```js
// features λ ˆμ΄μ–΄λ§Œ μ œν•œ (λ ˆκ±°μ‹œ λ™μž‘)
"fsd/no-cross-slice-dependency": ["error", {
featuresOnly: true
}]
```

### 5. 사전 μ •μ˜λœ ꡬ성 ν”„λ‘œν•„

이제 μ—¬λŸ¬ ꡬ성 프리셋을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

- `recommended` - ν‘œμ€€ ꢌμž₯ μ„€μ •
- `strict` - μ΅œλŒ€ κ°•μ œ μˆ˜μ€€
- `base` - μ‰¬μš΄ λ„μž…μ„ μœ„ν•œ 덜 μ—„κ²©ν•œ μ„€μ •

### 6. 포괄적인 ν…ŒμŠ€νŠΈ 컀버리지

이 ν”ŒλŸ¬κ·ΈμΈμ€ 이제 λͺ¨λ“  κ·œμΉ™μ— λŒ€ν•œ κ΄‘λ²”μœ„ν•œ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€:

- κΈ°λ³Έ import μ‹œλ‚˜λ¦¬μ˜€
- μ—£μ§€ μΌ€μ΄μŠ€μ™€ λ³΅μž‘ν•œ νŒ¨ν„΄
- 경둜 λ³€ν˜• (Windows, Unix, ν˜Όν•©)
- μ‚¬μš©μž μ •μ˜ ꡬ성
- μ‹€μ œ μ‚¬μš© μ˜ˆμ‹œ

### 경둜 별칭 지원

ν”ŒλŸ¬κ·ΈμΈμ€ 이제 λ‹€μ–‘ν•œ 경둜 별칭 ν˜•μ‹μ„ μ˜¬λ°”λ₯΄κ²Œ μ²˜λ¦¬ν•©λ‹ˆλ‹€:

```javascript
// 두 ν˜•μ‹ λͺ¨λ‘ μ§€μ›λ©λ‹ˆλ‹€
import { UserCard } from '@entities/user';
import { UserCard } from '@/entities/user';
```

### 동적 import 지원

λͺ¨λ“  κ·œμΉ™μ΄ 이제 동적 importλ₯Ό μ§€μ›ν•©λ‹ˆλ‹€:

```javascript
// μœ νš¨ν•œ 동적 import
const UserCard = await import('@entities/user');
const { UserCard } = await import('@entities/user');

// μœ νš¨ν•˜μ§€ μ•Šμ€ 동적 import (κ·œμΉ™μ— μ˜ν•΄ 감지됨)
const UserCard = await import('@entities/user/ui');
```

### 포괄적인 ν…ŒμŠ€νŠΈ 컀버리지

λͺ¨λ“  κ·œμΉ™μ΄ 이제 μ² μ €ν•˜κ²Œ ν…ŒμŠ€νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€:

- κΈ°λ³Έ import μ‹œλ‚˜λ¦¬μ˜€
- μ—£μ§€ μΌ€μ΄μŠ€μ™€ λ³΅μž‘ν•œ νŒ¨ν„΄
- 경둜 λ³€ν˜• (Windows, Unix, ν˜Όν•©)
- μ‚¬μš©μž μ •μ˜ ꡬ성
- μ‹€μ œ μ‚¬μš© 예제
- 경둜 별칭 ν˜•μ‹
- 동적 import νŒ¨ν„΄

## κ·œμΉ™

### no-public-api-sidestep

λ‚΄λΆ€ λͺ¨λ“ˆμ—μ„œμ˜ 직접 importλ₯Ό λ°©μ§€ν•˜κ³  public API μ‚¬μš©μ„ κ°•μ œν•©λ‹ˆλ‹€.

```javascript
// βœ… μœ νš¨ν•¨: public API μ‚¬μš©
import { UserCard } from '@entities/user';
import { UserCard } from '@/entities/user'; // 이것도 μœ νš¨ν•¨
import { UserCard } from '@entities/user/index';

// ❌ μœ νš¨ν•˜μ§€ μ•ŠμŒ: λ‚΄λΆ€ 직접 import
import { UserCard } from '@entities/user/ui/UserCard';
import { UserCard } from '@entities/user/model/types';

// βœ… μœ νš¨ν•¨: public APIλ₯Ό μ‚¬μš©ν•˜λŠ” 동적 import
const UserCard = await import('@entities/user');
const { UserCard } = await import('@entities/user');

// ❌ μœ νš¨ν•˜μ§€ μ•ŠμŒ: public APIλ₯Ό μš°νšŒν•˜λŠ” 동적 import
const UserCard = await import('@entities/user/ui/UserCard');
```

---

## 🀝 μ»¨νŠΈλ¦¬λ·°μ…˜(κΈ°μ—¬)

`eslint-plugin-fsd-lint`λ₯Ό κ°œμ„ ν•˜κΈ° μœ„ν•œ λͺ¨λ“  κΈ°μ—¬λ₯Ό ν™˜μ˜ν•©λ‹ˆλ‹€!
μƒˆλ‘œμš΄ κ·œμΉ™ μ œμ•ˆμ΄λ‚˜ κ°œμ„  사항이 μžˆλ‹€λ©΄ 자유둭게 Pull Requestλ₯Ό λ³΄λ‚΄μ£Όμ„Έμš”.

μžμ„Έν•œ λ‚΄μš©μ€ [κΈ°μ—¬ κ°€μ΄λ“œ](CONTRIBUTING.md)λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

---

## πŸ“ λΌμ΄μ„ μŠ€

이 ν”„λ‘œμ νŠΈλŠ” MIT λΌμ΄μ„ μŠ€λ‘œ λ°°ν¬λ©λ‹ˆλ‹€.
μžμ„Έν•œ λ‚΄μš©μ€ [LICENSE](LICENSE.md) νŒŒμΌμ„ μ°Έκ³ ν•˜μ„Έμš”.