들어가며

대규모 프론트엔드 애플리케이션을 개발하면서 가장 큰 도전 과제 중 하나는 확장성과 유지보수성입니다. 프로젝트가 커질수록 구조가 복잡해지고, 기능 추가나 변경이 기존 코드에 의도치 않은 영향을 미칠 가능성이 커집니다.

이런 상황에서 Feature-Sliced Design(FSD) 은 유용한 아키텍처 패턴으로, 각 기능을 독립적이고 재사용 가능하게 모듈화하여 관리할 수 있도록 도와줍니다.

이번 글에서는 FSD의 기본 개념을 소개하고, 도입을 고민하는 분들을 위해 그 장점과 도입 시 유의해야 할 점들에 대해 알아보겠습니다.


왜 Feature-Sliced Design인가?

기존의 프론트엔드 구조는 주로 레이어링(예: 컴포넌트, 컨테이너, 유틸리티) 을 기반으로 나누어집니다. 하지만 이 방식은 시간이 지나면서 복잡해지기 쉽고, 변경의 여파가 여러 레이어에 걸쳐 확산될 수 있습니다. FSD는 이러한 문제를 해결하기 위해 기능 중심(Feature-Oriented) 으로 코드를 구성합니다.

즉, 화면(UI), 상태(State), 로직(Logic) 을 기능별로 하나의 모듈로 묶어, 기능 단위로 코드를 분리하는 방법입니다. 이를 통해 애플리케이션의 각 부분이 서로 독립적이면서도 필요한 경우 쉽게 재사용할 수 있습니다.


FSD의 기본 원칙

FSD는 크게 세 가지 원칙을 기반으로 합니다.

모듈화 (Modularity)

각 기능을 독립적으로 나누어 개발합니다. 한 기능은 상태 관리, 비즈니스 로직, UI를 모두 포함할 수 있으며, 해당 기능만을 담당하는 모듈로 관리됩니다. 이로 인해 새로운 기능을 추가하거나 기존 기능을 수정할 때 전체 애플리케이션에 미치는 영향을 최소화할 수 있습니다.

기능 중심 구조 (Feature-Oriented)

애플리케이션의 코드를 UI 컴포넌트나 로직별로 나누는 것이 아니라 기능 단위로 수직 분리합니다. 예를 들어, 로그인 기능과 사용자 프로필 기능은 각각 독립된 모듈로 나뉘며, 각 모듈은 자신의 비즈니스 로직과 상태 관리 로직을 포함합니다.

레이어링 (Layering)

FSD는 여러 레이어로 애플리케이션을 구성하는데, 각 레이어는 고유한 역할을 수행합니다. 이를 통해 코드의 역할을 명확하게 구분하고, 레이어 간의 의존성을 관리하여 복잡성을 줄입니다. 일반적으로 App, Pages, Widgets, Features, Entities, Shared라는 레이어로 나누어집니다.


FSD 폴더 구조 살펴보기

Feature-Sliced Design(FSD)을 처음 도입하려는 개발자라면, 가장 먼저 궁금해할 부분이 폴더 구조일 것입니다.

FSD의 폴더 구조는 크게 Layers, Slices, Segments로 구분됩니다.


Layers

애플리케이션의 전반적인 책임을 수평적으로 나눈 것입니다. 각 레이어는 전체 애플리케이션의 특정 부분을 관리하고, 각 레이어는 독립적인 모듈로 동작합니다. 이 구조는 기능의 책임을 명확히 분리하고, 레이어 간의 의존성을 최소화하도록 돕습니다.

src/
  ├─ app/              # 전역 설정 및 상태 관리
  ├─ pages/            # 라우팅된 페이지 모음
  ├─ widgets/          # 페이지에 사용되는 독립적인 UI 컴포넌트
  ├─ features/         # (optional) 개별 기능 모듈
  ├─ entities/         # (optional) 도메인 엔터티 및 비즈니스 로직
  └─ shared/           # 공통 컴포넌트 및 유틸리티

app

애플리케이션의 전반적인 설정과 상태 관리를 담당합니다. 여기에는 라우팅 설정, 글로벌 스타일, 상태 관리 관련 코드들이 포함됩니다.

pages

각 라우트에 대응하는 페이지들이 위치하며, 애플리케이션 내에서 사용자가 접근하는 구체적인 화면입니다.

widgets

위젯은 페이지 단위에서 재사용되는 UI 요소를 정의하는 폴더입니다.

features (optional)

개별 기능들을 독립적인 모듈로 관리하는 폴더입니다. 로그인, 회원가입, 사용자 프로필 등 기능별로 분리되어 있습니다. 선택적 레이어입니다.

entities (optional)

도메인 엔터티와 비즈니스 로직을 관리하는 폴더로, 주로 데이터 모델과 관련된 코드들이 위치합니다. 선택적 레이어입니다.

shared

여러 레이어에서 공통적으로 사용되는 컴포넌트나 유틸리티 함수들이 위치하는 폴더입니다.


Slices

기능별로 애플리케이션을 수직으로 나눈 것입니다. 각 슬라이스는 특정 기능을 독립적으로 처리하며, 하나의 기능에 필요한 상태 관리, UI, 비즈니스 로직 등을 모두 포함합니다.

src/
  ├─ features/
  │   ├─ auth/            # 인증 기능
  │   └─ user-profile/     # 사용자 프로필 기능

Slice의 이름은 프로젝트의 비즈니스 영역에 따라 직접 결정되므로 표준화되어 있지 않습니다. 밀접하게 관련된 조각들은 구조적으로 디렉토리 내에 그룹지을 수 있지만, 이 디렉토리에 있는 코드는 직접적으로 공유되지 않아야 합니다.


Segments

슬라이스 내부에서 더 세부적인 역할로 나누어진 폴더입니다. 각 슬라이스는 상태 관리, UI 컴포넌트, 유틸리티 등을 세그먼트로 나누어 관리할 수 있습니다.

src/
  ├─ features/
  │   ├─ auth/
  │   │   ├─ ui/              # 인증 관련 UI 컴포넌트
  │   │   ├─ model/           # 인증 상태 관리 및 비즈니스 로직
  │   │   └─ lib/             # 인증 관련 유틸리티 및 API 호출
  │   └─ user-profile/
  │       ├─ ui/              # 사용자 프로필 UI 컴포넌트
  │       ├─ model/           # 사용자 프로필 상태 관리
  │       └─ lib/             # 유틸리티 및 API 호출 로직

segment도 팀의 합의에 따라 구성과 이름이 변경될 수 있습니다. 일반적으로 사용되는 세그먼트는 다음과 같습니다.

api
서버 요청을 처리하는 로직이 위치하는 세그먼트입니다. 외부 API와의 통신이나 데이터 요청 로직을 담습니다.

ui
기능과 관련된 UI 컴포넌트들이 위치합니다. 슬라이스 내에서 특정 기능에 필요한 모든 UI 요소가 여기에 포함됩니다.

model
비즈니스 로직과 상태 관리 로직을 처리하는 세그먼트입니다. 여기에는 actions, selectors, 상태 관리와 관련된 로직이 포함됩니다.

lib
슬라이스 내부에서 사용되는 보조 기능을 담습니다. 유틸리티 함수나 기타 로직이 여기에 위치합니다.

config
슬라이스에 필요한 구성값이나 설정이 포함된 세그먼트입니다.

consts
슬라이스에서 사용하는 상수를 정의하는 세그먼트입니다.


FSD 도입의 장점

로그인 기능을 수정해야 하지만, 프로필 기능에는 영향을 주고 싶지 않은 경우를 가정해봅시다.

src/
├── features/
│   ├── authentication/
│   │   ├── model/
│   │   │   └── useAuth.ts   # 로그인 및 세션 상태 관리
│   │   ├── ui/
│   │   │   └── LoginForm.tsx # 로그인 UI 컴포넌트
│   │   └── api/
│   │       └── authAPI.ts   # 인증 관련 API 호출
│   └── userProfile/
│       ├── model/
│       │   └── useUserProfile.ts # 사용자 프로필 관리 훅
│       ├── ui/
│       │   └── ProfileCard.tsx   # 사용자 프로필 카드 UI
│       └── api/
│           └── profileAPI.ts     # 프로필 API 호출

Authentication과 User Profile 기능이 분리되어 있으므로, 로그인 기능을 수정해도 프로필 기능에는 영향을 주지 않습니다. 이렇게 기능을 독립적으로 모듈화하면, 코드 변경이 다른 기능에 영향을 미치는 문제를 방지할 수 있습니다.

만약 여러 페이지에서 인증 상태를 확인하는 훅이 필요하다면, shared 폴더에 해당 훅을 정의하고 필요한 곳에서 재사용할 수 있습니다.

src/
├── features/
│   ├── authentication/
│   │   ├── model/
│   │   │   └── useAuth.ts # 인증 상태 관리 훅
│   │   └── api/
│   │       └── authAPI.ts # 인증 관련 API 호출
│   └── dashboard/
│       ├── model/
│       │   └── useDashboardData.ts # 대시보드 데이터 훅
│       ├── ui/
│       │   └── Dashboard.tsx  # 대시보드 UI 컴포넌트
│       └── api/
│           └── dashboardAPI.ts # 대시보드 API
└── shared/
    └── hooks/
        └── useAuth.ts # 재사용 가능한 인증 훅 (각 기능에서 import 가능)

이렇게 공통 모듈을 shared 폴더에 정리해 두면, 중복 코드를 줄이고 코드의 재사용성을 높일 수 있습니다.

또한, FSD는 기능별로 모듈화되어 있기 때문에, 특정 기능을 추가하거나 수정할 때 다른 기능에 영향을 미치지 않습니다. 만약 한 사람은 게시글을 개발하고, 다른 사람은 댓글 기능을 개발한다면,

src/
├── features/
│   ├── article/
│   │   ├── model/
│   │   │   └── useArticle.ts   # 게시글 상태 관리 훅
│   │   ├── ui/
│   │   │   ├── ArticleCard.tsx # 게시글 카드 UI
│   │   │   └── ArticleList.tsx # 게시글 리스트 UI
│   │   └── api/
│   │       └── articleAPI.ts   # 게시글 API 호출
│   └── comment/
│       ├── model/
│       │   └── useComment.ts   # 댓글 상태 관리 훅
│       ├── ui/
│       │   └── CommentList.tsx # 댓글 리스트 UI
│       └── api/
│           └── commentAPI.ts   # 댓글 API 호출

게시글 관련 기능을 수정해도 다른 사람의 댓글 기능에 영향이 가지 않습니다. 이렇게 기능을 독립적으로 모듈화하면, 협업할 때에도 코드 충돌을 방지할 수 있습니다.


도입 시 고려사항

FSD는 분명 강력한 패턴이지만, 처음 도입할 때는 몇 가지 유의해야 할 점이 있습니다.

복잡성 증가

FSD는 기능 중심으로 코드를 모듈화하다 보니, 작은 프로젝트에서는 오히려 복잡하게 느껴질 수 있습니다. 따라서 프로젝트의 규모와 복잡도를 고려하여 도입을 결정하는 것이 중요합니다.

처음 설계 시 많은 고민 필요

FSD는 기능을 기준으로 모듈화하는 패턴이기 때문에, 처음에 기능을 어떻게 분리할지, 어떤 부분을 모듈로 나눌지에 대한 많은 고민이 필요합니다. 초반 설계에 시간이 다소 소요될 수 있지만, 장기적으로는 유지보수성 면에서 이점을 제공합니다.

팀원들의 이해 필요

FSD를 도입하려면 이 패턴에 대한 팀원들의 이해가 필요합니다. 폴더 구조나 레이어링 원칙, 기능 모듈화의 장점을 충분히 이해하고 있어야 효율적으로 작업을 진행할 수 있습니다.


FSD 도입을 위한 실용적인 팁

작은 프로젝트부터 시작하기

처음부터 대규모 프로젝트에 FSD를 적용하기보다는, 작은 규모의 프로젝트나 기존 프로젝트의 일부에 먼저 도입해 보는 것이 좋습니다. 이를 통해 FSD의 장단점을 파악하고, 실제로 팀에서 잘 동작하는지 실험해 볼 수 있습니다.

공통 모듈을 우선적으로 정리하기

shared 폴더에 공통 유틸리티와 컴포넌트를 먼저 정리해 두면, 기능 모듈을 작성할 때 유용합니다. 반복적으로 사용하는 컴포넌트와 함수는 공통 모듈로 정리하여 코드 중복을 줄일 수 있습니다.

기능별 레이어링은 점진적으로 도입하기

처음부터 모든 레이어를 엄격히 구분하지 말고, 프로젝트가 성장함에 따라 필요한 레이어를 점진적으로 추가하는 방식으로 접근하는 것이 좋습니다. 이를 통해 과도한 구조화로 프로젝트가 복잡해지는 것을 막을 수 있습니다.


마치며

Feature-Sliced Design은 규모가 커지고 복잡해지는 애플리케이션에서 기능을 독립적으로 모듈화하여 유지보수성과 확장성을 높일 수 있는 강력한 아키텍처 패턴입니다. 초기 도입 시 설계에 대한 고민이 필요하지만, 올바르게 도입되면 장기적인 유지보수와 확장에 큰 도움을 줄 수 있습니다.

프론트엔드 애플리케이션에서 기능 중심의 모듈화가 필요하다고 느끼신다면, FSD를 도입해 보세요. 만약 대규모 프로젝트에서 효과적으로 코드를 관리할 수 있는 방법을 찾고 있다면, FSD는 유용한 선택이 될 것입니다.