FSD 아키텍처는 대규모 프론트엔드 프로젝트의 구조 문제를 계층 분리로 다룬다
항해99 코드 리뷰 중에 FSD(Feature-Sliced Design)라는 키워드가 나왔다. 처음 들어봤던 개념이라 찾아봤는데, 대규모 프론트엔드 프로젝트가 왜 구조적으로 망가지는지를 설명해주는 아키텍처 패턴이었다.
기존 프론트엔드 구조의 문제
보통 프론트엔드 프로젝트는 이렇게 시작한다:
src/
components/
pages/
hooks/
utils/
services/
작을 때는 문제없다. 그런데 팀이 늘고 기능이 쌓이면 components/에 수십 개가 넘는 컴포넌트가 생기고, 어디서 무엇이 어디를 import하는지 파악이 안 된다. UserCard가 ProductList에서 쓰이고, ProductList가 다시 UserProfile을 import하는 순환 의존성이 생기기 시작한다. 이걸 규모가 커지면서 생기는 “구조 문제”라고 한다.
FSD의 핵심: 계층을 정의하고, 위에서 아래로만
FSD는 프로젝트를 6개의 계층으로 나눈다:
| 계층 | 역할 |
|---|---|
app | 앱 초기화, 라우팅, 전역 스타일 |
pages | 라우트별 페이지 컴포넌트 |
widgets | 여러 feature를 조합한 독립 블록 |
features | 사용자 시나리오 단위 (좋아요, 댓글 작성 등) |
entities | 비즈니스 도메인 (User, Product, Order) |
shared | 공용 유틸리티, UI 컴포넌트, API 클라이언트 |
핵심 규칙은 단순하다. 상위 계층은 하위 계층을 import할 수 있지만, 그 반대는 안 된다. entities는 features를 import하면 안 된다. shared는 그 어떤 계층도 import하면 안 된다.
이 규칙 하나로 순환 의존성이 원천 차단된다.
각 feature는 슬라이스로 분리된다
계층 안에서 기능별로 또 분리한다. 예를 들어 features/ 안에는:
features/
add-to-cart/
write-comment/
like-post/
각 슬라이스는 자신의 UI, 모델, API 호출을 다 갖는다. 다른 슬라이스를 import하면 안 된다. 슬라이스 간 공유가 필요한 것은 shared나 entities로 내려야 한다.
이 덕분에 “add-to-cart 기능을 삭제해도 되나?” 같은 질문에 자신 있게 답할 수 있다. 해당 슬라이스가 다른 슬라이스에 의존하지 않으니까.
실제로 쓸 때 고민되는 지점
소규모 프로젝트에서 FSD를 처음부터 도입하면 오버엔지니어링이 될 수 있다. 화면이 3개짜리 프로젝트에서 6개 계층을 만드는 건 배보다 배꼽이 크다.
기존 레거시 코드베이스에 점진적으로 도입하는 것도 쉽지 않다. 처음에는 shared와 entities만 분리하고, 점차 features를 추가하는 방식으로 접근하는 게 현실적이다.
연결
- 실무에서 디자인 패턴은 이상과 타협의 연속이다 — 아키텍처 도입의 이상과 현실 타협
- Docker는 환경 일관성 문제를 이미지로 해결한다 — 경계를 명확히 나누는 원칙은 같은 맥락