FSD 아키텍처 도입기와 사용 후기

hannah·2025년 10월 20일

JavaScript

목록 보기
105/108

작년부터 익히 들었던 Feature-Sliced Design(FSD)이라는 아키텍처를 접하게 되었다. 처음에는 "프론트엔드에 무슨 거창한 아키텍처야?"라고 생각했지만, 카카오페이 기술 블로그와 여러 선배 개발자들의 회고를 읽으며 생각이 바뀌었다. FSD는 단순한 폴더 정리가 아니라 비즈니스 로직과 UI를 분리하고, 의존성의 방향을 강제하여 유지보수 가능한 애플리케이션을 만드는 방법론이었다.

이 글은 주니어 개발자의 시선에서 FSD를 학습하고, 실제 프로젝트에 적용해보며 배우고 느낀 점을 정리해보려고 한다.

🧐 FSD이란...!!

FSD는 말 그대로 기능(Feature) 단위로 쪼개서(Sliced) 설계(Design)하는 방법론이다. 기존에 우리가 습관처럼 쓰던 파일의 유형별(components, hooks, utils) 기준이 아니라, '비즈니스 가치'를 기준으로 프로젝트 구조를 나눈다.

가장 큰 특징은 "높은 응집도, 낮은 결합도"를 강제한다는 점이다. 관련된 코드끼리는 한곳에 모으고(응집도), 서로 다른 기능끼리는 멋대로 가져다 쓰지 못하게 막는(결합도) 것이다.

위는 구조 다이어그램으로 위에서 아래로 흐르는 엄격한 위계질서를 나타낸다.

🏗️ 세 가지 핵심 개념: Layers, Slices, Segments

FSD는 Layers(계층), Slices(슬라이스), Segments(세그먼트), 세 가지 계층 구조를 제시한다.

1. Layers (계층)
프로젝트의 최상위 폴더들이다. FSD에는 엄격한 단방향 의존성 규칙이 있는데, 상위 레이어는 하위 레이어를 쓸 수 있지만, 하위 레이어는 상위 레이어를 절대 쓸 수 없다는 것이다.

위에서부터 아래로 순서는 다음과 같다:

  1. App: 앱의 진입점으로 전역 설정, Provider, 라우터 세팅 등이 들어간다.
  2. Pages: 실제 페이지 단위로 기능들을 조립해서 화면을 만든다. 로직은 최소화한다.
  3. Widgets: 독립적인 UI 블록 (예: Header, PostCard). FeatureEntity를 결합해 만든다.
  4. Features: 사용자 상호작용이 있는 기능으로 (예: 로그인 버튼, 좋아요 누르기, 장바구니 담기) 비즈니스 가치를 주는 핵심 부분이다.
  5. Entities: 비즈니스 데이터 모델로 (예: User, Product) 데이터 그 자체와 이를 보여주는 단순 UI만 가진다.
  6. Shared: 특정 도메인에 종속되지 않는 재사용 가능한 코드 (UI Kit, axios 설정, 유틸 함수)로 범용적으로 어디서든 쓸 수 있다.

이 규칙에 따라 Shared에 있는 컴포넌트가 갑자기 Feature를 참조해서 순환 참조 에러가 터지는 일을 방지할 수 있다.

2. Slices (슬라이스)
Layers가 기술적인 위계라면, Slices(슬라이스)는 비즈니스 도메인에 따른 수평적 분할이다. Entities, Features, Widgets 등의 레이어 내부는 슬라이스로 나뉜다.

예를 들어, 쇼핑몰 앱을 만든다고 가정해보자.

  • entities/user (사용자 관련 데이터)
  • entities/product (상품 관련 데이터)
  • features/auth-by-email (이메일 로그인 기능)
  • features/add-to-cart (장바구니 담기 기능)

이렇게 폴더를 나누면, 관련된 코드가 한곳에 모을 수 있다. 이전에는 로그인 기능을 수정하려면 components/LoginForm.js, actions/auth.js, reducers/auth.js, utils/validation.js를 찾아 헤맸다면, FSD에서는 features/auth-by-email 폴더 하나만 열면 관련된 파일이 모두 들어있는 것이다.

같은 레이어의 슬라이스끼리는 서로 의존하면 안 된다. features/cart가 features/checkout을 import 하면 안 된다. 만약 두 기능이 서로 필요하다면? 그것은 상위 레이어인 Widgets나 Pages에서 조합하거나, 공통 로직을 Shared로 내려야 한다. 이 규칙이 슬라이스 간의 결합도를 낮춰준다.

3. Segments (세그먼트)
슬라이스 내부는 다시 Segments(세그먼트)로 나뉜다. 이는 파일의 역할에 따라 분류된다.

  • ui/: 컴포넌트, 스타일
  • model/: 비즈니스 로직, 상태 관리(Store, Hooks)
  • api/: 서버 통신 로직
  • lib/: 해당 슬라이스 전용 유틸리티
  • config/: 설정 값, 상수

이 구조는 어떤 슬라이스를 열더라도 구조가 똑같다는 장점이 있다.

😅 직접 써보며 겪은 시행착오

이론은 완벽해 보였지만, 막상 내 프로젝트에 적용하려니 초반엔 꽤나 혼란스러웠다.

1. Domain 나누기
가장 많이 고민했던 부분이다. 예를 들어 '사용자 프로필 카드'를 만든다고 치자.

  • 단순히 사용자 정보를 보여주니까 Entities/User인가?
  • 안에 '팔로우 버튼'이 있으니까 Features인가?
  • 아니면 재사용 가능한 덩어리니까 Widgets인가?

결론적으로, 데이터 표시에 집중하면 Entity, 상호작용(클릭, 데이터 변경)이 핵심이면 Feature, 이것들을 조합하면 Widget으로 보낸다. 하지만 처음엔 이 기준 잡기가 정말 어려웠다. 너무 완벽하게 나누려다 개발 속도가 느려지기도 했다.

2. Next.js 환경에서의 혹독한 적응기
Next를 사용 중이었는데, Next.js도 app 폴더를 쓰고 FSD도 app 레이어를 쓴다. 이름이 겹친다..! 결국 FSD의 app 레이어 이름을 app-layerapp-init 등으로 바꾸고, Next.js의 app 폴더는 라우팅 용도로만 사용하며 FSD의 Pages 레이어를 import 해서 쓰는 방식으로 타협했다.

3. 보일러플레이트의 압박
파일 하나 만들 때마다 폴더 구조를 갖춰야 하니 파일 수가 엄청나게 늘어난다. 간단한 토이 프로젝트라면 배보다 배꼽이 더 클 수 있다. index.ts 관리도 은근히 귀찮다..ㅎ

✨ FSD의 명확한 장점

초반에는 위와 같은 이유들로 설계가 쉽지 않았는데 적응하고 나니 만족했다!

  1. 코드 구조가 명확하다: 특정 기능을 수정하기 위해서는 해당 기능 폴더에 진입하면 된다.
  2. 리팩토링에 대한 부담이 적다: 슬라이스 간 의존성이 분리되어 있고, Shared 계층이 안정적으로 받쳐주기 때문에 수정 시 영향 범위를 예측하기 쉽다.
  3. 코드가 정돈된다: 의존성 규칙이 강제되면서 프로젝트 전체 구조가 자연스럽게 깔끔해진다.

🙏 마무리..

작은 프로젝트나 팀원들이 익숙하지 않다면 FSD 아키텍처가 가장 나은 선택지는 아니겠지만 유지보수가 중요한 중대형 프로젝트나, 장기적으로 관리해야 할 서비스라면 FSD 아키텍처 또한 앞으로 개발을 해나가며 좋은 선택지가 될 수 있을 것이라 생각한다!

0개의 댓글