249 lines
8.9 KiB
Markdown
249 lines
8.9 KiB
Markdown
# AS Application
|
|
|
|
Next.js 애플리케이션 with FSD(Feature Sliced Design) 아키텍처
|
|
|
|
## 기술 스택
|
|
|
|
- **프레임워크**: Next.js 15 + TypeScript
|
|
- **스타일링**: Tailwind CSS
|
|
- **상태관리**: Zustand
|
|
- **데이터 페칭**: TanStack Query (React Query)
|
|
- **폼 관리**: React Hook Form + Zod
|
|
- **테이블**: TanStack Table
|
|
- **HTTP 클라이언트**: Axios
|
|
- **날짜 처리**: Day.js
|
|
|
|
## 프로젝트 구조 (FSD)
|
|
|
|
이 프로젝트는 **Feature-Sliced Design v2.1**을 따르며, 계층적 아키텍처로 구성되어 있습니다.
|
|
|
|
```
|
|
as/
|
|
├── app/ # App 레이어 (Next.js App Router)
|
|
│ ├── (routes)/ # 라우트 구조
|
|
│ ├── layout.tsx # 루트 레이아웃
|
|
│ ├── page.tsx # 홈 페이지
|
|
│ ├── globals.css # 글로벌 스타일
|
|
│ └── providers.tsx # 전역 providers
|
|
│
|
|
├── widgets/ # Widgets 레이어
|
|
│ └── [widget-name]/ # 큰 독립적 UI 블록 (여러 페이지에서 재사용)
|
|
│ ├── ui/
|
|
│ ├── model/
|
|
│ └── index.ts
|
|
│
|
|
├── features/ # Features 레이어
|
|
│ └── [feature-name]/ # 사용자 상호작용 기능 (여러 페이지에서 재사용)
|
|
│ ├── ui/
|
|
│ ├── api/
|
|
│ ├── model/
|
|
│ └── index.ts
|
|
│
|
|
├── entities/ # Entities 레이어
|
|
│ └── [entity-name]/ # 비즈니스 엔티티 (User, Product 등)
|
|
│ ├── model/
|
|
│ ├── api/
|
|
│ ├── ui/
|
|
│ └── index.ts
|
|
│
|
|
└── shared/ # Shared 레이어
|
|
├── ui/ # UI Kit (비즈니스 로직 없는 공통 컴포넌트)
|
|
├── api/ # API 클라이언트 설정
|
|
├── lib/ # 내부 라이브러리 (dates, colors 등)
|
|
├── config/ # 환경 설정, feature flags
|
|
├── types/ # 공통 타입 정의
|
|
└── routes/ # 라우트 상수
|
|
```
|
|
|
|
### 계층 구조 이해하기
|
|
|
|
FSD는 **3단계 계층**으로 구성됩니다:
|
|
|
|
1. **Layers (레이어)**: 책임과 의존성에 따른 수평적 분리 (app, pages, widgets, features, entities, shared)
|
|
2. **Slices (슬라이스)**: 비즈니스 도메인에 따른 수직적 분리 (user, product, auth-login 등)
|
|
3. **Segments (세그먼트)**: 기술적 목적에 따른 분리 (ui, api, model, lib 등)
|
|
|
|
> 📚 각 레이어의 자세한 설명은 해당 폴더의 README.md를 참고하세요.
|
|
|
|
## FSD 레이어 설명
|
|
|
|
FSD는 계층적 아키텍처로, 각 레이어는 책임의 범위와 의존성의 정도에 따라 구분됩니다. 7개의 표준화된 레이어가 있으며, 프로젝트에 필요한 레이어만 사용하면 됩니다.
|
|
|
|
### 레이어 계층 구조 (위에서 아래로)
|
|
|
|
```
|
|
app/ ↑ 가장 높은 책임, 가장 많은 의존성
|
|
pages/ |
|
|
widgets/ |
|
|
features/ |
|
|
entities/ |
|
|
shared/ ↓ 가장 낮은 책임, 의존성 없음
|
|
```
|
|
|
|
### 1. App
|
|
- **목적**: 앱 전체에 영향을 주는 기술적/비즈니스적 사항
|
|
- **내용**: Context providers, 글로벌 스타일, 스토어 설정, 라우터 설정, 애널리틱스
|
|
- **특징**: 슬라이스가 없고 세그먼트로만 구성
|
|
- **의존성**: 모든 하위 레이어 사용 가능
|
|
|
|
### 2. Pages (app 디렉토리)
|
|
- **목적**: 웹사이트나 애플리케이션을 구성하는 페이지/화면
|
|
- **내용**: 페이지 UI, 로딩 상태, 에러 바운더리, 데이터 fetching
|
|
- **특징**: 한 페이지당 한 슬라이스 (유사한 페이지는 그룹화 가능)
|
|
- **의존성**: `widgets`, `features`, `entities`, `shared` 사용 가능
|
|
|
|
> 💡 **FSD v2.1 권장사항**: "pages-first" 접근 방식을 따라, 재사용되지 않는 큰 UI 블록과 비즈니스 로직은 페이지 슬라이스에 유지하세요.
|
|
|
|
### 3. Widgets
|
|
- **목적**: 큰 규모의 독립적인 UI 블록
|
|
- **내용**: 여러 features와 entities를 조합한 복합 UI
|
|
- **사용 시기**: 여러 페이지에서 재사용되거나, 페이지에 여러 개의 큰 독립적 블록이 있을 때
|
|
- **의존성**: `features`, `entities`, `shared` 사용 가능 (다른 `widgets` 불가)
|
|
|
|
### 4. Features
|
|
- **목적**: 사용자가 수행하는 주요 상호작용과 비즈니스 기능
|
|
- **내용**: 사용자 시나리오 (예: 로그인, 상품 검색, 댓글 작성)
|
|
- **사용 시기**: 여러 페이지에서 재사용되는 기능
|
|
- **의존성**: `entities`, `shared` 사용 가능 (다른 `features` 불가)
|
|
|
|
### 5. Entities
|
|
- **목적**: 비즈니스가 다루는 실제 개념 (User, Product, Order 등)
|
|
- **내용**: 데이터 모델, 타입, API 요청, 엔티티의 시각적 표현
|
|
- **특징**: 비즈니스 용어로 명명
|
|
- **의존성**: 다른 `entities`, `shared` 사용 가능
|
|
|
|
### 6. Shared
|
|
- **목적**: 애플리케이션의 기반 제공
|
|
- **내용**: API 클라이언트, UI Kit, 유틸리티 라이브러리, 글로벌 설정
|
|
- **특징**: 비즈니스 로직 없음, 슬라이스가 없고 세그먼트로만 구성
|
|
- **의존성**: 외부 라이브러리만 사용 가능 (다른 레이어 불가)
|
|
|
|
## 레이어 간 Import 규칙
|
|
|
|
> **핵심 규칙**: 슬라이스 내의 모듈(파일)은 **오직 하위 레이어**에 있는 다른 슬라이스만 import할 수 있습니다.
|
|
|
|
예시:
|
|
- `features/auth-login/ui/form.tsx`는 `entities/user`와 `shared/ui`를 import 가능
|
|
- 하지만 다른 `features/post-create`나 상위 레이어인 `widgets`는 import 불가
|
|
- 같은 슬라이스 내부(`features/auth-login/`)의 다른 파일들은 자유롭게 import 가능
|
|
|
|
## Public API 규칙
|
|
|
|
> **슬라이스 외부의 모듈은 슬라이스의 Public API만 참조할 수 있으며, 내부 파일 구조에 직접 접근할 수 없습니다.**
|
|
|
|
모든 슬라이스(및 슬라이스가 없는 레이어의 세그먼트)는 반드시 `index.ts`를 통해 Public API를 정의해야 합니다.
|
|
|
|
```typescript
|
|
// ✅ 올바른 import
|
|
import { LoginForm } from 'features/auth-login';
|
|
|
|
// ❌ 잘못된 import (내부 구조 직접 접근)
|
|
import { LoginForm } from 'features/auth-login/ui/login-form';
|
|
```
|
|
|
|
## 시작하기
|
|
|
|
```bash
|
|
# 의존성 설치
|
|
pnpm install
|
|
|
|
# 개발 서버 실행
|
|
pnpm run dev
|
|
|
|
# 프로덕션 빌드
|
|
pnpm run build
|
|
|
|
# 프로덕션 서버 실행
|
|
pnpm start
|
|
```
|
|
|
|
## 환경 변수
|
|
|
|
`.env.local` 파일 생성:
|
|
|
|
```env
|
|
NEXT_PUBLIC_API_URL=http://localhost:3000/api
|
|
```
|
|
|
|
## FSD 핵심 규칙
|
|
|
|
### 1. Import 규칙
|
|
|
|
> **슬라이스 내의 모듈(파일)은 오직 하위 레이어에 있는 슬라이스만 import할 수 있습니다.**
|
|
|
|
```typescript
|
|
// ✅ 올바른 import (하위 레이어)
|
|
// features/auth-login/ui/form.tsx
|
|
import { User } from 'entities/user';
|
|
import { Button } from 'shared/ui/button';
|
|
|
|
// ❌ 잘못된 import (같은 레이어)
|
|
// features/auth-login/ui/form.tsx
|
|
import { PostForm } from 'features/post-create'; // 금지!
|
|
|
|
// ❌ 잘못된 import (상위 레이어)
|
|
// features/auth-login/ui/form.tsx
|
|
import { Header } from 'widgets/header'; // 금지!
|
|
```
|
|
|
|
### 2. Public API 규칙
|
|
|
|
> **슬라이스 외부에서는 Public API(index.ts)를 통해서만 접근해야 합니다.**
|
|
|
|
```typescript
|
|
// ✅ 올바른 import (Public API 사용)
|
|
import { LoginForm } from 'features/auth-login';
|
|
|
|
// ❌ 잘못된 import (내부 구조 직접 접근)
|
|
import { LoginForm } from 'features/auth-login/ui/login-form';
|
|
```
|
|
|
|
**Public API 작성 시 주의사항**:
|
|
- 와일드카드 re-export(`export * from`) 사용 지양
|
|
- 필요한 것만 명시적으로 export
|
|
- 슬라이스의 인터페이스를 명확하게 표현
|
|
|
|
```typescript
|
|
// ❌ 나쁜 예시
|
|
export * from './ui/Comment';
|
|
export * from './model/comments';
|
|
|
|
// ✅ 좋은 예시
|
|
export { Comment } from './ui/comment';
|
|
export { CommentForm } from './ui/comment-form';
|
|
export { useComments } from './model/use-comments';
|
|
export type { Comment, CommentFormData } from './model/types';
|
|
```
|
|
|
|
### 3. 슬라이스 독립성
|
|
|
|
- **Zero Coupling**: 같은 레이어의 슬라이스들은 서로 독립적이어야 합니다
|
|
- **High Cohesion**: 슬라이스는 주요 목표와 관련된 대부분의 코드를 포함해야 합니다
|
|
- 예외: Entities 레이어에서 필요한 경우 `@x` 표기법으로 cross-import 가능
|
|
|
|
### 4. 세그먼트 명명 규칙
|
|
|
|
세그먼트 이름은 **목적**을 설명해야 하며, **본질**을 설명하면 안 됩니다:
|
|
|
|
- ❌ **나쁜 이름**: `components`, `hooks`, `types` (무엇인지만 설명)
|
|
- ✅ **좋은 이름**: `ui`, `api`, `model`, `lib` (왜 있는지 설명)
|
|
|
|
## 코드 규칙
|
|
|
|
- TypeScript 사용 (strict mode)
|
|
- 함수형 컴포넌트 사용
|
|
- Named exports 선호
|
|
- Tailwind CSS 사용
|
|
- ESLint + Prettier 적용
|
|
- Public API는 명시적으로 정의 (와일드카드 export 지양)
|
|
|
|
## 아키텍처 린팅
|
|
|
|
코드베이스의 FSD 규칙 준수를 자동으로 검증하려면 [Steiger](https://github.com/feature-sliced/steiger)를 사용하세요.
|
|
|
|
```bash
|
|
# Steiger 설치 및 실행
|
|
pnpm add -D @feature-sliced/steiger
|
|
npx steiger src
|
|
```
|