F.S.D 아키텍처

박요셉·2025년 5월 2일

Simple note

목록 보기
13/18

Feature-Sliced Design (FSD) 아키텍처 가이드

목차

소개

Feature-Sliced Design(FSD)은 프론트엔드 애플리케이션을 위한 아키텍처 방법론으로, 코드를 기능 중심으로 모듈화하고 각 기능을 독립적으로 관리하는 방식입니다. 이 아키텍처는 특히 대규모 프론트엔드 애플리케이션에서 코드의 유지보수성과 재사용성을 높이기 위해 설계되었습니다.

FSD의 핵심 원칙은 다음과 같습니다:

  • 기능 기반 분할: 애플리케이션을 기능별로 나누어 각 기능을 독립적인 모듈로 관리
  • 레이어링: 기능을 레이어로 나누어 책임을 분리
  • 단방향 의존성: 상위 레이어는 하위 레이어에만 의존할 수 있음
  • 공개 API: 각 모듈은 명확한 공개 API를 통해 외부와 소통

아키텍처 구조

FSD 아키텍처는 세 가지 주요 구성 요소로 이루어져 있습니다:

레이어 (Layers)

레이어는 코드의 최상위 구조로, 각각 특정 책임 영역을 가집니다. 레이어는 다음과 같은 계층 구조를 형성합니다 (위에서 아래로):

  1. App - 애플리케이션 초기화 및 글로벌 설정
  2. Processes (사용되지 않음) - 여러 페이지에 걸친 프로세스
  3. Pages - 페이지 컴포넌트
  4. Widgets - 재사용 가능한 UI 블록
  5. Features - 사용자 시나리오와 비즈니스 기능
  6. Entities - 비즈니스 도메인 모델
  7. Shared - 공통 유틸리티 및 UI 키트

중요한 규칙: 하위 레이어는 상위 레이어에 의존할 수 없습니다. 예를 들어, Entities는 Features를 import할 수 없습니다.

슬라이스 (Slices)

각 레이어 내에서 코드는 비즈니스 도메인에 따라 슬라이스로 나뉩니다. 슬라이스는 특정 비즈니스 엔티티나 기능을 중심으로 구성됩니다.

예시:

  • Entities 레이어: User, Product, Order 슬라이스
  • Features 레이어: UserAuth, AddToCart, Checkout 슬라이스

세그먼트 (Segments)

각 슬라이스는 목적에 따라 세그먼트로 더 세분화됩니다. 일반적인 세그먼트는 다음과 같습니다:

  • ui - UI 컴포넌트
  • model - 상태 관리 및 비즈니스 로직
  • api - 서버 통신
  • lib - 유틸리티 함수
  • config - 설정 값
  • types - 타입 정의

레이어 상세 설명

App 레이어

애플리케이션의 진입점으로, 다음을 포함합니다:

  • 애플리케이션 초기화
  • 라우팅 설정
  • 글로벌 프로바이더
  • 글로벌 스타일
  • 환경 설정
src/
└── app/
    ├── providers/
    ├── styles/
    ├── routes/
    └── index.ts

Processes 레이어

현재는 거의 사용되지 않는 레이어입니다. 여러 페이지에 걸친 복잡한 프로세스(예: 다단계 등록)를 관리합니다.

Pages 레이어

애플리케이션의 페이지를 정의합니다. 각 페이지는 위젯, 기능, 엔티티를 조합하여 구성됩니다.

src/
└── pages/
    ├── HomePage/
    ├── ProfilePage/
    ├── ProductPage/
    └── ...

Widgets 레이어

재사용 가능한 UI 블록으로, 여러 기능과 엔티티를 조합하여 더 큰 UI 구성 요소를 만듭니다.

src/
└── widgets/
    ├── Header/
    ├── Sidebar/
    ├── Footer/
    └── ...

Features 레이어

사용자 시나리오와 비즈니스 기능을 구현합니다. 사용자 상호작용과 관련된 로직을 포함합니다.

src/
└── features/
    ├── UserAuth/
    │   ├── ui/
    │   ├── model/
    │   ├── api/
    │   └── index.ts
    ├── AddToCart/
    └── ...

Entities 레이어

비즈니스 도메인 모델과 관련된 모든 것을 포함합니다. 데이터 구조와 기본 조작 방법을 정의합니다.

src/
└── entities/
    ├── User/
    │   ├── ui/
    │   ├── model/
    │   ├── api/
    │   └── index.ts
    ├── Product/
    └── ...

Shared 레이어

프로젝트 전체에서 사용되는 공통 코드를 포함합니다. 비즈니스 로직에 종속되지 않은 재사용 가능한 요소들을 담습니다.

src/
└── shared/
    ├── ui/
    ├── lib/
    ├── api/
    └── ...

자주 묻는 질문 (FAQ)

Features와 Entities의 차이점

Q: Features와 Entities의 차이점은 무엇인가요?

A: Features와 Entities는 다음과 같은 차이점이 있습니다:

Entities (엔티티):

  • 무엇(What) 에 해당: 비즈니스 도메인 객체 (User, Product 등)
  • 데이터 모델과 기본 조작 방법 정의
  • 여러 기능에서 공통으로 사용되는 데이터 구조
  • 예: User 엔티티는 사용자 정보 모델, 사용자 데이터 가져오기 API, 사용자 카드 컴포넌트 포함

Features (기능):

  • 어떻게(How) 에 해당: 사용자 행동과 상호작용 (로그인, 팔로우 등)
  • 여러 엔티티를 활용한 비즈니스 로직
  • 특정 사용자 시나리오를 구현하는 코드
  • 예: UserAuth 기능은 로그인/로그아웃 로직, 인증 폼, 토큰 관리 포함

특정 페이지에서만 사용되는 기능의 배치

Q: 특정 페이지에서만 사용되는 기능(예: 메인 페이지의 팔로우 기능)은 어디에 배치해야 하나요?

A: 특정 페이지에서만 사용되는 기능의 배치는 다음 기준으로 결정할 수 있습니다:

  1. 재사용 가능성이 있는 경우: Features 레이어에 배치

    src/features/followUser/
  2. 페이지에 특화되어 있고 재사용 가능성이 없는 경우: Pages 레이어 내부에 배치

    src/pages/MainPage/model/followFeature.ts

결정 기준:

  • 재사용성: 다른 페이지에서도 사용될 가능성이 있는가?
  • 독립성: 페이지의 다른 요소들과 밀접하게 연결되어 있는가?
  • 복잡성: 별도의 모듈로 분리할 가치가 있는가?

페이지 내부 로직의 적절한 배치

Q: 메인 페이지의 내부 전용 훅들과 API는 어디에 배치해야 하나요? 비즈니스 로직을 페이지 내부에 넣는 것이 적절한가요?

A: 페이지 내부 로직은 다음과 같이 분류하여 배치할 수 있습니다:

  1. 페이지 전용 UI 로직: Pages/MainPage/ui/

    • 페이지 레이아웃 컴포넌트
  2. 페이지 특화 비즈니스 로직: Pages/MainPage/model/

    • 페이지 전용 상태 관리
    • 페이지 특화 훅
  3. 페이지 전용 API 호출: Pages/MainPage/api/

    • 페이지 데이터 가져오기

비즈니스 로직을 페이지 내부에 넣는 것은 다음 경우에만 적절합니다:

  • 해당 로직이 페이지에만 특화되어 있고
  • 재사용될 가능성이 없으며
  • 페이지의 UI 표현과 밀접하게 연결되어 있을 때

그 외의 경우, 특히 중요한 비즈니스 로직은 적절한 레이어(Entities 또는 Features)로 분리하는 것이 좋습니다.

세그먼트 분류와 React Hooks 배치

Q: 세그먼트 분류에서 model은 무엇을 의미하나요? React hooks는 어디에 배치해야 하나요?

A: FSD에서 'model' 세그먼트와 React hooks 배치는 다음과 같습니다:

model 세그먼트:

  • 상태 관리와 비즈니스 로직을 담당
  • 상태 저장소 (Redux, MobX 등)
  • 상태 변경 함수 (actions, reducers)
  • 비즈니스 로직

React hooks 배치:
1. model 폴더에 배치하는 hooks:

  • 상태 관리 관련 hooks
  • 비즈니스 로직을 포함하는 hooks
  • 예: useUserState, useProductFilter
  1. lib 폴더에 배치하는 hooks:
    • 순수 유틸리티 성격의 hooks
    • 상태 변경 없이 기능만 제공하는 hooks
    • 예: useDebounce, useLocalStorage

hooks의 배치는 그 목적과 책임에 따라 결정됩니다. 상태 관리나 비즈니스 로직을 다루는 hooks는 model에, 순수 유틸리티 성격의 hooks는 lib에 배치하는 것이 적절합니다.

실제 구현 예시

Entities 예시 (User)

// entities/user/model/types.ts
export interface User {
  id: string;
  name: string;
  email: string;
  avatar: string;
}

// entities/user/api/index.ts
export const fetchUser = async (id: string): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

// entities/user/ui/UserCard.tsx
export const UserCard = ({ user }: { user: User }) => (
  <div className="user-card">
    <img src={user.avatar} alt={user.name} />
    <h3>{user.name}</h3>
  </div>
);

Features 예시 (FollowUser)

// features/followUser/model/index.ts
import { User } from 'entities/user';

export const followUser = async (userId: string): Promise<void> => {
  await fetch(`/api/users/${userId}/follow`, { method: 'POST' });
  // 상태 업데이트 로직...
};

// features/followUser/ui/FollowButton.tsx
import { followUser } from '../model';

export const FollowButton = ({ userId }: { userId: string }) => {
  const [isFollowing, setIsFollowing] = useState(false);
  
  const handleFollow = async () => {
    await followUser(userId);
    setIsFollowing(true);
  };
  
  return (
    <button onClick={handleFollow}>
      {isFollowing ? '팔로잉' : '팔로우'}
    </button>
  );
};

참고 자료

profile
개발자 지망생

0개의 댓글