근래에 원티드 프리온보딩 챌린지(2024-11) 강의를 듣다가 FSD 패턴에 대해 관심을 가지게 되었습니다. 이 패턴의 이론적인 개념에 대해서는 처음 접한 것은 아니지만 실제로 어떻게 적용해야 할지에 대해서는 감이 잘 잡히지 않았습니다. 이번 강의의 사전 과제를 FSD 패턴을 통해 적용해보며 맛보기를 시도했고, 그 과정에서 느낀 개인적인 생각을 공유드리려 합니다.
본 게시글에서는 FSD 패턴의 이론적인 개념도 어느 정도 다루겠지만, 특히 적용 과정에서 개인적으로 어려웠거나 고민이 되었던 부분에 대한 이야기가 주를 이룰 예정입니다.
FSD 패턴이란?
FSD 패턴에 대해 공식 문서에서는 Feature-Sliced Design (FSD) 는 프론트엔드 애플리케이션의 구조를 잡는 아키텍처 방법론 이라고 설명하고 있습니다. 즉, 어떠한 기능을 직접적으로 제공해주는 것이 아니라 비즈니스의 규모가 커지고 함께 개발하는 사람들이 많아질 때 효율적으로 협업할 수 있게 해주는 큰 틀의 가이드라인을 제시해주는 방법론이라고 볼 수 있을 것 같습니다.
주요 개념
위 그림과 같이 FSD 패턴에서는 Layers, Slices, Segments로 이루어져 있으며, 그 중 App과 Shared 레이어를 제외하고는 Segments를 포함한 세 개의 구조를 모두 갖추고 있습니다.
레이어
레이어는 FSD 패턴에서 첫 번째 구조 수준입니다. Layer의 목적은 코드의 책임 범위와 애플리케이션 내 다른 모듈에 대한 의존성 정도에 따라 코드를 구분하는 것입니다. 레이어에서는 상위 레이어에서 하위 레이어는 참조할 수 있지만, 하위 레이어에서 상위 레이어는 참조할 수 없습니다.
- App: 앱 전체와 관련된 모든 사항들을 포함합니다. (예: Routing, Provider 등)
- Processes: 더 이상 사용되지 않습니다.
- Page: 페이지 전체 또는 페이지의 주요 부분을 담당합니다.
- Widgets과 비슷한 특성을 가지지만, 더 큰 규모에서 사용됩니다. 이 레이어에서 각 slices 구조는 UI 컴포넌트와 데이터 조회 로직을 포함한 비즈니스 로직 등을 모두 포함합니다.
- Widgets: entities 및 feature와 같은 하위 레벨 단위의 구성에서 나온 자급자족적인 UI 블록입니다.
- 이 레이어에는 비즈니스 로직이 포함되지 않으며, 준비된 UI 컴포넌트나 비즈니스 로직과는 상관없는 키보드 상호작용 등이 포함될 수 있습니다.
- Features: 사용자가 비즈니스 entity와 상호작용하여 가치 있는 결과를 얻기 위해 서비스에서 수행할 수 있는 작업입니다.
- 이 레이어의 각 slices는 상호작용 가능한 UI 요소, 내부 상태 및 API 호출을 포함한 가치를 생성하는 동작을 가능하게 합니다.
- Entities: 프로젝트의 본질을 이루는 현실 세계의 개념들입니다.
- 이 레이어의 각 slices는 정적인 UI 요소, 데이터 저장소 및 CRUD 작업을 포함합니다.
- Shared: 프로젝트나 비즈니스의 세부 사항과 분리된 독립적인 모듈, 컴포넌트 추상화입니다.
- UI를 담당하는 공용 컴포넌트, API client 등이 있을 수 있습니다.
슬라이스
슬라이스는 비즈니스 도메인별로 코드를 분할합니다. 각 슬라이스의 이름은 비즈니스에 따라 자유롭게 설정할 수 있으며, 같은 레이어 안에서 다른 슬라이스를 참조할 수 없습니다.
- features/todo/TodoList.tsx > features/todo/TodoItem.tsx → 가능
- features/todo/TodoList.tsx > features/user/userType.ts → 불가능
세그먼트
세그먼트는 목적에 따라 코드를 그룹화합니다. 이름에 제한은 없지만, 일반적으로 다음과 같이 구분됩니다.
ui
- UI와 관련된 모든 것: UI 컴포넌트, 날짜 포맷터, 스타일 등.api
- 백엔드 상호작용: request 함수, 데이터 타입, mapper 등.model
- 데이터 모델: 스키마, 인터페이스, 스토어, 비즈니스 로직.lib
- 슬라이스 안에 있는 다른 모듈이 필요로 하는 라이브러리 코드.config
- 설정 파일과 기능 플래그.
여기까지가 공식 문서를 참고하여 작성한 FSD에 대한 내용입니다. 프로젝트의 폴더 구조를 기능별로 상세하게 나누는 것까지는 이해가 되었으나, 사실 이렇게 읽기만 해서는 잘 와닿지 않습니다. 이제부터는 공식 문서의 설명이 아닌, 제가 직접 작성한 TodoList 코드에 FSD 패턴을 적용하며 얻은 인사이트를 공유해보겠습니다. 공부가 목적이었기 때문에 최대한 모든 구조를 사용하고자 하였고, 그 과정에서 다소 과하다고 느껴지는 부분이 있을 수 있는 점 참고 부탁드립니다.
폴더 구조
📂 app
📁 providers
📁 routers
📁 styles
📂 pages
📂 widgets
📂 authForms
📂 ui
📄 SignUpForm.tsx
📂 todoList
📂 ui
📄 TodoList.tsx
📂 features
📂 todo
📂 api
📄 todos.ts
📂 hooks
📄 useTodoSearch.ts
📂 lib
📄 validate.ts
📂 types
📂 ui
📄 TodoItem.tsx
📂 entities
📂 user
📂 api
📄 auth.ts
📂 lib
📄 validate.ts
📂 types
📂 shared
📁 api
📄 axiosInstance.ts
📄 queryClient.ts
📁 const
📄 routes.ts
📄 time.ts
📁 lib
📄 storageController.ts
📁 types
📁 ui
📄 CommonButton.tsx
📄 CommonInput.tsx
개인적으로 위 폴더 구조를 만들어가는 과정에서 가장 어려웠던 부분은 widgets
, features
, entities
를 나누는 과정이었습니다. 그러다 문득 든 생각은 FSD 패턴은 아키텍처 방법론일 뿐이며, 비즈니스별로 같은 개념이더라도 다른 레이어에 위치할 수 있겠다는 것이었습니다. 이 부분에 대해 각 레이어별로 구체적으로 설명하겠습니다:
- App:
App.tsx
,main.tsx
를 포함한 앱의 실행과 관련된 가장 큰 부분들이 위치하고 있습니다. - Pages: 각 라우팅 페이지를 포함하고 있습니다.
- Widgets: 여기서부터는 고민이 많았던 부분인데,
SignUpForm.tsx
와 같은 user와 관련된 파일들이 포함되어 있습니다.SignUpForm.tsx
는 파일 이름 그대로 회원가입과 관련된 로직을 담고 있습니다. 하지만 이 파일을Widgets
폴더에 위치시킨 이유는 회원가입 로직이 비즈니스 로직이 아니라는 판단 때문입니다.
비즈니스 로직의 정의는 다양한 의견이 있을 수 있지만, 제가 생각하는 비즈니스 로직이란 구체적인 서비스와 직접적으로 관련된 로직을 의미합니다. 회원가입은 단순히 사용자의 계정을 생성하는 과정일 뿐, 특정 비즈니스와는 관련이 없다고 판단하여Widgets
에 포함하게 되었습니다. - Features: 여기에는
TodoList.tsx
,TodoItem.tsx
등 todo와 관련된 파일들이 포함되어 있습니다.TodoList
라는 서비스에서 Todo 기능은 비즈니스 로직에 해당한다고 판단하여, Todo 데이터를 서버에서 받아오는 API와 개별 Todo 항목을 보여주는TodoItem.tsx
등을 이 폴더에 포함하게 되었습니다. - Entities:
회원가입
,로그인
과 관련된 API 및 관련 로직이 포함되어 있습니다. 여기서 제가 정의한Entities
는 서비스에서 사용되는 추상적인 개념들을 나타냅니다. 예를 들어, 이 사전 과제인 TodoList에서는 유저(User) 자체는 특정 기능을 수행하지 않지만, 로그인하지 않으면 Todo 목록을 생성할 수 없으므로 유저는 다른 비즈니스 어디든 결합될 수 있는 중요한 개념입니다. 이러한 이유로User
를Entities
에, 그리고 서비스 기능으로서의Todo
를Features
에 포함하게 되었습니다.
만약 이 프로젝트가 TodoList 서비스가 아닌 회원가입한 유저들의 정보를 바탕으로 트렌드나 통계 등을 보여주는 서비스였다면, User는 직접적인 비즈니스 로직의 일부로 간주되어Features
에 포함했을 것입니다. - Shared: 공용 컴포넌트, 공용 유틸 등 특정 비즈니스에 종속되지 않고 어디서든 활용할 수 있는 부분들이 포함되어 있습니다.
위와 같은 기준으로 폴더를 구성하긴 했지만, 사실 애매한 부분이 많았습니다. TodoList는 워낙 작은 프로젝트이기 때문에 모든 레이어를 반드시 사용할 필요는 없었는데, 억지로 모든 레이어를 적용하려다 보니 오히려 불편함이 더해졌던 것 같습니다.
하지만 FSD 패턴을 억지로라도 적용하면서 개인적으로 느낀 점은, FSD 패턴은 서비스를 효과적으로 협업하고 유지보수할 수 있게 돕는 가이드일 뿐이라는 것입니다. 프로젝트마다 구조를 잡는 명확한 기준만 있다면, 어떻게 구조를 잡더라도 충분히 유연하게 접근할 수 있다는 생각이 들었습니다. 팀원들이 모두 이해하고 동의할 수 있는 기준이 있다면, 여섯 개의 레이어가 아닌 네 개의 레이어만으로도 충분히 효율적이고 깔끔한 구조를 설계할 수 있다고 생각합니다.
또한 비즈니스에 대한 명확한 정의와 깊은 이해가 뒷받침될수록, 폴더 구조를 더욱 확실하게 나누고 관리할 수 있을 것 같습니다.
결론
FSD(Feature-Sliced Design) 패턴은 분명 매력적인 디자인 패턴이지만, 이를 적용하려면 몇 가지 중요한 요소를 고려해야 한다고 생각합니다. 먼저, FSD를 도입하려면 비즈니스 로직에 대한 명확한 정의가 필요합니다. 프로젝트의 기능별 구조를 설정할 때 기준을 무엇으로 잡을지에 대한 팀(혹은 개인)의 철학 또한 중요한 요소로 작용할 것 같습니다.
또한, 일반적인 구조(layout
, page
, component
)에 비해 초기 설계 단계에서 시간이 더 소요될 수 있으며, 규모가 작은 프로젝트에서는 다소 과할 수 있다고 느꼈습니다. 하지만, 프로젝트 규모가 커져서 파일 관리가 어려운 상황이라면 FSD 공식 문서에서 제공하는 마이그레이션 가이드를 참고해 단계적으로 적용해볼 수 있을 것 같습니다.
참고로 공식 문서에서는 마이그레이션을 결정하는 기준을 아래와 같이 정해두었습니다.
- 새로운 팀원들이 생산적인 수준에 도달하기 어렵다고 불평하고 있습니다.
- 코드의 한 부분을 수정하면 관련이 없는 다른 부분이 자주 망가집니다.
- 새로운 기능을 추가하는 것이 생각해야 할 요소가 너무 많아 어려움을 겪고 있습니다.
제가 작성한 코드는 제 개인적인 생각과 TodoList라는 비즈니스에 대한 이해를 바탕으로 구성되었습니다. 만약 이 TodoList 관련 전체 코드가 궁금하시거나, 제가 잘못 이해한 부분이 있다면 댓글로 의견 주시면 감사하겠습니다.
'기타 프론트엔드 관련' 카테고리의 다른 글
Oxlint 등 oxc 간단하게 소개 (1) | 2024.11.29 |
---|