본문 바로가기

카테고리 없음

[스터디] React 폴더 구조, FSD Atomic Design 모노레포까지

728x90

❓폴더 구조 : 그게 뭔데

지금까지 나는 폴더 구조에 대해 별 생각이 없었다.

이전 회사에서는 혼자서 일했었고, 새로운 사람이 오고 나서도 프로젝트가 급박해서 다른 생각을 할 여력이 없었다.

 

그러다 퇴사를 하고 이직을 준비하면서 항해 부트캠프를 듣게 되었고, FSD를 알게 되었다.

FSD를 공부하면서 느꼈던 것은 ‘왜 이렇게까지 해야 하지?’였다.

처음 보는 개념에, 그 개념에 따라 폴더를 나눠야 하니 감이 오지 않았다.

팀원들과 이야기를 나누면서 해봐도 의견이 나뉘었다.

회사에서 프로젝트 진행하기도 바쁜데 이거 하나까지 이렇게 정해야 한다고?라는 생각에 ‘FSD는 별로다’라고 정해버렸다.

 

그리고 이 생각을 끝으로 폴더 구조는 다시 저 멀리 사라졌다.

 

 

‼️ 깨달음의 순간

 

그러다 새로운 직장에 들어오게 되고, 다른 사람의 코드를 내가 분석해야 하는 순간이 왔다.

이때 비로소 폴더 구조의 중요성을 깨닫게 되었다.

 

분석을 해야 하는데 코드가 너무 여러 곳으로 흩어져 있어서 힘들었다.

특정 기능을 이해하려면 api 폴더, components 폴더, pages 폴더, utils 폴더 등을 모두 뒤지면서 관련 코드 조각들을 찾아 맞춰야 했다.

가장 기본적인 폴더 구조이고, 내가 항상 쓰던 구조와 비슷했는데도 그랬다.

이런 경험을 통해 '어떤 폴더 구조가 좋지?'라는 생각이 처음으로 들게 되었다.

그러다 이번 스터디 주제가 '폴더 구조'가 되면서 본격적으로 찾아보고 고민하게 되었다.

이 글은 그런 고민의 과정을 담고 있다.

 


 

1. 폴더 구조란?

  • 폴더 구조는 말 그대로 프로젝트의 폴더, 파일들을 어떻게 정렬할 것인지 정하는 것을 의미한다.
  • 단순히 파일을 어디에 둘 것인가의 문제가 아니라, 코드의 구성과 관계를 어떻게 표현할 것인가에 관한 문제를 의미한다.

 

 

1) 기본 폴더 구조 

my-project/
  ├── src/
  │   ├── api/          # API 호출 관련 코드
  │   ├── components/   # 재사용 가능한 컴포넌트
  │   ├── pages/        # 페이지 컴포넌트
  │   ├── utils/        # 유틸리티 함수
  │   ├── schemas/      # 데이터 스키마
  │   ├── hooks/        # 커스텀 훅
  │   ├── contexts/     # React Context
  │   ├── App.js
  │   └── index.js

 

사실 스터디를 준비하면서 내 예전 폴더구조도 보고, 현재 맡고 있는 프로젝트의 폴더구조도 봐봤지만 대체로 비슷했다.  

아마 이 글을 보고 있는 사람들에게도 익숙한 구조일 것이다. 

리액트 프로그램을 만들 때 기본적으로 쓰게 되는 구조이다.

 

이러한 폴더구조의 단점은 하나의 기능을 수정하려 할 때, 너무 많은 곳의 참조를 찾아봐야 한다는 것이다.

예를 들어 '사용자 프로필' 기능을 수정하려면,

  • api/user.js에서 API 호출 함수 찾기
  • components/Profile/에서 관련 컴포넌트 찾기
  • pages/ProfilePage.js에서 페이지 컴포넌트 찾기
  • hooks/useProfile.js에서 관련 로직 찾기
  • contexts/UserContext.js에서 상태 관리 로직 찾기

위처럼 해당 기능이 정확히 어디에 어떻게 구현되어 있는지 모르고,

그 기능의 참조도 각각 다르기 때문에 계속해서 파일들을 타고 들어가야 한다.

특히 코드베이스에 익숙하지 않은 새로운 개발자에게는 이런 구조가 큰 장벽이 될 수 있다.

 

 

2) 기능 중심 구조(Feature-based)

그렇다면 기능별로 파일을 모아두면 어떨까? 함께 변경되는 코드들을 함께 위치시키는 것이다.

기능 중심 구조는 특정 기능에 관련된 모든 요소(컴포넌트, 훅, API 호출, 상태 관리 등)를 하나의 디렉터리에 모아두는 것을 말한다.

my-project/
  ├── src/
  │   ├── features/
  │   │   ├── user-profile/
  │   │   │   ├── components/         # 프로필 관련 컴포넌트
  │   │   │   │   ├── ProfileHeader.jsx
  │   │   │   │   ├── ProfileSettings.jsx
  │   │   │   │   └── UserAvatar.jsx
  │   │   │   ├── hooks/              # 프로필 관련 훅
  │   │   │   │   ├── useProfileData.js
  │   │   │   │   └── useProfileUpdate.js
  │   │   │   ├── api.js              # 프로필 API 호출
  │   │   │   ├── types.ts            # 타입 정의 (TypeScript)
  │   │   │   ├── utils.js            # 프로필 유틸리티 함수
  │   │   │   └── index.js            # 외부로 노출할 항목들
  │   │   │
  │   │   ├── authentication/
  │   │   │   ├── components/         # 인증 관련 컴포넌트
  │   │   │   │   ├── LoginForm.jsx
  │   │   │   │   ├── SignupForm.jsx
  │   │   │   │   └── ResetPassword.jsx
  │   │   │   ├── hooks/              # 인증 관련 훅
  │   │   │   │   ├── useAuth.js
  │   │   │   │   └── useLoginStatus.js
  │   │   │   ├── api.js              # 인증 API 호출
  │   │   │   ├── constants.js        # 인증 관련 상수
  │   │   │   └── index.js            # 외부로 노출할 항목들
  │   │
  │   ├── common/                     # 공통 요소들
  │   │   ├── components/             # 여러 기능에서 사용하는 공통 컴포넌트
  │   │   │   ├── Button/
  │   │   │   ├── Modal/
  │   │   │   └── FormElements/
  │   │   ├── hooks/                  # 공통 훅
  │   │   │   ├── useLocalStorage.js
  │   │   │   └── useWindowSize.js
  │   │   └── utils/                  # 공통 유틸리티 함수
  │   │       ├── formatters.js
  │   │       └── validators.js
  │   │
  │   ├── App.js                      # 앱 진입점
  │   └── index.js

 

위 예시처럼 features에 각 기능을 별로 폴더를 만들고, 해당 폴더 안에 필요한 모든 파일들을 넣어준다.

이렇게 구성하면, 특정 기능을 수정해야 할 때 하나의 폴더만 살펴보면 되기 때문에 코드 이해와 수정이 훨씬 편해진다.

또한 응집성이 높아지고, 결합도가 낮아지기 때문에 유지보수하기에도 좋아진다.

 

여러 기능에서 공통으로 사용하는 컴포넌트나 유틸리티는 common or shared 폴더를 만들어 공통 코드를 관리한다.

 

딱 기능단위로 나누기 때문에 해당 방식이 편할 것 같다고 생각했는데,

또 페이지가 여러 개인 프로젝트인 경우 한눈에 프로젝트를 파악하긴 힘들 것 같다는 생각이 들었다..=

보통 작업하는 프로젝트가 페이지가 많은 경우가 많아서 다른 방법이 더 잘 맞을 것 같았다.

 

 

3) FSD (Feature-Sliced Design)

FSD는 기능 중심 구조를 더 체계적으로 발전시킨 아키텍처 방법론을 말한다.

각 기능을 계층과 슬라이스로 구분하여 정리하는 것이 특징이다.

my-app/
  ├── src/
  │   ├── app/              # 앱 전체 설정
  │   │   ├── providers/    # 앱 수준 프로바이더
  │   │   ├── styles/       # 전역 스타일
  │   │   └── index.js      # 앱 진입점
  │   ├── processes/        # 비즈니스 프로세스
  │   │   ├── payment/      # 결제 프로세스
  │   │   └── authentication/ # 인증 프로세스
  │   ├── pages/            # 페이지 컴포넌트
  │   │   ├── HomePage/
  │   │   ├── ProfilePage/
  │   │   └── CartPage/
  │   ├── widgets/          # 복잡한 UI 블록
  │   │   ├── Header/
  │   │   ├── Sidebar/
  │   │   └── ProductList/
  │   ├── features/         # 사용자 시나리오
  │   │   ├── AddToCart/
  │   │   ├── FilterProducts/
  │   │   └── UserAuth/
  │   ├── entities/         # 비즈니스 엔티티
  │   │   ├── Product/
  │   │   ├── User/
  │   │   └── Order/
  │   └── shared/           # 공유 로직
  │       ├── api/          # API 클라이언트
  │       ├── ui/           # UI 키트
  │       └── lib/          # 유틸리티

 

의존성 방향이 명확하고, 확장성이 뛰어나다는 장점이 있다.

그러나 실제로 공부해 본 결과 배우는데 너무 오랜 시간이 걸린다..

또 나누는 지점이 명확하지 않아서 팀으로 프로젝트를 진행했을 때, 미리 기준을 명확히 정하지 않는다면 헷갈릴 가능성이 높다.

그래서 해당 방식은 나에게 맞지 않는 것 같다고 느꼈다.

 

여기까지 오면 나에게 맞는 폴더 구조란 없는 것처럼 느껴진다..

그럴 수밖에 없는 게 모든 폴더 구조는 각각의 장단점이 있으니까ㅠ

 

 

4) Atomic Design

해당 구조는 디자인 시스템에서 영감 받은 구조로, UI 컴포넌트를 복잡도에 따라 5단계로 나눈다.

이번에 스터디 진행하면서 처음 알게 된 구조이다.

my-app/
  ├── src/
  │   ├── components/
  │   │   ├── atoms/         # 가장 기본적인 UI 요소
  │   │   │   ├── Button/
  │   │   │   ├── Input/
  │   │   │   └── Text/
  │   │   ├── molecules/     # 여러 atoms로 구성된 요소
  │   │   │   ├── SearchBar/ # (Input + Button)
  │   │   │   └── FormField/ # (Label + Input + Error)
  │   │   ├── organisms/     # 여러 molecules로 구성된 복잡한 컴포넌트
  │   │   │   ├── Header/
  │   │   │   ├── Footer/
  │   │   │   └── ProductCard/
  │   │   ├── templates/     # 페이지 레이아웃 구조
  │   │   │   ├── MainLayout/
  │   │   │   └── AuthLayout/
  │   │   └── pages/         # 실제 페이지 컴포넌트
  │   │       ├── HomePage/
  │   │       └── LoginPage/
  │   ├── App.js
  │   └── index.js

 

 

아래 5단계로 나눠진다.

  • Atoms : 버튼, 입력 필드, 텍스트 등 더 이상 분해할 수 없는 기본 요소
  • Molecules : 여러 atoms를 결합한 간단한 UI 컴포넌트
  • Organisms : 여러 molecules와 atoms로 구성된 복잡한 UI 액션
  • Templates : 페이 구조를 정의하는 레이아웃
  • Pages : 실제 콘텐츠가 있는 페이지 컴포넌트

api와 hook에 관해서는 명확한 가이드라인이 없지만, 일반적으로는 Atomic Design 컴포넌트 구조와 별도로 관리한다.

개인적인 생각으로는 위 구조도 기본 구조와 마찬가지로 기능이 나뉘어 있어 수정할 때 헷갈릴 가능성이 높은 거 같다..

 

 

 

5) Monorepo

여러 패키지나 애플리케이션(프로젝트)을 하나의 저장소에서 관리하는 방식을 말한다.

이건 지금 내가 사용하기엔 적절하지 않지만, 새로 들어본 폴더 구조라서 정리했다.

monorepo/
  ├── packages/                  # 공유 패키지
  │   ├── ui/                    # UI 컴포넌트 라이브러리
  │   │   ├── src/
  │   │   └── package.json
  │   ├── utils/                 # 유틸리티 라이브러리
  │   │   ├── src/
  │   │   └── package.json
  │   └── api/                   # API 클라이언트
  │       ├── src/
  │       └── package.json
  ├── apps/                      # 애플리케이션
  │   ├── web/                   # 웹 앱
  │   │   ├── src/
  │   │   └── package.json
  │   └── admin/                 # 관리자 앱
  │       ├── src/
  │       └── package.json
  ├── package.json               # 루트 패키지 정보
  ├── lerna.json                 # Lerna 설정 (또는 nx.json, turbo.json)
  └── README.md

 

여러 프로젝트 간에 코드를 쉽게 공유할 수 있고, 재사용할 수 있다. 

큰 프로젝트를 여러 명이서 동시에 관리해야 하는 상황이라면 도움이 될 것 같다는 생각이 들었다.

 

하지만, 지금의 나는 혼자서 ~ 최대 2명이서 프로젝트를 진행 중이라

모노레포를 사용할 정도는 아니라는 판단이 들었다.

 

 

2. 결론

 

그래서 결론이 뭐냐면..

"절대적으로 옳은 폴더 구조는 없다는 것"이다.

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

폴더 구조는 각 프로젝트의 성격, 팀의 크기, 개발 문화에 따라 최적의 구조가 달라지는 게 맞는 것 같다.

그리고 현재 내 상황에서 이것저것 써보면서 가장 맞는 폴더 구조를 찾는 게 좋은 것 같다.

꼭 하나의 폴더 구조 방법을 고집하는 게 아니라 이것저것 섞는 것도 나쁘지 않은듯..

다만 팀원들과 명확하게 합의를 하고 일관된 구조를 유지하는 것이 중요하다.

 

그리고 나는 페이지가 있는 웹앱을 주로 개발하기 때문에 하이브리드 폴더 구조가 적절한 것 같다는 생각이 들었다.

page + 기능별로 나누면 좋을 것 같은데 좀 더 고민을 해보고 다음 프로젝트에 적용해 봐야겠다!!

 

 

스터디 진행하면서 진짜 여러 경험을 하게 되는 것 같다.

다음 스터디 주제는 "토이 프로젝트 진행"인데, 내가 스피커 역할이라 떨리고 기대된다!!

이번에도 열심히 해봐야지..!

 

728x90