
FSD(Feature-Slice Design)는 프론트엔드 애플리케이션을 개발할 때 코드를 구조화하는 아키텍처 방법론입니다.
이 접근법은 주로 React, Vue, Angular 같은 프레임워크를 사용하는 복잡한 웹 애플리케이션에서 매우 유용합니다.
FSD는 비즈니스 로직과 사용자 인터페이스를 기능(feature) 단위로 분리하고, 이를 다시 여러 계층(layer)으로 나누어 관리합니다. 이렇게 하면 코드의 가독성, 재사용성, 유지보수성이 크게 향상됩니다.
슬라이스는 특정 비즈니스 기능을 구현하는 코드의 집합입니다. 각 슬라이스는 독립적으로 존재하며, 다른 슬라이스와 최소한의 의존성만 가집니다.
예를 들어, "사용자 관리", "상품 카탈로그", "장바구니" 등이 각각의 슬라이스가 될 수 있습니다.
FSD는 코드를 여러 계층으로 나눕니다. 각 계층은 특정 역할과 책임을 가지며, 상위 계층은 하위 계층에만 의존할 수 있습니다(단방향 의존성).
FSD의 가장 중요한 원칙 중 하나는 의존성이 항상 위에서 아래로만 흐른다는 것입니다. 즉, 상위 계층의 모듈은 하위 계층의 모듈을 사용할 수 있지만, 하위 계층의 모듈은 상위 계층의 모듈을 사용할 수 없습니다.
이 원칙을 통해 순환 의존성을 방지하고 코드를 더 예측 가능하게 만들 수 있습니다.
FSD 아키텍처에서는 다음과 같은 폴더 구조를 사용합니다:
src/
├── app/ # 애플리케이션 설정 및 진입점
├── processes/ # 비즈니스 프로세스
├── pages/ # 페이지 컴포넌트 및 라우팅
├── widgets/ # 재사용 가능한 UI 블록
├── features/ # 사용자 상호작용 기능
├── entities/ # 비즈니스 엔티티
└── shared/ # 공통 유틸리티 및 타입
각 계층에 대해 더 자세히 살펴보겠습니다:
애플리케이션의 최상위 계층으로, 다음과 같은 요소를 포함합니다:
app/
├── styles/ # 글로벌 스타일
├── providers/ # 전역 상태 제공자
├── config/ # 환경 설정
└── index.ts # 진입점
복잡한 비즈니스 프로세스를 구현하는 계층입니다. 페이지 간의 전환이나 여러 기능을 연결하는 흐름을 담당합니다.
processes/
├── authentication/ # 인증 프로세스
└── checkout/ # 결제 프로세스
애플리케이션의 라우팅 및 페이지 컴포넌트를 포함합니다. 각 페이지는 위젯과 피처를 조합하여 구성됩니다.
pages/
├── home/ # 홈 페이지
├── profile/ # 프로필 페이지
├── product/ # 상품 페이지
└── routes/ # 라우트 정의
재사용 가능한 UI 블록을 구현합니다. 위젯은 여러 피처를 조합하여 더 큰 UI 요소를 만듭니다.
widgets/
├── header/ # 헤더 위젯
├── sidebar/ # 사이드바 위젯
├── footer/ # 푸터 위젯
└── product-card/ # 상품 카드 위젯
사용자가 상호작용할 수 있는 기능을 구현합니다. 각 피처는 특정 사용자 작업(예: 로그인, 상품 추가, 필터링)에 초점을 맞춥니다.
features/
├── auth/
│ ├── login/ # 로그인 기능
│ └── register/ # 회원가입 기능
├── product/
│ ├── add-to-cart/ # 장바구니 추가 기능
│ └── filter/ # 상품 필터링 기능
└── user/
└── edit-profile/ # 프로필 편집 기능
비즈니스 엔티티(사용자, 상품, 주문 등)를 표현하는 계층입니다. 엔티티는 상태 관리 및 데이터 모델을 포함합니다.
entities/
├── user/ # 사용자 엔티티
├── product/ # 상품 엔티티
└── order/ # 주문 엔티티
프로젝트 전체에서 공유되는 유틸리티, 타입, UI 키트 등을 포함합니다.
shared/
├── api/ # API 클라이언트
├── config/ # 공통 설정
├── lib/ # 외부 라이브러리 래퍼
├── ui/ # UI 컴포넌트
└── utils/ # 유틸리티 함수
각 슬라이스(예: features/auth/login)는 내부적으로 다음과 같은 구조를 가질 수 있습니다:
features/auth/login/
├── ui/ # UI 컴포넌트
├── model/ # 상태 관리 (Redux, MobX 등)
├── api/ # API 요청
├── lib/ # 도메인별 유틸리티
├── config/ # 설정
└── index.ts # 퍼블릭 API
중요한 점은 index.ts 파일을 통해서만 외부에 노출할 요소를 선택적으로 내보내므로, 내부 구현 세부사항은 캡슐화됩니다.
모듈성: 각 기능이 독립적인 모듈로 구성되어 있어 코드의 가독성과 유지보수성이 향상됩니다.
확장성: 새로운 기능을 추가할 때 기존 코드를 변경하지 않고도 쉽게 확장할 수 있습니다.
명확한 의존성: 단방향 의존성 원칙으로 인해 코드의 의존성 구조가 명확해집니다.
재사용성: 독립적인 모듈로 구성되어 있어 다른 프로젝트에서도 쉽게 재사용할 수 있습니다.
테스트 용이성: 독립적인 기능 단위로 나눠져 있어 테스트하기 쉽습니다.
FSD를 효과적으로 적용하기 위한 몇 가지 실제 예시를 살펴보겠습니다:
features/auth/login/
├── ui/
│ ├── LoginForm.tsx # 로그인 폼 컴포넌트
│ └── LoginButton.tsx # 로그인 버튼 컴포넌트
├── model/
│ ├── store.ts # 로그인 상태 관리
│ └── actions.ts # 로그인 액션
├── api/
│ └── loginApi.ts # 로그인 API 요청
└── index.ts # 외부에 노출할 요소 정의
index.ts에서는 다음과 같이 내보냅니다:
// features/auth/login/index.ts
export { LoginForm } from './ui/LoginForm';
export { loginUser } from './model/actions';
export type { LoginFormData } from './model/types';
// pages/profile/ui/ProfilePage.tsx
import { EditProfileForm } from 'features/user/edit-profile';
import { UserInfo } from 'entities/user';
import { ProfileHeader } from 'widgets/profile-header';
export const ProfilePage = () => {
return (
<div>
<ProfileHeader />
<UserInfo />
<EditProfileForm />
</div>
);
};
과도한 분할 피하기: 너무 작은 단위로 슬라이스를 나누면 오히려 복잡성이 증가할 수 있습니다.
일관성 유지하기: 모든 팀원이 같은 규칙을 따르도록 문서화하고 코드 리뷰를 통해 일관성을 유지하세요.
단방향 의존성 원칙 지키기: 하위 계층에서 상위 계층으로의 의존성이 생기지 않도록 주의하세요.
적절한 크기의 모듈 유지하기: 한 슬라이스가 너무 커지면 추가적인 분할을 고려하세요.
FSD는 복잡한 프론트엔드 애플리케이션을 구조화하는 강력한 방법론입니다. 비즈니스 도메인에 따라 코드를 슬라이스하고, 각 슬라이스를 계층으로 나누는 접근법은 코드의 가독성, 유지보수성, 확장성을 크게 향상시킵니다.
FSD를 도입할 때는 팀의 규모, 프로젝트의 복잡성, 개발자의 경험을 고려하여 적절히 조정하는 것이 중요합니다. 모든 프로젝트에 동일한 구조를 적용하기보다는, 프로젝트의 특성에 맞게 유연하게 적용하는 것이 좋습니다.
이 아키텍처를 통해 개발자들은 더 명확한 코드 구조 속에서 효율적으로 협업하고, 사용자에게 더 나은 경험을 제공하는 애플리케이션을 만들 수 있을 것입니다.