컴포넌트가 35개인데, 어디에 파일을 둬야 할까? — FSD 도입기

2026. 3. 21. 23:37·기타

안녕하세요 ~ 프론트엔드 개발자 이효린입니다 :D

 

혹시 이런 경험 있으신가요?

 

다른 사람이 짠 코드를 인수인계 받았는데, 어디서부터 봐야 할지 모르겠는 경험.
components/ 폴더를 열었더니 파일이 35개. 어떤 게 공통 컴포넌트인지, 어떤 게 특정 페이지에서만 쓰이는 건지 전혀 감이 안 잡히더라고요. 결국 파일을 하나하나 열어보면서 "이게 어디서 쓰이는 거지?" 하며 코드 탐정이 되어야 했어요. 😅

 

새로운 팀원이 합류했는데, 구조 설명에 30분이 걸리는 경험.
"이쪽은 이렇게 되어 있고, 근데 여기는 좀 달라요, 이건 예외적으로..." 설명하다 보면 저도 헷갈리기 시작하더라고요.

 

페이지 컴포넌트가 너무 커져서 쪼개기 시작했는데, depth만 깊어지는 경험.
pages/DataSource/components/Tree/nodes/NodeItem/... 이런 식으로 분리하다 보면 결국 pages/ 안에 또 다른 미니 프로젝트가 생겨버려요.

 

저는 이 세 가지를 다 겪었어요. 😇

 

이 글은 그 문제들을 해결하기 위해 FSD(Feature-Sliced Design)를 도입했던 과정을 정리한 글입니다!

어쩌다 이렇게 됐을까요?

어느 날 다른 팀원이 담당하던 코드를 인수받아서 기능을 개발해야 했어요.

열어보니 components/ 폴더에 파일이 35개. 도메인 구분 없이 flat하게 나열되어 있었습니다. workflow entity 하나만 봐도 81개 파일이 단일 폴더 안에 몰려 있었어요.

src/
├── components/   # 35개 컴포넌트가 도메인 구분 없이 flat하게
├── hooks/        # 11개 훅, 도메인 구분 없음
├── entities/
│   └── workflow/
│       └── canvas/  # 81개 파일 👀
├── utils/
├── stores/
└── pages/        # 16개 페이지

특정 기능을 수정하려면 어느 파일이 어디 있는지 파악하기 위해 여러 폴더를 왔다갔다 해야 했어요. 코드 한 줄 고치는데 파일을 7~8개씩 열어보는 일이 일상이 됐습니다.

정확히 뭐가 문제였을까요?

막연하게 "구조가 복잡하다"는 느낌을 받는 것과, "어떤 부분이 구체적으로 문제인가"를 파악하는 건 다르더라고요. 먼저 문제를 세 가지로 정리해봤어요.

 

① 컴포넌트가 어떤 도메인에 속하는지 모른다

components/ChatBubble.tsx를 보고 이게 chat 도메인 전용인지, 공통 컴포넌트인지 파일명만으로는 알 수가 없어요.

 

② entity 간 의존성 규칙이 없다
entities/workflow에서 entities/chat을 직접 import하고, entities/agent에서 entities/tool을 참조하는 코드가 섞여 있었습니다. 어디서 어디를 참조해도 되는지 기준이 없었어요.

 

③ 구조 기준이 없으니 사람마다 다르게 쓴다
팀원이 늘어날수록 같은 프로젝트인데 서로 다른 방식으로 파일을 배치하기 시작했어요. 코드 리뷰 때 "왜 여기 뒀어요?"라는 질문이 반복됐습니다.

세 가지 선택지를 비교해봤어요

문제를 파악했으니 어떤 구조로 바꿀지 고민했습니다. 세 가지 선택지를 검토했어요.

선택지 1. Atomic Design

디자인 시스템 문서에서 자주 보이는 구조죠 ! atoms / molecules / organisms / templates / pages로 나누는 방식이에요.

검토 결과: UI 재사용 단위는 명확해지지만, 비즈니스 로직의 경계를 정의하지 않았어요. organisms 안에 도메인 로직이 섞이기 시작하면 기존과 다른 방식으로 같은 혼란이 생길 것 같았습니다. 저희 문제의 핵심은 "컴포넌트를 얼마나 잘게 쪼개냐"가 아니라 "어떤 도메인에 속하는 코드인가"를 명확히 하는 것이었거든요.

선택지 2. 도메인 폴더 단순 분리

features/chat/, features/workflow/ 처럼 도메인 이름으로 폴더를 나누는 방식이에요.

검토 결과: 진입 장벽이 낮고 직관적이에요. 하지만 레이어 간 의존성 방향이 암묵적으로 남는 문제가 있었어요. features/chat에서 features/workflow를 참조해도 되는지, 순환 참조가 생기면 어떻게 처리하는지 기준이 없었거든요. 결국 같은 논의가 다른 형태로 반복될 것 같았습니다.

선택지 3. FSD (Feature-Sliced Design) ✅

레이어(Layer) - 슬라이스(Slice) - 세그먼트(Segment) 3단계 구조로 코드를 조직화하는 방법론이에요.

핵심은 의존성 방향을 구조 자체가 강제한다는 점입니다!

app → pages → features → entities → shared

상위 레이어가 하위 레이어만 참조할 수 있어요. 즉, entities끼리는 서로 참조할 수 없습니다. 이 규칙 하나로 "이 컴포넌트를 어디에 두어야 하는가"라는 질문의 대부분이 사라졌어요.

 

린터로 import 방향을 자동 검사할 수 있다는 점도 결정적이었습니다 :)

우리 팀에 맞게 조정했어요

FSD를 그대로 가져다 쓰면 생기는 문제가 있어요. entities와 features의 구분 기준이 추상적이에요.

공식 문서에서는 이렇게 설명합니다.

  • entities — "이것이 무엇인가?" (비즈니스 도메인 모델)
  • features — "사용자가 무엇을 할 수 있는가?" (사용자 인터랙션)

이론적으로는 명확해 보이지만, 막상 코드 앞에 서면 애매한 경우가 생기더라고요. 그래서 팀 내에서 규칙을 하나 추가했습니다.

 

💡 2개 이상의 entity가 결합되어야 하는 로직은 features로 승격한다.

 

예를 들어, '권한 + 데이터소스'를 함께 다루는 로직은 features/permission-management/로 분리하는 방식이에요.

// ❌ 이렇게 하면 안 돼요
// entities/workflow/model/useWorkflowChat.ts
import { ChatMessage } from '@/entities/chat'; // entity끼리 참조 금지!

// ✅ 이렇게 해요
// features/workflow-chat-test/model/useWorkflowChat.ts
import { ChatMessage } from '@/entities/chat';
import { WorkflowNode } from '@/entities/workflow';

 

이 기준을 실제 예시와 함께 문서화해서 팀 내 합의를 만들었어요 :)

목표 구조

논의 결과 저희 팀에서 정의한 구조는 아래와 같습니다.

src/
├── app/          # 라우터, 전역 스토어, 글로벌 스타일
├── pages/        # 라우팅 기반 페이지 컴포넌트
├── features/     # 사용자 인터랙션 (여러 entity 조합)
├── entities/     # 비즈니스 도메인 모델
└── shared/       # 공통 UI, API 클라이언트, 유틸리티

FSD 공식 스펙에는 widgets 레이어도 있는데요, 저희 프로젝트 규모에서는 pages가 그 역할을 충분히 담당할 수 있다고 판단해서 제외했어요.

각 slice 내부는 기술적 역할에 따라 segment로 나눕니다.

Segment 역할

ui/ 컴포넌트, 스타일
api/ API 호출, 어댑터
model/ 타입, 스키마, 훅, 비즈니스 로직
lib/ 유틸리티 함수
config/ 상수, 환경변수

점진적으로 적용했어요

한 번에 전체를 바꾸는 건 현실적으로 어렵죠. 기존 코드를 깨뜨리지 않으면서 단계를 나눠서 작업했어요.

Phase 1. shared/ 레이어 정리 — 공통 UI, API 클라이언트, 유틸리티 이동

 

Phase 2. app/ 레이어 분리 — 라우터, 전역 스토어 이동

 

Phase 3. entities/ 세그먼트 정규화 — components/ → ui/, hooks/ → model/

 

Phase 4. features/ 추출 — 여러 entity를 조합하는 로직을 pages에서 분리

 

Phase 5. ESLint 규칙 적용 — import 방향 자동 검사

신규 기능은 처음부터 FSD 구조로 작성하고, 기존 코드는 작업할 때 점진적으로 마이그레이션하는 방식으로 진행했어요. 현재 약 80% 적용 완료입니다!

바뀐 것들

구조를 바꾸고 나서 실제로 달라진 점들을 정리해봤어요.

파일 탐색이 빨라졌어요

entities/tool/api/adapter.ts처럼 경로만 봐도 "tool 도메인의 API 변환 로직"임을 바로 알 수 있어요. 코드를 열지 않아도 역할이 파악됩니다.

 

"어디에 두어야 하나" 논의가 줄었어요

레이어 의존성 규칙이 명확하니까 "이 컴포넌트는 어디에 있어야 하냐"는 PR 댓글이 거의 사라졌어요. 구조 자체가 답을 제시해주기 때문입니다 :)

 

API 변경 대응이 단순해졌어요

어댑터 패턴 덕분에 서버 스키마가 바뀌어도 adapter.ts 하나만 수정하면 됩니다.

 

신규 팀원 온보딩이 쉬워졌어요

폴더 구조만 보고도 도메인 경계를 파악할 수 있어서, 아키텍처 문서와 컨벤션 예시를 온보딩 자료로 활용하고 있습니다!

마치며

이 과정에서 가장 기억에 남는 말이 있어요.

"아키텍처를 도입할지 말지 먼저 고민을 했어야 한다. 작을 때 아키텍처를 도입하는 게 쉽다."

FSD를 도입한 건 좋은 결정이었지만, 프로젝트가 커진 뒤에 구조를 바꾸는 건 분명히 비용이 있었어요. 처음부터 어떤 기준으로 코드를 나눌지 팀 안에서 합의를 만들어두는 것이 훨씬 효율적이더라고요.

 

아키텍처를 도입할 때 중요한 건 어떤 구조를 선택하느냐가 아니라, 왜 이 구조를 선택했는지 팀이 납득하고 있느냐인 것 같아요. 문서화와 팀 합의 과정이 코드를 바꾸는 것만큼 중요했습니다.

 

FSD가 모든 프로젝트에 정답은 아니지만, "의존성 방향을 구조가 강제한다"는 핵심 아이디어는 어떤 구조를 선택하든 생각해볼 만한 가치가 있는 것 같아요 :)

 

도입하면서 겪었던 트러블이나 궁금한 점이 있으시면 댓글로 남겨주세요! 🙌

저작자표시 (새창열림)

'기타' 카테고리의 다른 글

옵시디언 + 클로드 코드로 나만의 RAG 챗봇 만들기 | Claudian 설치 방법  (0) 2026.02.22
2025년 1분기 회고록 + 4월까지 (feat. 취뽀후기 ?)  (5) 2025.05.04
[🐾놀멍 회고] 4. 10일 동안 640명이 쓴 서비스 만들기(2) | 서비스 배포 과정, 유레카 최종 프로젝트 우수상  (0) 2025.01.23
[🐾놀멍 회고] 3. 10일 동안 640명이 쓴 서비스 만들기(1) | 애자일 방법론(Scrum), 유레카 최종 프로젝트 우수상  (0) 2025.01.22
[🚖모여타 회고] 2. 이름뿐인 애자일 | MVP? MXP!  (2) 2025.01.20
'기타' 카테고리의 다른 글
  • 옵시디언 + 클로드 코드로 나만의 RAG 챗봇 만들기 | Claudian 설치 방법
  • 2025년 1분기 회고록 + 4월까지 (feat. 취뽀후기 ?)
  • [🐾놀멍 회고] 4. 10일 동안 640명이 쓴 서비스 만들기(2) | 서비스 배포 과정, 유레카 최종 프로젝트 우수상
  • [🐾놀멍 회고] 3. 10일 동안 640명이 쓴 서비스 만들기(1) | 애자일 방법론(Scrum), 유레카 최종 프로젝트 우수상
이효린
이효린
  • 이효린
    나는 이효린
    이효린
  • 전체
    오늘
    어제
    • 분류 전체보기 (43)
      • 🩵 React (15)
      • 💛 Javascript (3)
      • ⚓️ 개발환경 (9)
      • ⛓️‍💥 알고리즘 (0)
      • 🤍 ReactNative (1)
      • 🔥 취준일기 (2)
      • 🗂️ 자료구조&알고리즘 (2)
      • 기타 (7)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

    -
    React
    MSW
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
이효린
컴포넌트가 35개인데, 어디에 파일을 둬야 할까? — FSD 도입기
상단으로

티스토리툴바