Skip to content

FSD(Feature-Sliced-Design)

회사 프로젝트를 진행하면서, 규모가 커질수록 유지보수성과 확장성, 협업 효율성에 대한 고민이 있었다.
상위 폴더 구조외에 명확한 설계 기준이 없는 상태에서 기능이 계속 추가되다 보니 전체 구조를 파악할 때 어려웠고, 직접 작업할 때도 어디에 위치하여 어떤 구조로 진행하는 것이 좋을지 고민하는 시간도 있었다.
해당 문서는 기존 구조의 한계를 돌아보며, 확장성 및 유지보수성을 높이기 위한 Feature-Sliced-Design (FSD) 아키텍처를 정리해보고자 한다.

FSD 아키텍처

프론트엔드 애플리케이션 구조를 위한 아키텍처 방법론이다.
코드를 어떻게 분리하고 구성할지를 명확히 정의하여, 변화하는 비즈니스 요구 속에서 프로젝트를 이해하기 쉽고 안정적으로 유지할 수 있도록하고, 코드의 책임을 분리하여 쉽게 유지보수 및 접근할 수 있는 것을 목표로 한다.

제가 생각한 FSD 의 철학은 아래와 같다.

  • UI 가 아닌 비즈니스 기능 중심으로 구조를 설계한다.
  • 기능 단위로 코드를 캡슐화하여 의존성을 명확히 관리한다.
  • 프로젝트가 커질수록 구조적 이점이 커지도록 설계한다.

FSD 아키텍처 이를 위해 FSD 는 Layer → Slice → Segment 로 이어지는 명확한 계층 구조를 정의한다.

구분역할특징 및 규칙
레이어 (Layers)코드의 재사용 범위와 의존성 규칙에 따라 수직적으로 구분한 구조상위 레이어는 하위 레이어만 참조할 수 있는 단방향 의존성을 가진다.
(app → pages → widgets → features → entities → shared)
슬라이스 (Slices)하나의 레이어 안에서 코드를 비즈니스 도메인 기준으로 분리같은 레이어 내에서는 다른 슬라이스를 직접 참조하지 않는다.
이를 통해 높은 응집도와 낮은 결합도를 유지한다.
세그먼트 (Segments)슬라이스 내부에서 코드를 기술적 목적에 따라 구분ui, api, model역할이 명확한 이름을 사용한다.
components, hooks처럼 범용적인 이름은 지양한다.

Layers (레이어)

레이어는 FSD 구조의 최상위 계층으로, React 기준 src 디렉터리 하위에 위치한다.
레이어는 역할에 따라 명확히 구분되며, 표준적으로 다음과 같은 종류를 가진다.

구분설명특이사항
App애플리케이션 전반에 영향을 주는 설정과 초기화 코드라우터 설정, 진입점, 전역 상태 관리, 전역 스타일, 각종 Provider 등을 포함하며 slice 없이 segment로만 구성된다.
Processes페이지 간 흐름을 포함하는 복잡한 사용자 시나리오현재는 더 이상 사용되지 않는 레이어이다.
Pages전체 페이지 단위 또는 중첩 라우팅에서의 주요 화면 구성일반적으로 페이지 1개당 슬라이스 1개를 사용하며, 유사한 페이지는 하나의 슬라이스로 묶을 수 있다.
Widgets독립적으로 동작하며 여러 페이지에서 재사용 가능한 비교적 큰 단위의 기능 또는 UI보통 하나의 완결된 기능 단위를 담당한다.
Features제품 전반에서 재사용되는 기능 구현으로, 사용자에게 직접적인 비즈니스 가치를 제공클릭, 결제, 신청 등 동사적 개념의 기능을 담당한다.
Entities프로젝트에서 다루는 핵심 비즈니스 데이터와 그와 관련된 로직사용자, 주문, 상품 등 명사적 개념을 표현한다.
Shared여러 레이어에서 공통으로 사용하는 기본 구성 요소 모음프로젝트나 비즈니스와 강하게 결합되지 않은 재사용 코드가 중심이며, slice 없이 segment로만 구성된다.

processes 레이어는 현재 deprecated 되었으며, 신규 프로젝트에서는 사용하지 않는 것이 권장된다.

의존성 규칙

상위 레이어는 하위 레이어만 참조할 수 있다.
예를 들어 pages는 features, entities, shared를 참조할 수 있지만, 그 반대는 허용되지 않는다.
이러한 규칙은 의존성 흐름을 단방향으로 유지하여 코드 복잡도를 줄이고, 구조적 안정성을 높여준다.

※ 참고 문서
React Query와 함께 사용하기
NextJS와 함께 사용하기
Electron와 함께 사용하기

Slice (슬라이스)

슬라이스는 레이어 하위에 위치하는 두 번째 계층으로, 기능 단위의 묶음을 의미한다.
슬라이스의 이름은 표준화되어 있지 않으며, 프로젝트의 비즈니스 도메인에 따라 자유롭게 정의할 수 있다.

appshared 레이어슬라이스를 가지지 않고, 바로 세그먼트로 구성된다.

⭐ 모든 슬라이스는 반드시 엔트리 포인트를 가져야한다.

  • 슬라이스 내부 파일을 직접 import 할 수 없다.
  • 반드시 index.ts와 같은 엔트리 포인트를 통해 접근 가능한다.

이를 통해 슬라이스 내부 구현을 외부로부터 숨기고, 필요한 인터페이스만 노출할 수 있다.
결과적으로 모듈 간 결합도가 낮아지고, 변경에 강한 구조를 만들 수 있게 된다.

Segment (세그먼트)

세그먼트는 슬라이스 내부에서 역할에 따라 코드를 분리하는 마지막 계층이다.
일반적으로 다음과 같은 세그먼트 구성이 많이 사용된다.

네이밍설명예시
ui화면 표시와 사용자 인터페이스에 관련된 코드UI 컴포넌트, 스타일, 날짜 포맷터 등
api백엔드와의 통신 및 데이터 처리 로직request 함수, API 타입, mapper 등
model/store애플리케이션의 도메인 모델과 상태 관리 로직스키마, 인터페이스, 스토어, 비즈니스 로직
(Redux / Zustand의 action, reducer, selector 등)
types해당 슬라이스에서 사용하는 타입 정의 전용 코드DTO, API 응답 타입, Props 타입 등
lib해당 슬라이스 내부에서만 사용하는 공통 유틸리티헬퍼 함수, 내부 전용 유틸 코드
config기능과 관련된 설정 값 및 feature flag환경별 설정, 플래그 정의 파일 등

장점

  • 일관성 있는 프로젝트 구조
    표준화된 구조를 제공하여, 프로젝트 규모가 커지더라도 전체 구조를 예측하기 쉽다.

  • 유지보수성과 확장성 향상
    기능 단위로 코드가 분리되어 있어 특정 기능을 수정하더라도 다른 영역에 미치는 영향이 적고, 새로운 기능 추가 역시 비교적 수월한다.

  • 협업 효율성
    각 기능이 독립적으로 구성되어 있어 팀원 간 작업 충돌을 최소화할 수 있다.

  • 비즈니스 중심 설계
    UI가 아닌 기능을 중심으로 구조를 설계하기 때문에, 요구사항 변경 시 관련 기능을 빠르게 찾아 수정할 수 있다.

단점

  • 오버 엔지니어링의 가능성
    규모가 작거나 단순한 서비스에서는 구조가 과도하게 느껴질 수 있다.

  • 초기 학습 비용
    개념과 규칙이 명확한 만큼, 팀이 익숙해지기까지 일정한 학습 시간이 필요한다.

  • 팀 내 합의의 중요성
    FSD는 개인보다는 팀 단위에서 효과가 커지므로, 공통된 규칙과 컨벤션을 지속적으로 유지하는 것이 중요하다.

비교하기

React 기반으로 프로젝트를 진행하다 보면, 보통 아래와 같은 폴더 구조를 사용하게 된다.

src/
 ├─ apis
 ├─ components
 ├─ pages
 ├─ mocks
 ├─ constants
 ├─ hooks
 ├─ styles
 ...

이 구조에서는 코드가 무엇을 하는지보다는,
해당 코드가 어떤 역할이나 성격을 가지는지를 기준으로 분리하는 경우가 많다.

반면, FSD를 기준으로 프로젝트를 구성하면 다음과 같은 구조를 가지게 된다.
appshared 레이어는 슬라이스 없이 세그먼트로만 구성되는 것이 특징이다.

src/
 ├─ app/
 │   ├─ providers/
 │   ├─ routes/
 │   └─ index.ts

 ├─ pages/
 │   ├─ home/
 │   └─ dashboard/

 ├─ widgets/
 │   ├─ header/
 │   └─ footer/

 ├─ features/
 │   ├─ auth/
 │   │   ├─ ui/
 │   │   ├─ api/
 │   │   ├─ model/
 │   │   └─ types/
 │   │
 │   └─ ...

 ├─ entities/
 │   ├─ user/
 │   │   ├─ model/
 │   │   └─ types/
 │   │
 │   └─ ...

 └─ shared/
     ├─ ui/
     ├─ api/
     ├─ lib/
     ├─ hooks/
     ├─ styles/
     └─ index.ts

즉, 기존 구조가 코드의 성격을 기준으로 분리한다면,
FSD는 코드가 속한 기능과 도메인을 기준으로 구조를 설계한다.

기존

components/SignInForm.tsx
apis/auth.ts
hooks/useSignIn.ts
types/auth.ts
modles/auth.ts

FSD

features/
 └─ auth/
     ├─ ui/
     │   └─ SignInForm.tsx
     ├─ api/
     │   └─ signIn.ts
     ├─ hooks/
     │   └─ useSignIn.ts
     └─ types/
         └─ auth.ts

마무리

FSD는 모든 프로젝트에 반드시 적용해야 하는 정답은 아니라고 생각한다.
하지만 프로젝트 규모가 커질수록 구조로 인한 고민이 반복되고 있다면,
코드를 기능과 도메인 중심으로 바라볼 수 있는 하나의 좋은 기준이 될 수 있다.

이번 정리를 통해 단순히 폴더 구조를 바꾸는 것이 아니라,
코드를 어떤 기준으로 바라보고 나눌 것인가에 대해 다시 생각해보는 계기가 되었다.

앞으로는 프로젝트의 규모와 팀 상황에 맞게 FSD의 개념을 선택적으로 적용하며,
과도한 구조보다는 문제를 해결하는 데 도움이 되는 방향으로 활용해보려고 한다.