창업팀 프로젝트를 기획 및 설계 단계까지 진행하면서 개발에 들어가기 전 FSD 아키텍처 방법론을 도입하게 되었습니다. 회사와 병행하면서 진행될 사이드 프로젝트라 차근차근 다시 자바스크립트를 깊게 공부하며 여러번의 코드 리팩토링을 진행할 계획이었습니다. 결국 한두달만에 만들어야 되서 잠도 못자며 코딩하느라 클린코드는 못 했지만.. 그래도 배포해도 오류가 적고 기획 외의 베타 테스터들의 나쁘지 않은 평가를 받았던 것 같네요. 그래서 오늘은 FSD 아키텍처 방법론을 도입했지만 부족한 부분이 너무 많고 코드의 구조가 명확하지 않아 혼란스러웠던 경험을 토대로 적절한 FSD 아키텍처 방법론의 사고와 구조, 설계 방법 등 공식 문서를 깊게 파고들어볼 예정입니다. 프로젝트를 진행하면서 겪었던 문제점과 해결방법도 함께 생각하며 예제도 추가해봤습니다!
FSD 구조는 모든 프로젝트에 적합한 구조가 아닐 수도 있어요. 너무 많은 코드를 작성해야 할 수도 있으며 중간 투입자가 구조를 파악하기 어려울 수 있기 때문에 도입하기 전에 꼭 설계와 다른 아키텍처와 비교하여 적절한 방법론을 도입하시길 바랍니다.
https://feature-sliced.design/kr/docs/get-started/overview
Overview | Feature-Sliced Design
Feature-Sliced Design (FSD) 는 프론트엔드 애플리케이션의 코드를 구조화하기 위한 아키텍처 방법론입니다.
feature-sliced.design
1. FSD 아키텍처 방법론
FSD는 Feature(기능)-Sliced(조각) Design의 기능 분할 설계의 약자 입니다. 제가 처음에 이걸 들었을 때 기능 단위를 쪼개서 개발하는 구조로 이해했었습니다. 공식 문서에서는 애플리케이션의 코드를 구조화하기 위한 아키텍처이며, 새로운 기능이 추가되더라도 코드 구조가 무너지지 않고, 새 기능을 추가할 수 있는 프로젝트라고 하네요.
아래 이미지를 확인해보며 간단하게 설명 드리겠습니다.
레이어(Layers)가 웹 프로젝트의 /src 안에 명시할 수 있는 폴더 구조입니다. 레이어구조에는 app, processes, pages, widgets, features, entities, shared 구조가 있습니다. processes는 예전에 사용했었지만 이제는 더이상 권장하지 않습니다.
두번째는 슬라이스(Slices)에는 백엔드에서 많이 나오는 비즈니스 도메인 별로 나뉩니다. DDD(Domain-Driven Design)의 개념적으로 유사합니다. 아래 사진처럼 user, post, comment, account, order, chat 등이 있습니다.
마지막으로 세그먼트(Segments) 각 슬라이스 안에서 ui, model, api를 나누어서 화면상으로 보일 때의 UI, UX를 관리하고 model에서는 해당 도메인의 데이터 자체에 대한 모델을 담아내며 api는 백엔드와의 실제 통신을 정의하는 부분입니다. 즉, 백엔드와의 통신은 api, 데이터의 상태나 비즈니스 로직 관리는 model, 그리고 뿌려주는 부분이 ui입니다.

2. 레이어(Layers) 계층
FSD 구조에서 가장 뼈대가 되는 부분이 Layers부분입니다. 하기 내용들을 확인했을 때 이해가 되지만 직접 구현할 때는 헷갈리던 부분이었습니다.
- App - Routing, Entrypoint, Global Styles, Provider 등 앱을 실행하는 모든 요소
Processes - 더 이상 사용되지 않음- Pages - Route 기준으로 구성된 주요 화면 단위
- Widgets - 크고 독립적으로 동작하는 UI 구성 단위, 일반적으로 하나의 완결된 화면 기능(use case)을 제공합니다.
- Features - 사용자에게 비즈니스 가치를 제공하는 액션을 구현한 재사용 가능한 제품 기능 단위
- Entities - 프로젝트가 다루는 비즈니스 Entity
- Shared - 모든 Layer에서 재사용되는 코드(라이브러리, 유틸리티 등)
레이어는 프로젝트의 코드를 나눌 때 사용하는 가장 큰 단위입니다. 아래로 내려갈 수록 의존성이 줄어드는 순서입니다. 각 레이어는 여러 슬라이스로 구성되어 있습니다.
** 모든 레이어가 필요한 것은 아닙니다! 최소한의 구조로는 App, Pages, Shared로 사용하기도 합니다.
각 페이지는 모두 소문자로 구성되며, 같은 레이어 안에서 같은 레이어 안에 있는 하위 슬라이스만 import할 수 있습니다. 상위 컴포넌트는 import할 수 없는 단방향 의존성을 가지고 있습니다.
아래 표의 "구성 : " 부분은 세그먼트를 정리했습니다.
https://feature-sliced.design/kr/docs/reference/layers
| App | 앱 전역에서 사용하는 애플리케이션의 환경설정과 공용로직을 관리하는 곳입니다. 라우터 설정이나, Provider, 전역 상태관리, Context 등을 정의할 수 있습니다. 구성 : routes, store, styles, entrypoint(OS가 프로그램을 실행할 때 가장 먼저 호출하는 코드 시작 지점) |
Slice 없이 Segment로 구성 |
| Pages | 애플리케이션의 보이는 화면, 액티비티 부분에 해당됩니다. 전용 model이 없으며, 필요한 경우 간단한 상태만 관리하게 됩니다. 구성 : ui(화면, 로딩 화면, 에러 상태 처리), api(페이지에서 사용하는 데이터 패칭) |
page 1개 = Slice 1개 일반적 구조가 유사한 페이지들은 하나로 묶을 수 있음 |
| Widgets | 독립적으로 동작하는 비교적 큰 UI 블록입니다. 재사용되는 UI인 경우에 구성하는 것이 좋습니다. 재사용되지 않고 독립적인 UI인 경우라면 Widget으로 따로 빼지 않고 Pages 슬라이스 안에 넣어도 됩니다. |
재사용되는 UI인 경우에 widget으로 관리 |
| Features | 사용자가 애플리케이션을 수행하는 주요 기능으로 구성됩니다. 여러 페이지에서 재사용되는 기능인 경우에 features로 구성하면 좋습니다. 구성 : ui, api, model(검증로직,내부 상태 관리), config(feature flag 등 기능별 설정) |
features가 너무 많아지면 구조적으로 복잡해질 수 있기 때문에 초기 설계가 중요 |
| Entities | 프로젝트에서 다루는 핵심 비즈니스 개념을 구성합니다. 여러 entities 간에 상호작용이 발생할 수 있는데 공용 슬라이스를 만들어서 여러 entities에서 사용할 수 있도록 설계가 필요합니다. 구성 : model(데이터 상태, 도메인 로직, 검증 스키마), api(API 요청), ui(Entity의 시각적 표현) |
Cross-Import를 위한 Public API를 구성 가능 여러 페이지에서 사용할 수 있도록 설계 |
| Shared | 기본 구성 요소와 기반 도구를 모아두는 곳입니다. 비즈니스 로직은 포함되면 안되며, 공통 UI 컴포넌트나 공통 백엔드 API 요청함 수 등이 구성됩니다. 구성 : api(공통 API 요청 함수), ui(공통 UI 컴포넌트), lib(내부 라이브러리), config(환경변수, 전역 feature flag), routes(라우트 상수/패턴), i18n(번역 설정, 전문 문자열) |
공통적으로 사용하는 레이어이기 때문에 Segments의 이름을 직관적으로 알 수 있도록 설계되어야 함 |
공식 문서 상으로 레이어별 세그먼트 구성이 다릅니다. 표 내부에 "구성 : ~~" 부분에 참고해둘 테니 각 세그먼트의 특성을 파악해둔 상태에서 프로젝트의 구조를 설계하는데 뼈대를 잡을 수 있을 거라고 생각합니다.
3. 슬라이스(Slices) 계층
Slices는 두번째 계층으로, 비즈니스나 애플리케이션 관점에서 서로 관련있는 코드를 하나로 묶는게 관점입니다. 저는 next.js를 사용하면서 라우팅이 화면 단위로 잡고 했었는 데 한 화면에서 여러 도메인을 호출해야하는 경우는 어떤 구조가 효율적이고 유지보수에 적합하게 코드를 작성할 수 있을지 모호하더라구요. 처음에는 FSD구조를 그냥 버튼마다 CRUD 다 따로 하고 UI는 대충 잡으면 되지 않나?라고 생각했습니다. 물론 제가 제대로 FSD구조를 분석하지 않은 상태에서는 그랬습니다. 같은 기능이 조금씩 겹쳐있는 부분이 당연히 있을 텐데 그 부분을 구조화하는 과정이 중요한 포인트입니다. 그리고 이부분이 비즈니스 로직 코드 부분이라서 팀원들과 편한 방법을 찾아 자유롭게 작성할 수 있다고 생각합니다.
슬라이스 계층에서는 비즈니스 도메인을 관리하는 부분으로서 공식문서에서는 높은 응집도와 낮을 결합도를 강조하고 있습니다.

기능을 나열했을 때 이게 features야? entities인가? 생각이 들텐데, 사실 이 부분은 팀원들과 상의해서 팀 내 규칙을 만들면 됩니다!
저는 아직도 고민인게 모든 것을 feature로 두게 되면 폴더의 구조가 너무 많아지고 폴더명, 파일명 정하는게 너무 어려울 것같고,, 그렇다고 뿌리기만 하는 화면단을 features나 entites에 두지 않으면 widget이나 pages에서 해야한다는 점에서 새로운 팀원이 들어왔을 때 코드 분석에 걸림돌이 되지 않을까라는 생각을 했었습니다. AI에게 물어봐도.. "features가 될 수도 있고.. entities가 될 수 있어" 라고 하니 뭐가 더 효율적인가에 수준이 거의 동일하기 때문에 계속 고민이 됐었습니다.
높은 응집도와 낮은 결합도를 포인트로 잡아보면 기능 단위로 나누었을 때 한 기능이 독립적으로 구분될 수 있어야한다는 점을 중요하게 생각했습니다. entities와 features를 구분할 때의 핵심 내용은 데이터 중심인가? vs 사용자 행동 중심인가? 를 따지게 됩니다. entities는 따로 가공하지 않아도 제공해야하는 것이며, 여러 기능에서 재사용된다는 점에 중점을 잡았고, features는 사용자의 행동에 따라 적용되는 액션까지로 잡았습니다.
SNS를 예시로 작성해보면, SNS에서는 사용할 수 있는 기능을 아래 정도로 잡아봤어요. 실제 폴더 구조 예시를 보여주는 게 이해하기 쉬울 거라고 생각합니다.
- 게시글 목록 - entities
- 게시글 보기 - entities
- 게시글 작성하기 - features
- 게시글 수정하기 - features
- 게시글 삭제하기 - features
- 게시글 좋아요 - features
- 게시글 좋아요 취소 - features
- 댓글 보기 - endtities
- 댓글 작성하기 - features
- 댓글 수정하기 - features
- 댓글 삭제하기 - features
- 댓글 좋아요 - features
- 댓글 좋아요 취소 - features
단순히 보기만하는 것은 entities로 사용자의 행동에 의해서 API를 호출해야한다거나 변경되는게 있다면 features로 구분한다고 합니다.
4. 세그먼트(Segments) 계층
세그먼트 계층에서는 아래 폴더 구조로 코드를 기술적인 역할과 성격에 따라 나누는 기준이 있습니다.
- ui : UI 관련 코드(Component, Date Formatter, Style 등)
- api : Backend 통신(Request Function, Data Type, Mapper 등)
- model : Data Model(Schema, Interface, Store, Business Logic 등)
- lib : Slice 내부에서 사용하는 Library 코드
- config : Configuration, Feature Flag 등 설정 관련 코드
그렇다면 SNS 기능들의 폴더구조는 어떻게 만들어야 하는지 고민이 생겼었습니다. 사용자 액션에 따라서 features가 만들어진다면 한 페이지를 다 넣을 수도 있고 버튼하나가 될 수 있지 않나? 라는 생각을 했었고 과연 그게 효율적인가에 대해서도 의문이 계속 들었습니다. 세그먼트 계층을 ui/api/model/lib/config라고는 하지만 entities와 features에 적용하는 범위가 너무 모호했기 때문에 멘붕이 왔었습니다.
다시 말씀드리지만 이런 규칙들은 팀 내에서 자유롭게 정해도 됩니다. 아래 내용은 저의 개인적인 생각입니다.
먼저 entities를 폴더구조를 나눠볼건데 entities는 model, api, ui로 세그먼트 구성이 되어있다고 공식 문서에 적혀있습니다. 하나의 게시물을 예시를 들어볼게요
- 📁 model — 데이터 상태, 도메인 로직, 검증 스키마
- 📁 api — 해당 Entity와 관련된 API 요청
- 📁 ui — Entity의 시각적 표현
- 완성된 큰 UI 블록이 아니어도 됩니다.
- 여러 페이지에서 재사용 가능한 형태로 설계합니다.
- 비즈니스 로직은 가능하면 props/slot으로 외부에서 주입하는 방식을 권장합니다.
https://feature-sliced.design/kr/docs/reference/layers#entities
Layer | Feature-Sliced Design
Layer는 Feature-Sliced Design에서 코드를 나눌 때 사용하는 가장 큰 구분 단위입니다.
feature-sliced.design
entities/
post/
api/
getPost.ts
getPostList.ts
model/
types.ts : 게시물 도메인의 데이터 타입
selectors.ts : 정렬이나 가공할 때 사용
ui/
PostCard.tsx
PostTitle.tsx
PostMeta.tsx : (게시글 내에서 보여지는 댓글이나 좋아요 수, 공유 수 등)
PostComment.tsx : 만약 게시물에서만 ui가 다르다면 여기에 넣어도 되지만 공용 ui라면 빼는게 좋음. 보통은 entites/comment로 뺄 것 같네요
index.ts
그렇다면 게시물 하나에 사용자가 할 수 있는 액션은 좋아요, 좋아요 취소, 댓글 작성, 댓글 수정 등 많이 있을 텐데 이건 어떻게 구성하는지에 대해 의문이 들었을거에요 features에 들어가는 좋아요를 예시로 작성해보겠습니다.
features/
like-post/
api/
likePost.ts
unlikePost.ts
model/
types.ts : 좋아요의 도메인 데이터 타입
useLikePost.ts : 좋아요 훅 -> 좋아요의 useState와 좋아요 수 계산값
ui/
LikeButton.tsx : 버튼 태그로만 이루어지며 model/useListPost의 로직에 의존
index.ts
그러면 entities와 features가 함께 있는 게시물 같은 경우에 pages나 widget에서 각 기능을 조립하여 사용하면 됩니다. 저의 tstory를 예시를 들어볼겠습니다. 좀 부끄럽고 헷갈릴 수 있지만 ui의 파일로 불러오는 방식으로 작성해봤습니다.


이런식으로 예시를 들어봤어요. 지금 이미지들을 봤을 때 의문점이 생겨야 합니다.
- 게시물 상세에서 [FSD 구조] 예시라는 제목 아래에 있는 동그란 i 아이콘은 features 아닌가?
- 댓글에서 답글도 features 아닌가?
- 댓글의 맨 오른쪽 세로 ... 케밥도 features 아닌가?
- 공감 옆에 있는 것들도 다 features인데 각각 features로 둬야하나 하나로 묶을 수 있지 않을까?
- 댓글을 공용 ui로 만든다면 page에서는 어떻게 구성되는거지?
- 섹션은 또 어떻게 나눠야 해?
- 그러면 page안에 너무 많이 entities랑 features가 너무 중구난방이지 않나?
- 양갱의 개발블로그 컴포넌트도 뺄수 있지 않나?
정답은 팀원들과의 규칙을 정하면 됩니다!
정해진게 없어요 features와 entities를 합쳐서 widget에 넣어도 되고 그 widget을 pages에 뿌려도 됩니다! 그렇게 되면 중구난방되어 있던 게 정리가 될거고 PostPage.tsx에서는 여러 widget으로 구성되겠죠? 각 widget의 구조는 팀마다 다르게 구성될 겁니다.

너무 많이 나눠진것 같긴하지만,, 팀내에서 규칙을 정해 만들어 낼 수 있을거라고 생각합니다. 제가 작성한 것은 참고만 하시길..
마무리
FSD(Feature-Sliced Design)는 코드를 나누는 규칙뿐만아니라 기능 중심으로 도메인, 비즈니스 로직에 따로 분리하는 설계방식이라고 느꼈습니다. 처음에는 단순히 보이는 걸로 나누면 되지 않나라며 단순 구조를 생각했지만, 깊게 파고들수록 기능단위로 생각하며 어떤 방식으로 나눠야할 지 감이 잡히지 않았습니다. 구조를 설계하면서 무작정 컴포넌트를 나누는 것이 아니라 기능 단위로 봤을 때 어떤 퍼포먼스가 있어야하는지 고민해야한다고 느꼈습니다.
높은 응집도와 낮을 결합도, entities와 features를 구분할 때 생각해야할 데이터 중심인가? vs 사용자 행동 중심인가?
논점이 가장 중요한 것 같습니다. FSD 구조를 도입한다면 기능 재사용성이 높아지며 서비스 규모가 커지더라도 구조가 쉽게 무너지지 않을것같습니다.
아직 완벽하게 익숙해진 것은 아니지만, 직접 프로젝트에 도입하면서 계속 적용해본다면, 유지보수하기 쉽고 확장 가능한 프론트엔드 구조를 만들 수 있을 것 같습니다.
간단한 프로젝트를 하나 만들어봐야겠는데 아직도 고민이 되네요.. 오랜만에 공부하고 작성했는 데 꽤 오래걸렸던 것 같네요 긴글 읽어주셔서 감사합니다..!
혹시 오류나 잘못된 부분이 있다면 댓글로 작성해주세요! 수정하겠습니다
참고
https://feature-sliced.design/kr/docs/get-started/overview
https://tech.kakaopay.com/post/fsd/
이 페이지가 정말 잘 쓰여진 포스팅이었습니다.
'프론트 > 프론트엔드' 카테고리의 다른 글
| [Redux] 리덕스란? - 리덕스 개념 및 사용방법 (2) | 2024.01.21 |
|---|---|
| [JSP] JSP에서 포워드(forward)와 리다이렉트(redirect), 특정 페이지로 이동 방법 (0) | 2023.10.20 |
| [프론트엔드] Redux란? (0) | 2023.10.13 |
| [프론트엔드] Web Server란? (0) | 2023.10.13 |
| [프론트 엔드] HTTP 프로토콜과 상태 코드 (0) | 2023.10.13 |