프론트엔드 개발자의 폴더구조.. 개발을 처음 시작했을때 강사분이 추천하는 Container / Presenter 패턴을 스터디하고 첫 프로젝트를 시작했는데, 하나의 패턴만 알고 있어서 불편함을 느끼지 못하고 이것이 가장 깔끔한 패턴인 줄 알았다. 그렇게 두번째 프로젝트에 나보다 경험이 더 있는 친구가 프론트 리드를 맡고 그 친구의 스타일을 따라가다 보니 전의 폴더 구조 패턴이 본인에게 맞지 않고, 비효율적이라고 느꼈다. 그 계기로 계속해서 시야를 넓히려고 노력중이고, 현재는 사수 개발자가 없으니 많은 글들과 코드를 보려고 하고 있는 것 같다.
현재 진행중인 프로젝트가 릴리즈 후 회고를 통해 문제점을 인식했고, 개선하려는 과정때문에 한동안 프론트엔드 개발이 중단되었는데, 혼자 관리하는 코드이고, 코드를 최대한 나중에 수정없이 해야겠다! 하면서 구성했는데, 지금 다시보니 개선이 많이 필요할 것 같다는 생각이 들었다. 😥😥😥
그 중 하나가 바로 폴더구조이다. 나름 체계적으로 타입, api, hook, 컴포넌트 등을 구분하려 구조를 구성했는데, 릴리즈 기간에 맞춰 급하게 코딩할때 고민없이 아무 파일위치에다가 만들었던 것들이 지저분해보였다. 고민이 없었던 것도 맞지만, 폴더구조 방향성을 처음부터 구체적으로 정의해놓지 않았기 때문에 발생한 문제라고 생각했다.
기존 구조의 문제
기존에는 관심사를 분리하여 폴더구조를 구성했습니다. 문제가 없어 보일수도 있지만, velog에서 이를 정말 재밌게 풀어낸 분의 글을 보니 생각이 바뀌었습니다.
프론트엔드 개발자 관점으로 바라보는 관심사의 분리와 좋은 폴더 구조 (feat. FSD)
최근 프론트엔드 개발에서 주목받는 FSD 아키텍쳐 폴더 구조를 주제로 소프트웨어 공학 관점에서의 관심사의 분리라는 원칙을 통해 설명하고자 했습니다. 이 글은 그동안 프론트엔드가 복잡성
velog.io
"역할만으로 구분하던 구조를 조금더 다양한 관심사를 만족하는 구조로 만들어보자 "
FSD 아키텍처
Feature-Sliced Design(FSD)는 프론트엔드 애플리케이션의 구조를 잡는 아키텍처 방법론입니다. 기존에는 역할에 따라서 파일과 폴더FSD 아키텍처는 이름 말 그대로 기능(Feature)을 기준으로 코드를 분리하는 방식입니다. 이는 프로젝트 크기가 커짐에 따라 훨씬 더 유지보수성과 확장성 측면에서 향상시키는데 도움을 줍니다.
FSD 아키텍처는 소프트웨어 공학의 원칙에 근거하여 설계되었습니다.
1) 단일 책임의 원칙(Single Responsibility Principle, SRP)
: 관심사 분리, 하나의 모듈이나 클래스는 오직 하나의 역할만을 수행해야 하며, 그 역할에 대해서만 책임을 져야 합니다.
2) OCP 원칙(Open/Closed Principle)
: 확장에는 열려 있고, 변경에는 닫혀 있어야 한다는 개념.
3) 응집도, 결합도
: 응집도는 높게, 결합도는 낮게, 즉 같은 역할이라면 가까이 두기.
레이어
레이어는 FSD 아키텍처의 표준화입니다. 모든 레이어를 사용할 필요는 없고, 이름이 특히 중요합니다.
1. App 레이어 ( src/app/ )
|
슬라이스
슬라이스는 비즈니스 도메인별로 코드를 분할합니다. 이는 같은 레이어 안에서 다른 슬라이스를 참조할 수 없으며, 이 규칙은 높은 응집도와 낮은 결합도를 유지하는 데 도움이 됩니다.
예시
- 📂 entities. // 비즈니스 도메인 엔티티 레이어
- 📂 user // User 엔티티 슬라이스
- 📂 model // 데이터 모델 및 상태 관리
- 📂 userSlice.ts // Redux 또는 zustand의 상태 관리
- 📂 slectors.ts // 사용자 관련 상태 선택자 함수
- 📂 api.ts // API 호출 및 사용자 데이터 처리 로직
- 📂 ui // UI 컴포넌트
- 📂 UserProfile.tsx. // 사용자 프로필 컴포넌트
- 📂 UserList.tsx. // 사용자 리스트 컴포넌트
- 📂 lib
- userUtils.ts // 사용자 관련 데이터 변환 및 헬퍼 함수
- 📂 model // 데이터 모델 및 상태 관리
- 📂 user // User 엔티티 슬라이스
- 📂 features. // 특정 기능을 담당하는 레이어
- 📂 user-auth // 사용자 인증 기능 슬라이스
- 📂 model // 상태 관리 및 비즈니스 로직
- 📂 authSlice.ts // 사용자 인증 기능 슬라이스
- 📂 ui // 인증 관련 UI 컴포넌트
- 📂 LoginForm.tsx. // 로그인 폼 컴포넌트
- 📂 LogoutButton.tsx. // 로그아웃 버튼 컴포넌트
- 📂 model // 상태 관리 및 비즈니스 로직
- 📂 user-auth // 사용자 인증 기능 슬라이스
각 슬라이스는 하나의 기능 또는 도메인과 관련된 코드를 한 곳에 모아두기 때문에, 관련된 코드를 쉽게 찾을 수 있고, 기능이 추가되거나 변경될 때 해당 슬라이스만 수정하면 되서 코드 변경이 다른 부분에 미치는 영향을 최소화할 수 있습니다.
세그먼트
FSD 아키텍처의 구성 요소 중 하나로, 슬라이스(Slice)보다 큰 범위의 분리 단위를 의미. 여러 슬라이스를 하나로 묶어 관리하는 역할을 합니다. 즉 세그먼트는 슬라이스를 그룹화하여 더 큰 기능적 모듈을 구성하고, 프로젝트의 전체적인 구조와 레이어 간 관계를 명확하게 정의하는 데 사용됩니다.
FSD가 풀고자 했던 문제는 결합도를 낮추고 응집력을 높이는 것입니다. 소프트웨어공학에서 이러한 접근을 하는데 FSD 또한 이러한 방식을 착안해서 문제를 해결하고자 하는 것입니다.
shared는 무엇인가
Shared는 FSD 아키텍처에서 사용하는 공통적인 레이어 중 하나이고, 여러 세그먼트와 슬라이스에서 재사용 가능한 공통 리소스 및 모듈을 관리하는 공간입니다. 예를 들면 공통적인 컴포넌트(버튼, 인풋 등), 스타일, 유틸리티 함수 등이 있습니다. 아래는 shared 레이어의 예시 구조입니다.
src/
└── shared/ // Shared 레이어의 루트
├── components/ // 공통 UI 컴포넌트
│ ├── Button.tsx // 공통 버튼 컴포넌트
│ ├── Modal.tsx // 모달 창 컴포넌트
│ └── Input.tsx // 공통 입력 컴포넌트
│
├── styles/ // 글로벌 및 공통 스타일 파일
│ ├── theme.ts // 전역 테마 설정 (컬러, 폰트 등)
│ └── globals.css // 글로벌 스타일 시트
│
├── lib/ // 공통 유틸리티 함수 및 헬퍼 함수
│ ├── formatDate.ts // 날짜 포맷팅 함수
│ ├── mathHelpers.ts // 수학 계산 관련 헬퍼 함수
│ └── api.ts // 공통 API 호출 함수
│
├── hooks/ // 재사용 가능한 React Hooks
│ ├── useWindowSize.ts // 윈도우 크기 감지 Hook
│ └── useDebounce.ts // 디바운스 기능을 제공하는 Hook
│
├── constants/ // 프로젝트 전역에서 사용되는 상수 값
│ ├── apiUrls.ts // API URL 목록
│ └── roles.ts // 사용자 권한 관련 상수 값
│
└── assets/ // 공통적으로 사용하는 정적 자산 (이미지, 아이콘, 폰트 등)
├── logo.svg // 프로젝트 로고
├── icons/ // 아이콘 모음
└── fonts/ // 폰트 파일 모음
마무리
프로젝트와 경험이 쌓이면서 처음에는 UI 중심에서의 흥미가 점차 생겨났다가 이제는 비즈니스 로직, 클린코드, 개념 정립 등등에 대한 주제로 관심사가 퍼져 나가면서 본인만의 폴더구조에 대해서 관심이 생겨서 이것저것 다른 개발자분들의 생각도 읽고, 정리할 수 있게 된 계기가 된 것 같다. 여기서 중요한 것은 어떤 폴더구조가 가장 좋냐에 대한 정답은 없는 것 같다. 스타일 라이브러리에도 취향차이가 있듯이 본인에게 어울리고 잘 활용할 수 있는 방식을 택하는 것이 맞다고 생각한다. 다만 그렇다고 해서 한가지가 맞다고 생각하고 그거에만 몰두하지말고 그릇이 크기를 넓히고 여러 시도들을 통해서 상황에 맞는 최적의 구조를 결정할 수 있는 능력을 기르는 것이 중요하다고 생각한다.
'프론트엔드 기록 > 리액트' 카테고리의 다른 글
빈 화면 없이 자연스럽게 데이터 갱신하여 SSR UX 개선하기 (0) | 2025.03.03 |
---|---|
리액트 Flux 패턴이란?( vs MVC 모델 ) (2) | 2024.10.29 |
리액트의 동작 원리, Virtual DOM (1) | 2024.09.12 |
NextJS 경로 가로채기 라우팅 (0) | 2024.09.02 |
NextJS 서버 클라이언트 컴포넌트의 대한 고찰 및 전략 (0) | 2024.08.25 |