기존 아키텍처에서 FSD로의 마이그레이션
이 가이드는 기존 아키텍처를 Feature-Sliced Design(FSD) 으로 단계별 전환하는 방법을 설명합니다.
아래 폴더 구조를 예시로 살펴보세요. (파란 화살표를 클릭하면 펼쳐집니다).
📁 src
📁 actions
- 📁 product
- 📁 order
- 📁 api
- 📁 components
- 📁 containers
- 📁 constants
- 📁 i18n
- 📁 modules
- 📁 helpers
📁 routes
- 📁 products.jsx
- 📄 products.[id].jsx
- 📁 utils
- 📁 reducers
- 📁 selectors
- 📁 styles
- 📄 App.jsx
- 📄 index.js
시작 전 체크리스트
Feature-Sliced Design(FSD)이 정말 필요한지 먼저 확인하세요.
모든 프로젝트가 새로운 아키텍처를 요구하는 것은 아닙니다.
전환을 고려해야 할 징후
- 신규 팀원이 프로젝트에 적응하기 어려워하는 경우
- 코드 일부를 수정할 때, 관련 없는 다른 코드에 오류가 발생하는 경우가 잦은 경우
- 새 기능을 추가할 때 고려해야 할 사항이 너무 많아 어려움을 겪는 경우
팀의 합의 없이 FSD 전환을 시작하지 마세요.
팀 리더라도 전환의 이점이 학습·전환 비용을 상회한다는 점을 먼저 설득해야 합니다.
또한, 개선 효과가 바로 눈에 띄지 않을 수 있으므로 팀원 및 프로젝트 매니저(PM) 의 승인을 사전에 확보하고 이점을 공유하세요.
- FSD 전환은 단계적으로 진행할 수 있어 기존 기능 개발을 중단하지 않아도 됩니다.
- 명확한 아키텍처 구조는 신규 개발자 온보딩 시간을 단축합니다.
- 공식 문서를 활용하면 별도 문서 유지·관리 비용을 절감할 수 있습니다.
마이그레이션을 시작하기로 결정했다면, 📁 src
폴더에 별칭(alias)을 설정하는 것을 첫 단계로 삼으세요.
1단계: 페이지 단위로 코드 분리하기
대부분의 커스텀 아키텍처는 규모와 관계없이 이미 어느 정도 페이지 단위로 코드를 나누고 있습니다. 📁 pages
폴더가 있다면 이 단계를 건너뛰어도 됩니다.
위에 예시 폴더처럼 📁 routes
만 있다면 다음 순서를 따르세요.
📁 pages
폴더를 새로 만듭니다.📁 routes
에 있던 페이지용 컴포넌트를 가능한 한 모두📁 pages
폴더로 옮깁니다.- 코드를 옮길 때마다 해당 페이지 전용 폴더를 만들고 그 안에
index
파일을 추가해 entry를 노출합니다.
이 단계에서는 Page A에서 Page B의 코드를 import해도 괜찮습니다. 나중 단계에서 이러한 의존성을 분리할 예정이니, 우선 페이지 폴더를 만드는 것에 집중하세요.
route file:
export { ProductPage as default } from "src/pages/product"
page index file:
export { ProductPage } from "./ProductPage.jsx"
page component file:
export function ProductPage(props) {
return <div />;
}
2단계: 페이지 외부 코드를 분리하기
📁 src/shared
폴더를 만든다.📁 pages
또는📁 routes
를 import하지 않는 모든 코드를 이곳으로 이동한다.
📁 src/app
폴더를 만든다.📁 pages
또는📁 routes
를 import하는 코드를 이곳으로 옮긴다. 라우트 파일도 여기에 포함한다.
Shared layer에는 slice가 없다.
따라서 segment 간 import는 자유롭다.
이제 폴더 구조는 다음과 같아야 합니다:
📁 src
📁 app
📁 routes
- 📄 products.jsx
- 📄 products.[id].jsx
- 📄 App.jsx
- 📄 index.js
📁 pages
📁 product
📁 ui
- 📄 ProductPage.jsx
- 📄 index.js
- 📁 catalog
📁 shared
- 📁 actions
- 📁 api
- 📁 components
- 📁 containers
- 📁 constants
- 📁 i18n
- 📁 modules
- 📁 helpers
- 📁 utils
- 📁 reducers
- 📁 selectors
- 📁 styles
3단계: 페이지 간의 cross-imports 해결
한 페이지가 다른 페이지의 코드를 가져오고 있다면 두 가지 방법으로 의존성을 제거한다.
방법 | 사용 시점 |
---|---|
A. 코드 복사 | 페이지마다 로직이 달라질 가능성이 있거나, 재사용성이 낮을 때 |
B. Shared로 이동 | 여러 페이지에서 공통으로 쓰일 때 |
- Shared 이동 위치 예시
- UI 구성 요소 →
📁 shared/ui
- 설정 상수 →
📁 shared/config
- 백엔드 호출 →
📁 shared/api
- UI 구성 요소 →
코드 복사는 잘못이 아니다. 중복보다 의존성 최소화가 더 중요할 때가 많다.
다만 비즈니스 로직은 중복을 피해야 하며, 복사 시에도 DRY 원칙을 염두에 둔다.
4단계: Shared 레이어 정리하기
- 한 페이지에서만 쓰이는 코드는 해당 페이지 slice로 이동한다.
actions / reducers / selectors
도 예외가 아니다. 사용처와 가까이 두는 편이 좋다.
Shared는 모든 layer가 의존할 수 있는 공통 의존점이므로, 코드를 최소화해 변경 위험을 낮춘다.
최종 폴더 구조는 다음과 같아야 합니다:
📁 src
- 📁 app (unchanged)
📁 pages
📁 product
- 📁 actions
- 📁 reducers
- 📁 selectors
📁 ui
- 📄 Component.jsx
- 📄 Container.jsx
- 📄 ProductPage.jsx
- 📄 index.js
- 📁 catalog
📁 shared (only objects that are reused)
- 📁 actions
- 📁 api
- 📁 components
- 📁 containers
- 📁 constants
- 📁 i18n
- 📁 modules
- 📁 helpers
- 📁 utils
- 📁 reducers
- 📁 selectors
- 📁 styles
5단계: 기술적 목적별 segment 정리
segment | 용도 예시 |
---|---|
ui | Components, formatters, styles |
api | Backend requests, DTOs, mappers |
model | Store, schema, business logic |
lib | Shared utilities / helpers |
config | Configuration files, feature flags |
“무엇인지”가 아니라 “무엇을 위해” 존재하는지를 기준으로 나눈다.
따라서components
,utils
,types
같은 이름은 지양한다.
- 각 페이지에
ui / model / api
등 필요한 segment를 만든다. - Shared 폴더를 정리한다.
components·containers
→shared/ui
helpers·utils
→shared/lib
(기능별 그룹화 후)constants
→shared/config
선택 단계
6단계: 여러 페이지에서 재사용되는 Redux slice를 Entities / Features layer로 분리하기
- 여러 페이지에서 재사용되는 Redux slice는 주로 product, user 같은 business entity를 표현합니다.
이 경우 Entities layer로 옮기고, entity마다 폴더를 하나씩 만듭니다. - 댓글 작성처럼 사용자 행동(action) 을 다루는 slice는 Features layer로 이동합니다.
Entities와 Features는 서로 독립적으로 사용될 수 있도록 설계되어 있습니다.
Entitles 간 연결이 필요하면 Business-Entities Cross-Relations 가이드를 참고하세요.
해당 slice와 연관된 API 함수는 📁 shared/api
에 그대로 두어도 무방합니다.
7단계: modules 폴더 리팩터링
📁 modules
는 과거에 비즈니스 로직을 모아 두던 곳으로, 성격상 Features layer와 비슷합니다.
단, 앱 Header처럼 large UI block(예: global Header, Sidebar)이라면 Widgets layer로 옮기는 편이 좋습니다.
8단계: shared/ui에 presentational UI 기반 마련하기
📁 shared/ui
에는 비즈니스 로직이 전혀 없는, 재사용 가능한 presentational UI 컴포넌트만 남겨야 합니다.
- 기존
📁 components
·📁 containers
에 있던 컴포넌트에서 비즈니스 로직을 분리해 상위 layer로 이동합니다. - 여러 곳에서 쓰이지 않는 부분은 복사(paste) 해서 각 layer에서 독립적으로 관리해도 괜찮습니다.