|
| 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