react

[설계] 모듈 응집도와 단방향 의존성을 통한 유지보수 비용 줄이기

긍정왕_JERRY 2025. 12. 18. 12:27
728x90
반응형

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가  다른 레이어에서도 쓰이고 있는나?
  • 이 모듈의 의존성이 어떻게 되는지?

즉, 하위 레이어에 위치한 모듈일수록 고려해야 할 부분이 많아지게 됩니다.


계층 이동은 “미리”가 아니라 “나중에” 하는 것이 유리합니다

여기서 핵심은 이 지점입니다.

처음부터 상위 계층으로 올려두는 것보다,
영향 범위가 구체화된 뒤에 계층을 이동하는 편이 관리 비용이 낮다.

왜 그런가?

  1. 한 가지 이유로 수정되어야 하는 모듈은 같은 위치에 모여있는 것이 좋다
  2. 모듈의 의존 범위가 넓어질수록 수정이 어려워진다.
  3. 같은 이유로 수정되어야 하는 모듈이라는 것이 확인됐을 때 하위 계층으로 승격해야 선제적 이동으로 인한 분기 로직을 예방할 수 있다.

예를 들어 cart의 배송 계산 로직이

  • 주문 페이지
  • 결제 페이지

에서도 사용되기 시작했다면, 그때 비로소 다음과 같은 이동이 자연스럽습니다.

pages/cart/model/useCartCalculation
           ↓     (승격, 계층 이동)
entities/cart/model/useCartCalculation
 

이 이동은 판단이 아니라 확인의 결과입니다.


시각적으로 관리되는 구조가 좋은 구조입니다

정리해 보면, 좋은 FSD 구조는 다음 특징을 가집니다.

  • 의존 모듈이 물리적으로 응집되어 있다
  • 변경 시 영향 범위가 폴더 단위로 보인다
  • 단방향 의존성으로 인해 영향이 상위로 확산되지 않는다
  • 재사용이 확인된 순간에만 계층 이동이 발생한다

이 구조에서는 질문이 단순해집니다.

  • “이 기능을 고치려면 어디를 보면 되는가?”
  • “이 기능을 지우면 무엇이 영향 받는가?”

그 질문에 폴더 하나로 답할 수 있다면, 그 구조는 이미 충분히 잘 설계된 구조입니다.


마무리

FSD의 목적은 정교한 분류가 아니라 의존성의 가시화와 통제입니다.
캡슐화된 단방향 의존성 구조는 의존성 관계를 직관적으로 표현함으로서 유지보수 비용을 낮춥니다.

  • 실제로 재사용되고 있는가?
  • 영향 범위는 어디까지인가?

이 질문에 구조 자체가 답을 해주는 상태, 그것이 관리하기 좋은 아키텍처입니다.

728x90
반응형