sanguk.dev
작성완료
FSD 아키텍처 정리

FSD 아키텍처 정리

FSD(Feature Sliced Design)는 프론트엔드 애플리케이션 아키텍처로, 코드를 기능 단위로 슬라이싱하여 의존성을 단방향으로 유지하는 방법론이다. 구조는 Layers, Slices, Segments의 3단계로 나뉘며, 각 레이어는 역할에 따라 분류된다. 핵심 원칙으로는 단방향 의존성, Public API 필수, 도메인 기반 슬라이싱 등이 있다. 자주 발생하는 실수로는 잘못된 import 방향과 index.ts 없이 파일 접근 등이 있다.

FSD 아키텍처Web Front Developer

FSD란 무엇인가?

FSD(Feature Sliced Design) 는 프론트엔드 애플리케이션을 위한 아키텍처 방법론입니다.
그 뿌리는 FDA(Feature Driven Architecture) 에 두고 있으며, 핵심 아이디어는 코드를 기능(Feature) 단위로 슬라이싱하여 관심사를 명확하게 분리하고, 모듈 간 의존성을 항상 단방향으로 유지하는 것입니다.

💡 왜 FSD가 필요할까?
프로젝트 규모가 커질수록 코드 간 의존성이 복잡해지고, 어떤 파일을 수정했을 때 어디까지 영향을 미치는지 파악하기 어려워집니다. FSD는 이 문제를 구조적으로 해결합니다.


전체 구조 한눈에 보기


FSD는 3단계 계층 구조로 이루어져 있습니다.
계층 순서는 Layers → Slices → Segments 입니다.


1. Layers (레이어)

레이어는 FSD의 첫 번째 계층으로, 애플리케이션 전반에 걸친 코드를 역할에 따라 분류한 단위입니다.

핵심 규칙

상위 레이어는 하위 레이어를 의존할 수 있지만, 하위 레이어는 절대로 상위 레이어를 의존할 수 없습니다.
이 규칙 덕분에 의존성 그래프가 항상 한 방향(위 → 아래)으로만 흐르고, 특정 레이어를 수정했을 때 영향 범위가 명확하게 한정됩니다.

  • 아래로 내려갈수록 → 추상화가 심화 (비즈니스를 모르는 순수한 코드)
  • 위로 올라갈수록 → 비즈니스 로직이 심화 (도메인 지식이 담긴 코드)

레이어 목록

레이어역할비고
app앱 전체 진입점, 전역 Provider, 라우터 설정Next.js에서 라우터 역할
pages페이지 단위 컴포넌트Next.js App Router 사용 시 생략
widgets여러 feature/entity를 조합한 독립적인 UI 블록불필요 시 생략 가능
features사용자 인터렉션 및 특정 비즈니스 로직버튼 클릭, 폼 제출 등
entities특정 도메인의 API 호출 함수 및 DTO 정의서버 모델 관련 코드
shared비즈니스와 무관한 공통 유틸, UI 컴포넌트, 통신 설정어디서든 재사용 가능한 코드

레이어별 코드 예시

shared — 비즈니스 로직 없는 순수 공통 코드

typescript
// shared/api/base.ts — axios 인스턴스 설정
import axios from 'axios';

export const httpClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  withCredentials: true,
});

entities — 도메인 모델 및 API 호출

typescript
// entities/user/api/getUser.ts
import { httpClient } from '@/shared/api/base';
import { UserDTO } from '../model/types';

export const getUser = (id: string): Promise<UserDTO> =>
  httpClient.get(`/users/${id}`).then(res => res.data);

features — 사용자 인터렉션 로직

typescript
// features/auth-form/model/useLogin.ts
import { login } from '@/entities/session';

export const useLogin = () => {
  const handleSubmit = async (form: LoginForm) => {
    await login(form); // entities 레이어 의존 (허용)
  };
  return { handleSubmit };
};

2. Slices (슬라이스)

슬라이스는 FSD의 두 번째 계층으로, 각 레이어 안에서 비즈니스 도메인 단위로 코드를 묶는 폴더입니다.

핵심 규칙

  • 이름은 비즈니스 도메인 기반으로 자유롭게 결정합니다. (auth-form, user-profile, cart 등)
  • app과 shared 레이어에는 슬라이스를 두지 않습니다.
    • app → 앱 전체에 관련된 단일 코드 집합
    • shared → 도메인 지식이 없는 순수 추상 코드이므로 도메인 분리 불필요
  • 슬라이스 내부 파일은 반드시 세그먼트 폴더 안에 위치해야 합니다. 세그먼트 없이 파일을 직접 두면 안 됩니다.
  • 슬라이스는 반드시 index.ts** (Public API)** 를 가져야 합니다.

Public API가 중요한 이유

슬라이스 내부 구조가 어떻게 바뀌든, 외부에서는 index.ts를 통해서만 접근합니다. 덕분에 내부 리팩토링을 해도 외부 코드에 영향을 주지 않습니다.

typescript
// features/auth-form/index.ts
export { AuthForm } from './ui';         // UI 컴포넌트 공개
export * as authFormModel from './model'; // 모델 네임스페이스로 공개

외부에서는 이렇게 사용합니다:

typescript
import { AuthForm, authFormModel } from '@/features/auth-form';
//                                         ↑ index.ts를 통해서만 접근

디렉토리 구조 예시

javascript
features/
  ├── auth-form/
  │     ├── ui/
  │     │     └── AuthForm.tsx
  │     ├── model/
  │     │     ├── useLogin.ts
  │     │     └── types.ts
  │     ├── api/
  │     │     └── loginApi.ts
  │     └── index.ts     ← Public API (필수!)
  └── user-profile/
        ├── ui/
        ├── model/
        └── index.ts

3. Segments (세그먼트)

세그먼트는 FSD의 마지막 계층으로, 슬라이스 내부에서 기술적인 역할에 따라 코드를 분류하는 폴더입니다.

세그먼트 종류

세그먼트역할예시
uiUI 컴포넌트 및 스타일LoginForm.tsx, UserCard.tsx
model비즈니스 로직, 상태 관리, data aggregationuseLogin.ts, store.ts, types.ts
api백엔드 API 호출 코드loginApi.ts, getUserApi.ts
lib슬라이스 내부에서만 쓰는 유틸 함수formatDate.ts, validators.ts
config상수, 환경 설정 값constants.ts

💡 세그먼트 이름이 정해진 것은 아닙니다. 프로젝트에 맞게 hooks/, store/ 등을 추가할 수 있습니다.


의존성 흐름 정리

javascript
[app]
  ↓ 의존 가능
[pages]
  ↓
[widgets]
  ↓
[features]
  ↓
[entities]
  ↓
[shared]
  • features에서 entities를 import하는 것 ✅
  • entities에서 features를 import하는 것 ❌
  • shared에서 어떤 상위 레이어도 import하는 것 ❌

핵심 원칙 5가지

  1. 단방향 의존성 — 상위 레이어 → 하위 레이어 방향만 허용
  2. Public API 필수 — 각 슬라이스는 index.ts로만 외부에 노출
  3. 도메인 기반 슬라이싱 — 슬라이스 이름은 비즈니스 도메인 기반으로 결정
  4. 세그먼트 내 배치 — 모든 파일은 반드시 세그먼트 폴더 안에 위치
  5. 영향 범위 최소화 — 의존성 그래프의 영향이 일부 레이어에만 국한되도록 설계

자주 하는 실수

실수올바른 방법
entities에서 features를 import절대 금지. 방향 반대
index.ts 없이 내부 파일 직접 import반드시 index.ts를 통해 접근
세그먼트 없이 슬라이스 루트에 파일 배치반드시 ui/, model/ 등 세그먼트 안에 배치
shared에 도메인 로직 작성shared는 비즈니스 로직 없는 순수 코드만

참고 자료

  • FSD 공식 문서
  • FSD GitHub

댓글

댓글 작성

0 / 1000