Skip to content

Commit b87820f

Browse files
committed
feat: 20260125.mdx - FSD도입기 문서 작성
1 parent a76a599 commit b87820f

File tree

1 file changed

+226
-0
lines changed

1 file changed

+226
-0
lines changed

posts/20260125.mdx

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
---
2+
title: "FSD 도입기: 같은 아키텍처, 다른 결과"
3+
description: "도메인이 얕으면 짐이 되고, 깊으면 빛을 발한다."
4+
date: "2026-01-25"
5+
published: true
6+
category: "Frontend"
7+
tag: "FSD, Architecture"
8+
---
9+
10+
> 두 프로젝트의 다른 결과, 그리고 그 사이에서 배운 것들
11+
12+
## 들어가며
13+
14+
프론트엔드 아키텍처에 대한 고민은 프로젝트 규모가 커질수록 피할 수 없는 숙제입니다.
15+
우리 팀 또한 사내 서비스의 프론트엔드를 고도화하면서 여러 문제에 직면했고, 그 해결책으로 Feature-Sliced Design(FSD) 아키텍처를 선택했습니다.
16+
17+
하지만 결과는 프로젝트의 성격에 따라 극명하게 갈렸습니다.
18+
사용자 접점이 많은 **홍보용 웹사이트**에서는 기대에 미치지 못했고,
19+
핵심 기능을 담당하는 **특정 도메인이 포함된 프로덕트**에서는 매우 성공적이었습니다.
20+
21+
이 글은 그 여정에 대한 솔직한 기록입니다.
22+
23+
## 도입 전, 우리가 마주한 문제들
24+
25+
### 1. 제각각인 폴더 관리
26+
27+
컴포넌트는 항상 `components/` 폴더에 두었습니다. 문제는, 그 안에서의 정리 방식이 개발자마다 달랐다는 것입니다.
28+
29+
```
30+
components/
31+
├── common/ # A 개발자: "공통이니까 여기"
32+
├── MainSection/ # B 개발자: "페이지 단위로 나누자"
33+
├── ProductCard/ # C 개발자: "도메인별로 구분하자"
34+
└── Button/ # D 개발자: "UI 단위로..."
35+
```
36+
37+
어떤 규칙도 없었습니다.
38+
누군가는 페이지 단위로, 누군가는 도메인별로, 또 누군가는 그냥 만든 순서대로 폴더를 만들었습니다.
39+
"이 컴포넌트 어디 있어요?"라는 질문에 "그거 아마 components 어딘가에..."라고 답하는 일이 잦아졌습니다.
40+
41+
### 2. 순환 의존성 문제
42+
43+
명확한 레이어 구분이 없다 보니 컴포넌트 간 의존성이 꼬이는 경우가 잦았습니다.
44+
`CoreCard``SubBanner`를 임포트하고, 그 안에서 다시 `ActionButton`을 임포트하는 식이었습니다.
45+
46+
```typescript
47+
48+
// 이런 일이 자주 일어났습니다
49+
import SubBanner from '@/components/module-a/Banner';
50+
51+
```
52+
53+
### 3. 재사용성의 한계
54+
55+
"공통 컴포넌트"라고 만들었지만, 특정 도메인 로직이 섞여 있어 실제로는 재사용이 어려운 경우가 많았습니다. 비즈니스 로직과 UI의 분리가 명확하지 않았기 때문입니다.
56+
57+
### 4. 테스트 가능성 저하
58+
59+
의존성이 복잡하게 얽히다 보니 단위 테스트를 작성하기도 어려웠습니다.
60+
하나의 컴포넌트를 테스트하려면 관련된 5~6개의 의존성을 함께 모킹해야 했습니다.
61+
62+
---
63+
64+
## FSD를 선택한 이유
65+
66+
위 문제들을 해결할 수 있을 거라 기대하며 FSD를 선택했습니다.
67+
68+
| 문제 | FSD가 해결해줄 거라 기대한 것 |
69+
|------|------------------------------|
70+
| 제각각인 폴더 관리 | 레이어 기반 구조로 "어디에 둘지" 명확해짐 |
71+
| 순환 의존성 | 단방향 의존성 규칙으로 강제 |
72+
| 재사용성 한계 | shared/entities 분리로 비즈니스 로직과 UI 분리 |
73+
| 테스트 어려움 | 레이어별 독립성으로 모킹 최소화 |
74+
75+
## 사례 1: 홍보용 웹사이트 - 러닝커브와 도메인의 벽
76+
77+
### 도입 초기: features vs entities 구분의 어려움
78+
79+
FSD를 홍보용 사이트 프로젝트에 처음 적용하면서 가장 어려웠던 점은 **features와 entities의 구분**이었습니다.
80+
81+
```typescript
82+
83+
// 이 InteractionButton은 features일까, entities일까?
84+
85+
// entities/domain-a/ui/InteractionButton.tsx ? ❌
86+
87+
// features/domain-a/ui/InteractionButton.tsx ? 🤔
88+
89+
```
90+
91+
이론적으로는 명확해 보였지만, 실제 코드를 작성하다 보면 경계가 모호한 경우가 많았습니다. 팀원들과의 논의도 길어졌고, 러닝커브가 생각보다 높았습니다.
92+
93+
왜 이렇게 헷갈릴까? 어느 정도 개발이 진행된 시점에 레이어별 컴포넌트 분포를 분석해봤습니다.
94+
95+
```
96+
widgets: 100개 이상 ...
97+
features: 10여 개 ...
98+
entities: 50여 개 ...
99+
```
100+
101+
- **widgets이 압도적으로 많다는 것이 문제의 핵심이었습니다.**
102+
103+
홍보용 웹사이트는 마케팅 특성상 **퍼블리싱과 단순 인터랙션 위주의 페이지** 가 대부분이었습니다.
104+
복잡한 비즈니스 로직보다는 "컨텐츠를 사용자에게 보다 더 감각적으로 보여주는 것"이 주된 목표였죠.
105+
106+
```tsx
107+
// 전형적인 페이지 구조
108+
export default function ContentPage() {
109+
return (
110+
<div>
111+
<HeroWidget />
112+
<FeatureSectionWidget />
113+
<FAQWidget />
114+
<ContactWidget />
115+
</div>
116+
);
117+
}
118+
```
119+
120+
결과적으로 **pages에 widgets만 나열하는 형태** 가 되었습니다.
121+
features 레이어는 거의 활용되지 않았고, entities도 API 호출 정도만 담당하는 얇은 레이어가 되었습니다.
122+
123+
### "이게 FSD가 맞나?" 하는 의문
124+
125+
FSD의 핵심은 **복잡한 비즈니스 로직을 레이어로 잘 나누어 관리** 하는 것입니다. 하지만 이 프로젝트는,
126+
127+
- 사용자 인터랙션이 많지 않음 (features 활용도 ↓)
128+
- 도메인 로직이 단순함 (entities 활용도 ↓)
129+
- 페이지별 독립적인 UI 구성이 주요 작업 (widgets만 ↑)
130+
131+
"아키텍처가 프로젝트 성격에 맞지 않는 건 아닐까?" 하는 고민이 들었습니다.
132+
133+
### 그래도 완전한 실패는 아니었습니다.
134+
135+
- **공통 컴포넌트 정리**: `shared/ui`에 재사용 가능한 컴포넌트를 명확히 분리
136+
- **일관된 파일 구조**: 팀원들이 파일을 찾기 쉬워짐
137+
- **의존성 규칙**: 단방향 의존성 원칙만큼은 지켜져 순환 참조가 사라짐
138+
139+
<aside data-type="info">
140+
하지만 FSD의 진가를 발휘하기에는 프로젝트의 도메인 깊이가 얕았다는 결론을 내렸습니다.
141+
</aside>
142+
143+
## 사례 2: 에디터 프로덕트 - 도메인이 명확하니 구조도 명확하다
144+
145+
> 특정 도메인을 가진 프로덕트를 에디터 프로덕트라고 칭하겠습니다.
146+
147+
에디터 프로젝트는 시작부터 달랐습니다.
148+
**복잡한 데이터 구조를 직접 핸들링** 해야 하는 명확한 도메인이 있었기 때문입니다.
149+
150+
```
151+
widgets: 20개 미만
152+
features: 10여 개
153+
entities: 20여 개
154+
```
155+
156+
홍보용 사이트와 비교하면 **레이어가 균형있게 분포**되어 있습니다.
157+
158+
### 왜 잘 맞았을까?
159+
160+
에디터에는 "element", "layer", "context" 같은 **명확한 도메인 개념** 이 있었습니다.
161+
이게 자연스럽게 entities가 되었고, "정렬", "스타일 편집" 같은 **사용자 액션** 이 features가 되었습니다.
162+
163+
```
164+
entities/element → features/alignment → widgets/toolbar
165+
(데이터) (액션) (조합)
166+
```
167+
168+
"이 코드 어디에 둬야 하지?"라는 고민이 거의 없었습니다.
169+
도메인 자체가 레이어 구분을 알려주는 느낌이었어요.
170+
171+
홍보용 사이트에서 헷갈렸던 features vs entities 구분이,
172+
에디터에서는 **"데이터냐 액션이냐"** 로 명확하게 나뉘었습니다.
173+
팀원들 반응도 달라졌습니다. "왜 이렇게 나눴지?"가 아니라 **"이렇게 나누니 관리가 쉽네"** 라는 반응이 나왔습니다.
174+
175+
---
176+
177+
## 회고: 아키텍처는 은탄환이 아니다
178+
179+
### 배운 것
180+
181+
#### 1. 아키텍처는 도메인에 맞아야 한다
182+
183+
FSD는 훌륭한 아키텍처지만, **모든 프로젝트에 맞는 것은 아닙니다.**
184+
185+
- **단순 노출형 프로젝트** (사례 1): 과도한 레이어 분리가 오히려 부채가 될 수 있음
186+
- **비즈니스 중심 프로덕트** (사례 2): FSD의 레이어 분리가 강력한 유지보수성을 제공
187+
188+
#### 2. 러닝커브를 과소평가하지 말 것
189+
190+
"아키텍처 문서를 공유하면 되겠지"라고 생각했지만, 실제 기대와는 달랐습니다.
191+
192+
- Features vs Entities 구분에만 2~3주 소요
193+
- 팀원들이 익숙해지기까지 1~2개월
194+
- 컨벤션 정립까지 수많은 코드 리뷰
195+
196+
#### 3. 도메인을 이해하는 것이 먼저
197+
198+
에디터 환경에서 FSD가 잘 어울린다고 생각한 이유는 **책임이 명확했기 때문**이라 생각합니다.
199+
200+
- "핵심 데이터"가 무엇인지 정의됨
201+
- "각 모듈"의 역할이 독립적임
202+
- "상태 관리"의 범위가 명확함
203+
204+
반면 홍보용 사이트에서 어려웠던 이유는 **도메인 계층이 얇았던 점** 이 가장 크다고 생각해요.
205+
206+
- "핵심 도메인"과 "단순 UI"의 경계가 모호함
207+
- "기능"보다는 "레이아웃" 위주의 작업이 많음
208+
209+
<aside data-type="success">
210+
즉, **아키텍처보다 도메인의 복잡도와 성격을 먼저 파악해야 합니다.**
211+
</aside>
212+
213+
## 마치며
214+
215+
FSD가 좋아 보여서, 트렌디해 보여서, 적용하면 뭔가 달라질 것 같아서 도입했습니다.
216+
돌이켜보면 "우리 프로젝트에 맞는가?"는 깊게 고민하지 않았던 것 같습니다.
217+
218+
결국 아키텍처는 문제를 해결하는 도구일 뿐입니다.
219+
도메인이 복잡하면 FSD가 빛을 발하고, 단순하면 오히려 짐이 됩니다.
220+
221+
> 좋은 아키텍처는 따로 없습니다. 우리 문제에 맞는 아키텍처가 좋은 아키텍처입니다.
222+
223+
---
224+
225+
## 참고자료
226+
- [FSD 문서](https://feature-sliced.design/kr/docs/get-started/overview)

0 commit comments

Comments
 (0)