FSD μ•„ν‚€ν…μ²˜μ™€ ν•¨κ»˜ 생각해보기

FSD 와 배럴 파일

 

Vue3μ—μ„œ React.js둜 ν”„λ‘œμ νŠΈλ₯Ό λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ν•˜λ©΄μ„œ Feature-Sliced Design(FSD) μ•„ν‚€ν…μ²˜λ₯Ό λ„μž…ν•˜κΈ°λ‘œ κ²°μ •ν–ˆμŠ΅λ‹ˆλ‹€. κ·Έ κ³Όμ •μ—μ„œ 배럴 파일(barrel files)에 λŒ€ν•œ 고민이 생겼고, 졜근 λͺ‡ λ…„ 사이에 이에 λŒ€ν•œ 관점이 많이 λ°”λ€Œμ—ˆλ‹€λŠ” 것을 λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.

 

배럴 νŒŒμΌμ΄λž€?

배럴 νŒŒμΌμ€ μ—¬λŸ¬ λͺ¨λ“ˆμ„ ν•˜λ‚˜μ˜ 파일(주둜 index.js λ˜λŠ” index.ts)μ—μ„œ λ‹€μ‹œ λ‚΄λ³΄λ‚΄λŠ” νŒ¨ν„΄μž…λ‹ˆλ‹€. 이 νŒ¨ν„΄μ€ μ½”λ“œ 정리와 import 문을 λ‹¨μˆœν™”ν•˜λŠ” 데 도움이 λ©λ‹ˆλ‹€.

 

배럴 파일 μ „ ν›„

 

배럴 파일 없이:

// 각 νŒŒμΌμ—μ„œ κ°œλ³„μ μœΌλ‘œ κ°€μ Έμ˜€κΈ° 
import { Button } from "../components/Button"; 
import { TextField } from "../components/TextField"; 
import { Checkbox } from "../components/Checkbox";

 

배럴 파일 μ‚¬μš© μ‹œ:

// ν•˜λ‚˜μ˜ κ²½λ‘œμ—μ„œ λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈ κ°€μ Έμ˜€κΈ° 
import { Button, TextField, Checkbox } from "../components";

 

배럴 파일의 μ‹€μ œ κ΅¬ν˜„

일반적인 배럴 파일 (components/index.ts):

// μ™€μΌλ“œμΉ΄λ“œ 내보내기 (ꢌμž₯ν•˜μ§€ μ•ŠμŒ) 
export * from './Button'; 
export * from './TextField'; 
export * from './Checkbox'; 

// λ˜λŠ” λͺ…μ‹œμ  내보내기 (ꢌμž₯) 
export { Button } from './Button'; 
export { TextField } from './TextField'; 
export { Checkbox } from './Checkbox';

 

배럴 파일의 μ„±λŠ₯ 문제

Marvin의 JavaScript μƒνƒœκ³„ 속도 κ°œμ„ : 배럴 파일 문제 κΈ€μ—μ„œ μ§€μ ν•œ 바와 같이, 배럴 νŒŒμΌμ€ λ‹€μŒκ³Ό 같은 μ„±λŠ₯ 문제λ₯Ό μΌμœΌν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€:

  1. λͺ¨λ“ˆ κ·Έλž˜ν”„ λ³΅μž‘μ„± 증가: 배럴 파일이 μ¦κ°€ν• μˆ˜λ‘ λͺ¨λ“ˆ κ°„ μ˜μ‘΄μ„± κ·Έλž˜ν”„κ°€ λ³΅μž‘ν•΄μ§‘λ‹ˆλ‹€.
  2. λΆˆν•„μš”ν•œ λͺ¨λ“ˆ λ‘œλ”©: 특히 μ™€μΌλ“œμΉ΄λ“œ 내보내기(export * from)λ₯Ό μ‚¬μš©ν•  경우, μ‹€μ œλ‘œ ν•„μš”ν•˜μ§€ μ•Šμ€ λͺ¨λ“ˆκΉŒμ§€ λ‘œλ“œλ  수 μžˆμŠ΅λ‹ˆλ‹€.

 

λͺ¨λ“ˆ κ·Έλž˜ν”„ 비ꡐ

배럴 파일 없이:

App.js
β”œβ”€β”€ Button.js
β”œβ”€β”€ TextField.js
└── Checkbox.js

 

배럴 파일 μ‚¬μš© μ‹œ:

App.js
└── components/index.js
    β”œβ”€β”€ Button.js
    β”œβ”€β”€ TextField.js
    └── Checkbox.js

 

μ„±λŠ₯ ν…ŒμŠ€νŠΈ κ²°κ³Ό μ˜ˆμ‹œ

λͺ¨λ“ˆ 수배럴 파일 μ—†μŒλ°°λŸ΄ 파일 μ‚¬μš©μ°¨μ΄

λͺ¨λ“ˆ 수 배럴 파일 μ—†μŒ 배럴 파일 μ‚¬μš© 차이
10개 150ms 180ms +20%
50개 450ms 630ms +40%
100개 820ms 1250ms +52%

 

μ°Έκ³ : 이 μˆ˜μΉ˜λŠ” μ˜ˆμ‹œμš©μ΄λ©°, μ‹€μ œ ν”„λ‘œμ νŠΈμ—μ„œλŠ” λ‹€μ–‘ν•œ μš”μΈμ— 따라 λ‹¬λΌμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

FSD μ•„ν‚€ν…μ²˜μ—μ„œμ˜ 배럴 파일

Feature-Sliced Design(FSD)은 배럴 파일과 μœ μ‚¬ν•œ νŒ¨ν„΄μ„ μ˜λ„μ μœΌλ‘œ ν™œμš©ν•©λ‹ˆλ‹€. μ΄λŠ” λ‹¨μˆœνžˆ μ½”λ“œ μž‘μ„±μ˜ νŽΈμ˜μ„±μ„ μœ„ν•œ 것이 μ•„λ‹ˆλΌ, μ•„ν‚€ν…μ²˜μ˜ ꡬ쑰와 λͺ©μ μ„ λ°˜μ˜ν•œ κ²°μ •μž…λ‹ˆλ‹€.

FSDλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κΈ°λŠ₯ λ‹¨μœ„λ‘œ λΆ„ν• ν•˜κ³  κ³„μΈ΅ν™”λœ ꡬ쑰λ₯Ό λ§Œλ“œλŠ” 것을 λͺ©ν‘œλ‘œ ν•©λ‹ˆλ‹€. 이 κ΅¬μ‘°μ—μ„œ 각 λ ˆμ΄μ–΄(layer)와 슬라이슀(slice)λŠ” λͺ…ν™•ν•œ μ±…μž„κ³Ό 역할을 κ°€μ§‘λ‹ˆλ‹€. 배럴 파일과 μœ μ‚¬ν•œ νŒ¨ν„΄μ„ μ‚¬μš©ν•¨μœΌλ‘œμ¨, FSDλŠ” λ‹€μŒκ³Ό 같은 이점을 μ–»μŠ΅λ‹ˆλ‹€:

  • μΊ‘μŠν™”: 각 λͺ¨λ“ˆμ˜ λ‚΄λΆ€ κ΅¬ν˜„μ„ 숨기고 곡개 APIλ§Œμ„ λ…ΈμΆœν•©λ‹ˆλ‹€.
  • μ˜μ‘΄μ„± 관리: μƒμœ„ λ ˆμ΄μ–΄κ°€ ν•˜μœ„ λ ˆμ΄μ–΄μ—λ§Œ μ ‘κ·Όν•  수 μžˆλ„λ‘ μ œν•œν•©λ‹ˆλ‹€.
  • μž¬μ‚¬μš©μ„±: 곡톡 κΈ°λŠ₯을 μ‰½κ²Œ κ³΅μœ ν•˜κ³  μž¬μ‚¬μš©ν•  수 있게 ν•©λ‹ˆλ‹€.
  • μœ μ§€λ³΄μˆ˜μ„±: μ½”λ“œ ꡬ쑰λ₯Ό μΌκ΄€λ˜κ²Œ μœ μ§€ν•˜μ—¬ κ°œλ°œμžκ°€ μ‰½κ²Œ μ΄ν•΄ν•˜κ³  μˆ˜μ •ν•  수 있게 ν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ κ΅¬μ‘°λŠ” λŒ€κ·œλͺ¨ ν”„λ‘œμ νŠΈμ—μ„œ μ½”λ“œμ˜ 쑰직화와 관리λ₯Ό μš©μ΄ν•˜κ²Œ λ§Œλ“€μ–΄, μž₯기적으둜 ν”„λ‘œμ νŠΈμ˜ ν™•μž₯μ„±κ³Ό μœ μ§€λ³΄μˆ˜μ„±μ„ ν–₯μƒμ‹œν‚΅λ‹ˆλ‹€. λ”°λΌμ„œ FSDμ—μ„œ 배럴 파일과 μœ μ‚¬ν•œ νŒ¨ν„΄μ˜ μ‚¬μš©μ€ λ‹¨μˆœν•œ νŽΈμ˜μ„±μ„ λ„˜μ–΄μ„œ, μ•„ν‚€ν…μ²˜μ˜ 핡심 원칙을 μ‹€ν˜„ν•˜κΈ° μœ„ν•œ μ „λž΅μ  선택이라고 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 

FSDμ—μ„œμ˜ 배럴 파일 ν™œμš© - μ‹€μ œ κ²½ν—˜κ³Ό 팁

FSD μ•„ν‚€ν…μ²˜λ₯Ό μ‹€μ œ ν”„λ‘œμ νŠΈμ— μ μš©ν•΄λ³Έ κ²½ν—˜μ— λ”°λ₯΄λ©΄, 배럴 파일(Public API)의 μ£Όμš” 이점 쀑 ν•˜λ‚˜λŠ” μœ μ§€λ³΄μˆ˜μ„± ν–₯μƒμž…λ‹ˆλ‹€. 이에 λŒ€ν•΄ μ΄λ„ˆμ„œν΄ 재직자 κ³Όμ •μ—μ„œ λ§Œλ‚œ λ™λ£Œ κ°œλ°œμžλΆ„κ»˜μ„œ λ‹€μŒκ³Ό 같은 μΈμ‚¬μ΄νŠΈλ₯Ό κ³΅μœ ν•΄μ£Όμ…¨μ–΄μš”:

"FSDλ₯Ό μ‚¬μš©ν•˜λ©΄μ„œ λŠλ‚€ λ°”λ‘œλŠ”, Public APIλ₯Ό ν™œμš©ν•˜λŠ” μ£Όλœ μ΄μœ  μ€‘ ν•˜λ‚˜κ°€ μœ μ§€λ³΄μˆ˜μ— μžˆμŠ΅λ‹ˆλ‹€. μ˜ˆλ₯Ό λ“€μ–΄, entities/user의 μŠ€ν‚€λ§ˆκ°€ μΌλΆ€ λ³€κ²½λ˜λ”라도 features/loginUser, features/registerUser λ“±μ—μ„œ import κ΅¬λ¬Έμ„ λ³€κ²½ν•  ν•„μš”κ°€ μ—†μ–΄μ§‘λ‹ˆλ‹€."


μ΄λŠ” FSD의 ν•΅μ‹¬ μ›μΉ™ μ€‘ ν•˜λ‚˜μΈ λͺ¨λ“ˆ κ°„ μ˜μ‘΄μ„± κ΄€λ¦¬μ™€ μΌμΉ˜ν•˜λŠ” κ΄€μ μž…λ‹ˆλ‹€.

 

배럴 νŒŒμΌ μƒμ„± μžλ™ν™”

λŒ€κ·œλͺ¨ ν”„λ‘œμ νŠΈμ—μ„œ λ§€λ²ˆ μˆ˜λ™μœΌλ‘œ λ°°λŸ΄ νŒŒμΌ(Public API)을 κ΄€λ¦¬ν•˜λŠ” κ²ƒμ€ λ²ˆκ±°λ‘œμšΈ μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. μ΄λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ μ‹€μš©μ μΈ μ ‘κ·Ό λ°©λ²•μœΌλ‘œ, λ°°λŸ΄ νŒŒμΌμ„ μžλ™μœΌλ‘œ μƒμ„±ν•˜λŠ” μŠ€ν¬λ¦½νŠΈλ₯Ό ν™œμš©ν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄:

pnpm api features/loginUser


μ΄λŸ¬ν•œ CLI λͺ…λ Ήμ–΄λ₯Ό ν†΅ν•΄ features/loginUser λ‚΄λΆ€μ˜ λͺ¨λ“ˆμ„ μžλ™μœΌλ‘œ λΆ„μ„ν•˜κ³ , λͺ…μ‹œμ μœΌλ‘œ λ‚΄λ³΄λ‚΄λŠ” λ°°λŸ΄ νŒŒμΌμ„ μƒμ„±ν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ _둜 μ‹œμž‘ν•˜λŠ” νŒŒμΌμ΄λ‚˜ ν΄λ”λŠ” λ‚΄λΆ€μš©μœΌλ‘œ κ°„μ£Όν•˜μ—¬ μžλ™μœΌλ‘œ μ œμ™Έν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

 

λͺ…μ‹œμ  λ‚΄λ³΄λ‚΄κΈ°μ˜ μ€‘μš”μ„±

μ•žμ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄, μ™€μΌλ“œμΉ΄λ“œ 내보내기(export * from './module')λŠ” ꢌμž₯λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€μ‹ , λͺ…μ‹œμ  내보내기λ₯Ό μ‚¬μš©ν•˜λ©΄ μ„±λŠ₯ 이슈λ₯Ό μ΅œμ†Œν™”ν•˜λ©΄μ„œλ„ FSD의 이점을 μΆ©λΆ„νžˆ ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

// Good: λͺ…μ‹œμ  내보내기
export { LoginForm } from './LoginForm';
export { useAuth } from './useAuth';
export type { User } from './types';

μ΄λŸ¬ν•œ μ ‘κ·Ό 방식은 λͺ¨λ“ˆμ˜ 곡개 APIλ₯Ό λͺ…ν™•νžˆ μ •μ˜ν•˜κ³ , λΆˆν•„μš”ν•œ μ˜μ‘΄μ„±μ„ λ°©μ§€ν•˜λ©°, μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ ν–₯μƒμ‹œν‚΅λ‹ˆλ‹€. μ’€ 더 μƒμ„Έν•œ μ˜ˆμ‹œλ₯Ό λ“€μ–΄λ³Όκ²Œμš”.

μŠ€ν¬λ¦½νŠΈμ— λŒ€ν•œ 폴더 ꡬ쑰 μ˜ˆμ‹œ:

project-root/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ features/
β”‚   β”‚   └── loginUser/
β”‚   β”‚       β”œβ”€β”€ ui/
β”‚   β”‚       β”œβ”€β”€ model/
β”‚   β”‚       └── index.ts (μžλ™ 생성될 파일)
β”‚   └── ... (λ‹€λ₯Έ FSD 폴더듀)
β”œβ”€β”€ scripts/
β”‚   └── generate-barrel.ts
β”œβ”€β”€ package.json
└── tsconfig.json

 

scripts/generate-barrel.ts νŒŒμΌμ˜ λ‚΄μš©:

import fs from 'fs';
import path from 'path';

function generateBarrel(directory: string) {
  const files = fs.readdirSync(directory);
  const exports: string[] = [];

  files.forEach(file => {
    if (file.startsWith('_') || file === 'index.ts') return;

    const filePath = path.join(directory, file);
    const stats = fs.statSync(filePath);

    if (stats.isDirectory()) {
      exports.push(`export * from './${file}';`);
    } else if (file.endsWith('.ts') || file.endsWith('.tsx')) {
      const name = path.parse(file).name;
      exports.push(`export { ${name} } from './${name}';`);
    }
  });

  const content = exports.join('\n') + '\n';
  fs.writeFileSync(path.join(directory, 'index.ts'), content);
}

const targetDir = process.argv[2];
if (!targetDir) {
  console.error('Please specify a target directory');
  process.exit(1);
}

const fullPath = path.resolve(process.cwd(), targetDir);
generateBarrel(fullPath);
console.log(`Barrel file generated for ${fullPath}`);

 

package.json에 슀크립트 μΆ”κ°€:

{
  "scripts": {
    "api": "ts-node scripts/generate-barrel.ts"
  }
}

이제 pnpm api features/loginUser λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜λ©΄, src/features/loginUser/index.ts νŒŒμΌμ΄ μžλ™μœΌλ‘œ μƒμ„±λ˜κ±°λ‚˜ μ—…λ°μ΄νŠΈλ©λ‹ˆλ‹€! μ•„λž˜λΆ€ν„°λŠ” μ’€ 더 일반적인 μ˜ˆμ‹œλ₯Ό λ“€μ–΄λ³Όκ²Œμš”.

 

FSD κΈ°λ³Έ 폴더 ꡬ쑰:

src/
β”œβ”€β”€ app/        # κΈ€λ‘œλ²Œ μ„€μ •, μŠ€νƒ€μΌ, ν”„λ‘œλ°”μ΄λ”
β”œβ”€β”€ processes/  # λΉ„μ¦ˆλ‹ˆμŠ€ ν”„λ‘œμ„ΈμŠ€
β”œβ”€β”€ pages/      # νŽ˜μ΄μ§€ μ»΄ν¬λ„ŒνŠΈ
β”œβ”€β”€ widgets/    # 볡합 UI 블둝
β”œβ”€β”€ features/   # μ‚¬μš©μž μΈν„°λž™μ…˜
β”œβ”€β”€ entities/   # λΉ„μ¦ˆλ‹ˆμŠ€ μ—”ν‹°ν‹°
└── shared/     # 곡유 μœ ν‹Έλ¦¬ν‹°, UI ν‚€νŠΈ

 

FSDμ—μ„œμ˜ 배럴 파일 μ‚¬μš© μ˜ˆμ‹œ

features/auth/index.ts (μ μ ˆν•œ 배럴 파일 μ‚¬μš©):

// λͺ…μ‹œμ  내보내기 - 곡개 APIλ₯Ό λͺ…ν™•νžˆ μ •μ˜
export { LoginForm } from './ui/LoginForm';
export { useAuth } from './model/useAuth';
export type { AuthUser } from './model/types';

 

κ· ν˜• 작힌 접근법: μ‹€μ œ κ΅¬ν˜„ μ˜ˆμ‹œ

1. κ΅¬μ‘°ν™”λœ 배럴 파일 μ‚¬μš©:

features/
β”œβ”€β”€ auth/
β”‚   β”œβ”€β”€ ui/
β”‚   β”‚   β”œβ”€β”€ LoginForm.tsx
β”‚   β”‚   └── RegisterForm.tsx
β”‚   β”œβ”€β”€ model/
β”‚   β”‚   β”œβ”€β”€ useAuth.ts
β”‚   β”‚   └── types.ts
β”‚   └── index.ts  # μ•„ν‚€ν…μ²˜ κ²½κ³„μ—λ§Œ 배럴 파일 μ‚¬μš©
└── userProfile/
    β”œβ”€β”€ ui/
    β”œβ”€β”€ model/
    └── index.ts

 

2. λͺ…μ‹œμ  내보내기:

// features/auth/index.ts
// λͺ…μ‹œμ  λ‚΄λ³΄λ‚΄κΈ°λ‘œ 곡개 API μ •μ˜
export { LoginForm } from './ui/LoginForm';
export { RegisterForm } from './ui/RegisterForm';
export { useAuth } from './model/useAuth';
export type { User, AuthCredentials } from './model/types';

 

3. FSD μ•„ν‚€ν…μ²˜ κ·œμΉ™μ— λ”°λ₯Έ κ°€μ Έμ˜€κΈ°:

// 잘λͺ»λœ 방식: λ ˆμ΄μ–΄ 우회
import { Button } from '@/shared/ui/Button';

// μ˜¬λ°”λ₯Έ 방식: μ•„ν‚€ν…μ²˜ 경계 쑴쀑
import { Button } from '@/shared/ui';

 

4. μ‹€μ œ React μ»΄ν¬λ„ŒνŠΈ μ˜ˆμ‹œ:

// features/auth/ui/LoginForm.tsx
import { useState } from 'react';
import { Button, TextField } from '@/shared/ui';
import { useAuth } from '../model/useAuth';
import type { AuthCredentials } from '../model/types';

export const LoginForm = () => {
  const [credentials, setCredentials] = useState({
    email: '',
    password: ''
  });
  const { login, isLoading } = useAuth();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    login(credentials);
  };

  return (
    
      <TextField
        label="Email"
        value={credentials.email}
        onChange={(e) => setCredentials(prev => ({
          ...prev,
          email: e.target.value
        }))}
      />
      <TextField
        type="password"
        label="Password"
        value={credentials.password}
        onChange={(e) => setCredentials(prev => ({
          ...prev,
          password: e.target.value
        }))}
      />
      
        {isLoading ? 'Logging in...' : 'Login'}
      
    
  );
};

 

ν˜„λŒ€μ  λ„κ΅¬μ˜ 영ν–₯

μ΅œμ‹  λΉŒλ“œ 도ꡬ듀은 배럴 파일의 μ„±λŠ₯ 문제λ₯Ό 일뢀 μ™„ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

 

λ²ˆλ“€λŸ¬λ³„ μ΅œμ ν™” μ„€μ •:

Vite μ„€μ • μ˜ˆμ‹œ:

// vite.config.js
export default {
  build: {
    target: 'esnext',
    minify: 'esbuild',
    rollupOptions: {
      output: {
        manualChunks(id) {
          // μ΅œμ ν™”λœ 청크 μ„€μ •
        }
      }
    }
  }
}

 

Webpack μ„€μ • μ˜ˆμ‹œ:

// webpack.config.js
module.exports = {
  optimization: {
    usedExports: true, // 트리 쉐이킹 ν™œμ„±ν™”
    moduleIds: 'deterministic',
    splitChunks: {
      chunks: 'all',
    }
  }
}

 

μ„±λŠ₯ μΈ‘μ • - μ‹€μ‹œκ°„ ν”„λ‘œμ νŠΈ λͺ¨λ‹ˆν„°λ§

// build-analyzer.ts
import { performance } from 'perf_hooks';

export function measureBuildTime(taskName: string, task: () => void) {
  const startTime = performance.now();
  task();
  const endTime = performance.now();
  console.log(`Task "${taskName}" took ${endTime - startTime}ms`);
}

// μ‚¬μš© μ˜ˆμ‹œ
measureBuildTime('Module compilation', () => {
  // λΉŒλ“œ 둜직
});

 

κ²°λ‘  - ν”„λ‘œμ νŠΈ 규λͺ¨λ³„ ꢌμž₯ 접근법

 

μ†Œκ·œλͺ¨ ν”„λ‘œμ νŠΈ (10,000쀄 μ΄ν•˜)

src/
β”œβ”€β”€ components/
β”‚   └── index.ts  # 배럴 파일 μ‚¬μš© κ°€λŠ₯
β”œβ”€β”€ hooks/
β”‚   └── index.ts  # 배럴 파일 μ‚¬μš© κ°€λŠ₯
└── utils/
    └── index.ts  # 배럴 파일 μ‚¬μš© κ°€λŠ₯

 

쀑간 규λͺ¨ ν”„λ‘œμ νŠΈ (10,000~50,000쀄)

src/
β”œβ”€β”€ features/
β”‚   β”œβ”€β”€ feature1/
β”‚   β”‚   └── index.ts  # μ•„ν‚€ν…μ²˜ κ²½κ³„μ—λ§Œ 배럴 파일
β”‚   └── feature2/
β”‚       └── index.ts
└── shared/
    β”œβ”€β”€ ui/
    β”‚   └── index.ts  # λͺ…μ‹œμ  내보내기 μ‚¬μš©
    └── lib/
        └── index.ts  # λͺ…μ‹œμ  내보내기 μ‚¬μš©

 

λŒ€κ·œλͺ¨ ν”„λ‘œμ νŠΈ (50,000쀄 이상)

src/
β”œβ”€β”€ app/
β”œβ”€β”€ processes/
β”œβ”€β”€ pages/
β”‚   └── index.ts  # μ—„κ²©ν•˜κ²Œ μ œν•œλœ 배럴 파일
β”œβ”€β”€ widgets/
β”‚   └── index.ts  # μ—„κ²©ν•˜κ²Œ μ œν•œλœ 배럴 파일
β”œβ”€β”€ features/
β”‚   └── [각 κΈ°λŠ₯별]/
β”‚       └── index.ts  # 곡개 API만 λͺ…μ‹œμ  내보내기
β”œβ”€β”€ entities/
β”‚   └── [각 엔티티별]/
β”‚       └── index.ts  # 곡개 API만 λͺ…μ‹œμ  내보내기
└── shared/
    └── [각 λͺ¨λ“ˆλ³„]/
        └── index.ts  # μ„±λŠ₯ μΈ‘μ • ν›„ κ²°μ •
 

 

배럴 파일의 μ‚¬μš©μ€ κ²°κ΅­ 개발 κ²½ν—˜κ³Ό μ„±λŠ₯ μ‚¬μ΄μ˜ κ· ν˜•μ„ μ°ΎλŠ” 문제라 μƒκ°ν•©λ‹ˆλ‹€.

FSD μ•„ν‚€ν…μ²˜λ₯Ό λ„μž…ν•œλ‹€λ©΄, 배럴 파일의 μž₯단점을 μΈμ‹ν•˜κ³  ν”„λ‘œμ νŠΈμ˜ 규λͺ¨μ™€ νŒ€μ˜ ν•„μš”μ— 맞게 적절히 μ‘°μ ˆν•˜λŠ” 것이 μ€‘μš”ν•  것 κ°™μ•„μš”.

λͺ…ν™•ν•œ μ•„ν‚€ν…μ²˜ 경계, λͺ…μ‹œμ μΈ 내보내기, 그리고 ν˜„λŒ€μ μΈ λΉŒλ“œ 도ꡬλ₯Ό ν™œμš©ν•œλ‹€λ©΄, 배럴 파일의 이점을 μ·¨ν•˜λ©΄μ„œλ„ μ„±λŠ₯ 문제λ₯Ό μ΅œμ†Œν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€!

+ Recent posts