Intro

Feature-Sliced Design(FSD)을 이야기할 때 흔히 “어디까지 나눌 것인가”에 집중합니다.
하지만 실제로 유지보수 비용을 결정짓는 요소는 폴더의 개수가 아니라 의존성의 형태와 영향 범위입니다.
이 글에서는 다음 질문에 답하려 합니다.
- 변경이 발생했을 때, 의존 관계에 있는 코드들이 같은 위치에 모여있는가? (co-location)
- 해당 변경의 영향 범위를 구조적으로 제한하고 있는가
- 영향 범위가 명확해졌을 때, 계층 이동이 자연스럽게 일어나는가
시나리오로 보는 “응집된 의존성”
장바구니(cart) 페이지를 예로 들어보겠습니다.
페이지 내부에 캡슐화된 구조
pages/cart
├─ ui
│ ├─ CartPage
│ ├─ CartSummary
│ └─ FreeShippingNotice
└─ model
├─ useCartCalculation
└─ shippingPolicy
- 의존 관계에 있는 모듈들이 물리적으로 한 폴더에 모여 있음
- 변경 시 “이 기능에 영향을 받는 코드”가 시각적으로 드러남
- 페이지 외부로 퍼져나가는 의존성이 없음
이 상태에서는 구조가 곧 의존성 그래프입니다.
단방향 의존성이 만드는 “보수적인 영향 범위”
이제 요구사항을 추가해 봅니다.
“장바구니 총액이 5만 원 이상이면 무료배송 표시 및 배송비 계산”
이 변경의 영향 범위는 명확합니다.
pages/cart 내부
├─ ui ✅ UI 일부 수정
└─ model ✅ 배송 정책 상수 추가
중요한 점은 관련 모듈이 응집되어있다는 사실입니다.
- page → 내부 모듈로만 의존
- 다른 레이어(feature, entity, shared)는 의존하지 않음
- 따라서 변경은 자연스럽게 page 레이어에서 캡슐화된 private 모듈로 제한됨
이런 구조적 이점을 통해, 변경이 발생시 영향 범위를 시각적으로 알 수 있어 사이드 이팩트 영향을 판단하기 수월합니다.
하위 레이어를 의존하게 되는 경우
만약 모듈을 선제적으로 하위 레이어에 배치한 경우라면 어떨까요?
pages/cart
features/cart-summary
features/free-shipping-notice
entities/cart
widgets/shipping-info
shared/constants
의존성을 그려보면 다음과 같습니다.
CartPage
├─ CartSummary (feature)
│ └─ cart entity
│ └─ ✅ shared 계산 로직
├─ FreeShippingNotice (✅ feature)
└─ ShippingInfo (✅ widget)
└─ ✅ shared/constants
이 구조에서는 각 계층별 모듈을 수정해야 할 때 아래와 같은 사항을 고민해봐야 합니다.
- CartPage의 기능 수정을 위해 CartSummary를 수정하게 될 경우 다른 화면에서 영향은 없을까?
- CartPage를 지워야 할 경우에 함께 사라져야 하는 모듈은 어떤 모듈을이지?
- cart Entity가 다른 레이어에서도 쓰이고 있는나?
- 이 모듈의 의존성이 어떻게 되는지?
즉, 하위 레이어에 위치한 모듈일수록 고려해야 할 부분이 많아지게 됩니다.
계층 이동은 “미리”가 아니라 “나중에” 하는 것이 유리합니다
여기서 핵심은 이 지점입니다.
처음부터 상위 계층으로 올려두는 것보다,
영향 범위가 구체화된 뒤에 계층을 이동하는 편이 관리 비용이 낮다.
왜 그런가?
- 한 가지 이유로 수정되어야 하는 모듈은 같은 위치에 모여있는 것이 좋다
- 모듈의 의존 범위가 넓어질수록 수정이 어려워진다.
- 같은 이유로 수정되어야 하는 모듈이라는 것이 확인됐을 때 하위 계층으로 승격해야 선제적 이동으로 인한 분기 로직을 예방할 수 있다.
예를 들어 cart의 배송 계산 로직이
- 주문 페이지
- 결제 페이지
에서도 사용되기 시작했다면, 그때 비로소 다음과 같은 이동이 자연스럽습니다.
pages/cart/model/useCartCalculation
↓ (승격, 계층 이동)
entities/cart/model/useCartCalculation
이 이동은 판단이 아니라 확인의 결과입니다.
시각적으로 관리되는 구조가 좋은 구조입니다
정리해 보면, 좋은 FSD 구조는 다음 특징을 가집니다.
- 의존 모듈이 물리적으로 응집되어 있다
- 변경 시 영향 범위가 폴더 단위로 보인다
- 단방향 의존성으로 인해 영향이 상위로 확산되지 않는다
- 재사용이 확인된 순간에만 계층 이동이 발생한다
이 구조에서는 질문이 단순해집니다.
- “이 기능을 고치려면 어디를 보면 되는가?”
- “이 기능을 지우면 무엇이 영향 받는가?”
그 질문에 폴더 하나로 답할 수 있다면, 그 구조는 이미 충분히 잘 설계된 구조입니다.
마무리
FSD의 목적은 정교한 분류가 아니라 의존성의 가시화와 통제입니다.
캡슐화된 단방향 의존성 구조는 의존성 관계를 직관적으로 표현함으로서 유지보수 비용을 낮춥니다.
- 실제로 재사용되고 있는가?
- 영향 범위는 어디까지인가?
이 질문에 구조 자체가 답을 해주는 상태, 그것이 관리하기 좋은 아키텍처입니다.
'react' 카테고리의 다른 글
| useReducer를 이용한 상태 모델링의 이점 (0) | 2025.12.19 |
|---|---|
| [설계] 실무에서 자주 겪는 잘못된 추상화 사례 (0) | 2025.12.19 |
| [설계] UI로직에서 비즈니스 로직 분리하기 (0) | 2025.12.17 |
| [최적화] Nextjs 번들 최적화 (Bundle Optimization) (0) | 2025.12.12 |
| 2025년에 돌아보는 react-query (0) | 2025.12.10 |