'이 파일은 어디에 둬야 하지?'에서 벗어나기 위한 FSD 폴더 구조 정리

2026. 1. 22. 17:53·FRONTEND

 

프로젝트가 커질수록 가장 자주 발생하는 문제는 '이 파일은 어디에 둬야 하지?'라는 질문이다.

기능이 늘어나면 컴포넌트, API, 훅, 타입이 여기저기 흩어지고, 작은 수정도 영향 범위를 예측하기 어려워진다.

 

이 글은 FSD(Feature-Sliced Design)를 이번 프로젝트에 맞춰 적용하면서 정리한 판단 기준과 폴더 구조를 기록한 글이다.

*Processes 레이어가 deprecated 된 이후의 방식을 기준으로 설명한다.

 

0. 프로젝트 맥락 - 홈 화면이 가진 특성이 구조를 결정한다

이번 프로젝트의 홈 화면은 대략 이런 형태이다.

이 한 장의 화면만 봐도 '어디까지가 전역 골격이고, 어디부터가 기능이며, 무엇이 도메인 상태인지'가 갈린다.

- 하단 탭은 단순 UI가 아니라 라우팅과 결합된 앱 골격이다.

- 링크 복사는 사용자 액션(동사)이므로 feature로 분리하는 편이 자연스럽다.

- 값2/값3은 화면 컴포넌트가 아니라 도메인 상태(명사)와 계산 로직으로 관리되어야 한다.

 

이 글은 이 판단을 FSD의 레이어 규칙으로 안정적으로 고정하는 과정이다.


1. 구조가 없을 때 벌어지는 일

대부분의 프로젝트는 아래와 같은 구조로 시작한다.

초기에는 빠르고 직관적이다. 하지만 화면과 기능이 늘어나는 순간부터 문제가 시작된다.

- 어떤 UI가 특정 페이지 전용인지, 여러 페이지에서 재사용되는지 구분이 흐려진다.

- API 요청 코드가 apis/에 쌓이면서 어떤 기능에 필요한 요청인지 헷갈리며 도메인이 사라진다.

- 공용 훅과 기능 훅이 섞여서 의존성이 꼬이기 시작한다.

- 신규 기능을 추가할 때마다 '일단 여기 넣고 보자'가 반복된다.

- 수정할 때마다 '이거 다른 화면에도 쓰던데?'라는 불안이 생긴다.

 

이 상태가 되면 개발 생산성이 떨어지는 것보다 더 큰 문제가 생긴다.

팀이 구조를 믿지 못하게 되는 상황이 된다.

 

결국 '이 코드는 누가 제일 잘 아는가'가 중요해지고, 기존 작업자 의존이 심해진다.

이러한 문제를 해결하기 위해 FSD를 도입했다.


2. FSD란 무엇인가

FSD는 프론트엔드 애플리케이션 구조를 설계하기 위한 방법론이다. 핵심은 다음 두 가지이다.

 

1. 코드를 재사용 범위와 역할에 따라 명확히 나눈다.

2. 의존성 방향(단방향)을 강제해 구조적 혼란을 막는다.

FSD는 코드를 세 가지 축으로 나눈다.

- Layers (레이어) : 재사용 범위와 의존성 방향에 따라 수직으로 나누는 계층

- Slices (슬라이스) : 레이어 안에서 비즈니스 도메인별로 나누는 묶음

- Segments (세그먼트) : 슬라이스 안에서 기술 목적별로 나누는 폴더

 

이 조합이 제공하는 가장 큰 장점은 하나다.

코드를 어디에 둬야 하는지 판단하는 기준이 생긴다는 점이다.


3. Layers - '어디까지 재사용되는가'로 나누는 기준

FSD에서 가장 중요한 규칙은 단방향 의존성이다.

- 위 레이어는 아래 레이어를 import 할 수 있다

- 아래 레이어는 위 레이어를 import 할 수 없다

 

일반적인 레이어 흐름은 다음과 같다.

app → pages → widgets → features → entities → shared

 

각 레이어는 다음 역할을 가진다. (진행하는 프로젝트 기준으로 설명)

 

3-1. app - 앱 전역을 조립하는 레이어

app은 앱이 실행되기 위한 골격이 모이는 곳이다.

라우팅, 프로바이더, 전역 레이아웃, 전역 설정이 들어간다.

 

이번 프로젝트에서 app은 이런 모양이 된다.

슬라이스가 없고 세그먼트만 존재하는 형태가 자연스럽다.

 

3-2. pages - 라우팅되는 화면 단위

pages는 URL에 매핑되는 화면이다.

이 레이어의 핵심은 무엇을 보여줄지가 아닌 무엇을 조립할지이다.

 

이번 프로젝트라면 이런 페이지들이 된다.

페이지 내부에 로직이 과도하게 들어가면 다른 레이어의 역할이 무너진다.

그렇기 때문에 페이지는 큰 덩어리들을 가져와 배치하는 곳으로 남겨두는 편이 구조가 오래간다.

 

3-3. widgets - 여러 페이지에서 조립되는 큰 UI 블록

widgets는 페이지에서 조립되는 큰 블록 UI이다.

여러 페이지에서 재사용될 수 있는 덩어리가 들어간다.

중요한 점은 '위젯은 UI 덩어리'라는 점이다.

위젯은 내부에서 feature/entities를 가져다 쓰되, 자기보다 위 레이어(app/pages)를 알면 안 된다는 규칙만 지키면 된다.

 

3-4. features - 사용자 액션(동사) 단위

features는 버튼 클릭, 모달 열기, 폼 제출처럼 동사 기준으로 나뉜다.

이 레이어는 'UI도 포함할 수 있지만, 본질은 사용자 액션'이다.

 

다음과 같은 역할을 하게 된다.

- auth/social-login: 소셜 로그인 실행

- auth/refresh-session: refresh로 세션 갱신

- profile/create-profile: 프로필 생성 제출

- profile/nickname-check: 닉네임 중복 검사(디바운스/검증 상태 포함)

- share/copy-share-link: 링크 복사(토스트 포함)

 

홈 화면 요구사항으로 보면 '링크 복사'는 대표적인 feature이다.
반대로 '고정값/값2/값3의 계산'은 사용자 액션이 아니라 상태 모델에 가까우므로 entity 쪽으로 내려가야 한다.

 

3-5. entities - 비즈니스 도메인(명사)의 기준점

entities는 프로젝트가 다루는 명사 기준의 타입/상태/API가 모인다.

이 레이어가 잡히면 화면이 늘어나도 데이터의 기준점이 흔들리지 않는다.

 

- session/{model,api}: access/refresh, 로그인 상태

- user/model: 사용자 정보

 

 

예를 들어 랭킹 정책이 '1위는 고정, 2~10위만 갱신'이라면

이 조건은 ranking/model의 selector/계산 로직에서 정해지고 UI는 결과만 받아 그리는 편이 안전하다.

 

3-6. shared - 완전 공용(비즈니스와 최대한 분리)

shared는 특정 도메인에 속하지 않고 어디서든 쓰일 수 있는 코드가 모인다. 

여기서 기준은 단순하다.
ex) 이 코드가 방문자(guest)를 알면 shared가 아니다.


4. Processes가 빠진 이유와 대체 방식

FSD의 레이어 설명에는 Processes가 deprecated로 표기되어 있다.

복잡한 페이지 간 시나리오를 담던 레이어였지만, 이제는 deprecated로 정리되어 있다.

 

이 변화는 현실적인 문제를 반영한 결과로 보였다.

- '페이지 간 시나리오'는 대부분 라우팅과 상태로 해결 가능하다.

- processes는 범위가 애매해 또 하나의 '여기 둬도 되나?' 폴더가 되기 쉽다.

- 복잡한 흐름을 한 폴더로 모으면 오히려 추적이 어려워진다.

 

대신 다음과 같은 방식이 자연스럽게 자리 잡았다.

- 라우팅 / 가드 / 셸 구조 → app에서 해결

- 사용자 행동 단위 → features로 분리

- 상태의 기준점 → entities에 두기


5. Segments - components/hooks가 아니라 ui/api/model로 나누는 이유

폴더를 components, hooks, types로 나누면 처음에는 직관적이다.

하지만 시간이 지나면 '이 코드가 왜 존재하는지'가 사라진다. 기술 형태만 남고 도메인이 지워진다.

 

FSD에서는 세그먼트를 목적 중심으로 나눈다.

- ui : 화면, 컴포넌트, 스타일

- api : 서버 통신

- model : 상태, 스키마, 비즈니스 로직

- lib : 슬라이스 내부 유틸

- config : 설정 파일, feature flag

 

폴더를 보는 순간 '이 파일이 무슨 목적으로 존재하는가'가 보이기 시작한다.

이게 규모가 커질수록 결정적으로 크게 작동한다.


6. 의문 - layouts는 왜 app에 있나

처음에는 하단 탭도 공용 컴포넌트처럼 보였다. 그래서 shared/ui에 두려고 했었다.

하지만 폴더 구조를 공부하다 보니 이번 프로젝트의 하단 탭은 단순 UI가 아니었다.

 

1. /home, /guests, /ranking 라우팅 구조와 강하게 결합된다.

2. 현재 활성 탭은 URL과 동기화되어야 한다.

3. 방문자 탭 배지(새로 온 방문자 체크)는 전역 상태와 결합된다.

4. 어떤 페이지에서는 탭이 사라져야 한다.

 

즉, 탭은 재사용 UI가 아니라 앱의 골격이다.
그래서 app/layouts에 두는 것이 구조적으로 자연스럽다.

 

그러면서 'layouts에 두면 늘 고정되어 있는 것은 아닌가?'라는 의문이 들었다.

이에 대한 답으로는 layout에 둔다고 해서 모든 페이지에 항상 탭이 붙는 것은 아니라는 것이다.

 

라우트 구조로 탭이 있는 구간 / 없는 구간을 나누면 된다.

이 차이를 라우트 구조로 표현하는 순간 layout의 의미가 명확해진다.


7. 예시 - HomePage.tsx 하나로 시작한 상태에서의 분해 기준

처음에는 HomePage.tsx 하나로 시작하는 것이 자연스럽다.

문제는 '언제 무엇을 빼낼지' 기준이 없을 때 생긴다. 기준만 있으면 분해는 어렵지 않다.

 

7-1. HomePage에 남길 것

- 위젯을 조립하는 역할

- 페이지 수준의 레이아웃 배치

- 라우팅 이동 트리거 정도

 

7-2. HomePage에서 빠질 것

- 홈 상단 요약(고정값/값2/값3 카드 묶음) → widgets/home-summary

- 공유 링크 패널(복사 버튼, 토스트) → widgets/share-link-panel

- 링크 생성/조회 보장 같은 액션 → features/share/ensure-share-link

- 고정값/값2/값3의 계산 로직, 상태 기준점 → entities/*/model

- API 호출 정의 → entities/*/api 또는 해당 feature/api

 

이렇게 분리하면 HomePage는 화면으로 남고, 나머지는 각자의 역할로 이동한다.

결과적으로 수정할 때 '어디를 고쳐야 하는지'가 더 빠르게 보이게 된다.


8. 결론

FSD를 도입하며 가장 크게 달라진 점은 폴더 구조가 아니다.
결정 비용이 사라진 것이 핵심이다.

 

새 코드를 만들 때마다 이 질문만 하면 된다.

- 이 코드는 앱 전역 조립인가 → app

- 이 코드는 라우팅되는 화면인가 → pages

- 이 코드는 큰 덩어리 UI인가 → widgets

- 이 코드는 사용자 액션인가 → features

- 이 코드는 도메인 데이터의 기준점인가 → entities

- 이 코드는 완전 공용인가 → shared

 

FSD 아키텍처를 공부하면서 협업하기 좋은 폴더 구조란
누가 코드를 추가하더라도 같은 기준으로 같은 위치를 선택할 수 있는 구조라는 결론에 도달했다.

'FRONTEND' 카테고리의 다른 글

프론트엔드 개발자는 무엇을 설계하고 만드는가  (2) 2025.08.05
[UI/UX 분석] 토스 ① : 홈 화면 살펴보기  (1) 2025.07.22
React, Vue, Angular 차이점  (0) 2025.05.26
실시간 협업 캔버스에서의 동기화 처리 방식 (Yjs 기반)  (0) 2025.05.18
Yjs Awareness로 실시간 사용자 상태 공유하기  (0) 2025.05.11
'FRONTEND' 카테고리의 다른 글
  • 프론트엔드 개발자는 무엇을 설계하고 만드는가
  • [UI/UX 분석] 토스 ① : 홈 화면 살펴보기
  • React, Vue, Angular 차이점
  • 실시간 협업 캔버스에서의 동기화 처리 방식 (Yjs 기반)
uoaheu
uoaheu
uoaheu 님의 블로그 입니다.
  • uoaheu
    uoaheu 님의 블로그
    uoaheu
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • 알고리즘 (7)
      • CS (9)
      • FRONTEND (9)
        • React (12)
        • Kotlin (1)
        • JS (5)
        • HTML (2)
      • SQL (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    toss uiux
    boj25418
    토스분석
    BFS
    유레카3기
    toss 분석
    useactionstate
    부트캠프후기
    리액트usestate
    토스 uiux
    멀티캠퍼스it부트캠프
    혼자서 공부하는 네트워크
    mysql 피벗테이블
    엘지유플러스유레카프론트엔드
    알고리즘
    mysql로 피벗테이블만들기
    백준1926번
    이더넷프레임
    토스 앱 분석
    mysql
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
uoaheu
'이 파일은 어디에 둬야 하지?'에서 벗어나기 위한 FSD 폴더 구조 정리
상단으로

티스토리툴바