Feature-Sliced Design(FSD)은 프론트엔드 애플리케이션을 위한 아키텍처 방법론으로, 코드를 기능 중심으로 모듈화하고 각 기능을 독립적으로 관리하는 방식입니다. 이 아키텍처는 특히 대규모 프론트엔드 애플리케이션에서 코드의 유지보수성과 재사용성을 높이기 위해 설계되었습니다.
FSD의 핵심 원칙은 다음과 같습니다:
FSD 아키텍처는 세 가지 주요 구성 요소로 이루어져 있습니다:
레이어는 코드의 최상위 구조로, 각각 특정 책임 영역을 가집니다. 레이어는 다음과 같은 계층 구조를 형성합니다 (위에서 아래로):
중요한 규칙: 하위 레이어는 상위 레이어에 의존할 수 없습니다. 예를 들어, Entities는 Features를 import할 수 없습니다.
각 레이어 내에서 코드는 비즈니스 도메인에 따라 슬라이스로 나뉩니다. 슬라이스는 특정 비즈니스 엔티티나 기능을 중심으로 구성됩니다.
예시:
각 슬라이스는 목적에 따라 세그먼트로 더 세분화됩니다. 일반적인 세그먼트는 다음과 같습니다:
애플리케이션의 진입점으로, 다음을 포함합니다:
src/
└── app/
├── providers/
├── styles/
├── routes/
└── index.ts
현재는 거의 사용되지 않는 레이어입니다. 여러 페이지에 걸친 복잡한 프로세스(예: 다단계 등록)를 관리합니다.
애플리케이션의 페이지를 정의합니다. 각 페이지는 위젯, 기능, 엔티티를 조합하여 구성됩니다.
src/
└── pages/
├── HomePage/
├── ProfilePage/
├── ProductPage/
└── ...
재사용 가능한 UI 블록으로, 여러 기능과 엔티티를 조합하여 더 큰 UI 구성 요소를 만듭니다.
src/
└── widgets/
├── Header/
├── Sidebar/
├── Footer/
└── ...
사용자 시나리오와 비즈니스 기능을 구현합니다. 사용자 상호작용과 관련된 로직을 포함합니다.
src/
└── features/
├── UserAuth/
│ ├── ui/
│ ├── model/
│ ├── api/
│ └── index.ts
├── AddToCart/
└── ...
비즈니스 도메인 모델과 관련된 모든 것을 포함합니다. 데이터 구조와 기본 조작 방법을 정의합니다.
src/
└── entities/
├── User/
│ ├── ui/
│ ├── model/
│ ├── api/
│ └── index.ts
├── Product/
└── ...
프로젝트 전체에서 사용되는 공통 코드를 포함합니다. 비즈니스 로직에 종속되지 않은 재사용 가능한 요소들을 담습니다.
src/
└── shared/
├── ui/
├── lib/
├── api/
└── ...
Q: Features와 Entities의 차이점은 무엇인가요?
A: Features와 Entities는 다음과 같은 차이점이 있습니다:
Entities (엔티티):
Features (기능):
Q: 특정 페이지에서만 사용되는 기능(예: 메인 페이지의 팔로우 기능)은 어디에 배치해야 하나요?
A: 특정 페이지에서만 사용되는 기능의 배치는 다음 기준으로 결정할 수 있습니다:
재사용 가능성이 있는 경우: Features 레이어에 배치
src/features/followUser/
페이지에 특화되어 있고 재사용 가능성이 없는 경우: Pages 레이어 내부에 배치
src/pages/MainPage/model/followFeature.ts
결정 기준:
Q: 메인 페이지의 내부 전용 훅들과 API는 어디에 배치해야 하나요? 비즈니스 로직을 페이지 내부에 넣는 것이 적절한가요?
A: 페이지 내부 로직은 다음과 같이 분류하여 배치할 수 있습니다:
페이지 전용 UI 로직: Pages/MainPage/ui/
페이지 특화 비즈니스 로직: Pages/MainPage/model/
페이지 전용 API 호출: Pages/MainPage/api/
비즈니스 로직을 페이지 내부에 넣는 것은 다음 경우에만 적절합니다:
그 외의 경우, 특히 중요한 비즈니스 로직은 적절한 레이어(Entities 또는 Features)로 분리하는 것이 좋습니다.
Q: 세그먼트 분류에서 model은 무엇을 의미하나요? React hooks는 어디에 배치해야 하나요?
A: FSD에서 'model' 세그먼트와 React hooks 배치는 다음과 같습니다:
model 세그먼트:
React hooks 배치:
1. model 폴더에 배치하는 hooks:
useUserState, useProductFilteruseDebounce, useLocalStoragehooks의 배치는 그 목적과 책임에 따라 결정됩니다. 상태 관리나 비즈니스 로직을 다루는 hooks는 model에, 순수 유틸리티 성격의 hooks는 lib에 배치하는 것이 적절합니다.
// entities/user/model/types.ts
export interface User {
id: string;
name: string;
email: string;
avatar: string;
}
// entities/user/api/index.ts
export const fetchUser = async (id: string): Promise<User> => {
const response = await fetch(`/api/users/${id}`);
return response.json();
};
// entities/user/ui/UserCard.tsx
export const UserCard = ({ user }: { user: User }) => (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
</div>
);
// features/followUser/model/index.ts
import { User } from 'entities/user';
export const followUser = async (userId: string): Promise<void> => {
await fetch(`/api/users/${userId}/follow`, { method: 'POST' });
// 상태 업데이트 로직...
};
// features/followUser/ui/FollowButton.tsx
import { followUser } from '../model';
export const FollowButton = ({ userId }: { userId: string }) => {
const [isFollowing, setIsFollowing] = useState(false);
const handleFollow = async () => {
await followUser(userId);
setIsFollowing(true);
};
return (
<button onClick={handleFollow}>
{isFollowing ? '팔로잉' : '팔로우'}
</button>
);
};