최근 회사에서 실제로 적용하고 있는 폴더구조에 대해서 이야기를 해보고자 한다 ㅎㅎ.
FSD폴더구조와 유사한점이 많지만 자유도 측면에서 장점이 많은 프랙탈 폴더구조를 우리팀에선 채택하고 있다.
사실 프랙탈 폴더구조란 말은 없긴하다. FSD에서 영감을 받아서 커스터마이징한 폴더구조인데, 구조가 프랙탈처럼 같은 형태가 계속 반복되는 폴더구조라 네이밍을 붙이게 되었다.
좋은 폴더구조 ?
좋은 폴더구조는 뭘까?
좋은 폴더구조는 아래 항목중에 어떤게 잘 되어야 할까?
1. 내가 봐야할 부분을 바로 찾을 수 있는 구조
2. 공통화된 파일들을 추적하기 좋은 구조
3. 파일의 영향범위를 파악하기 쉬운구조
등등이 있을 것이다. 무엇보다 1번이 가장 중요한 것 같다라고 최근 생각한다. 기능을 추가하던, 고치던 내가 해당 레포의 어느 파일을 건드려야 할지 안다는 것은 매우 중요하다.
폴더엔 뭐가 들어가지?
그렇다면 FE개발을 할 때 우리가 정리해야할 파일들은 어떤것이 있는지 파악하는것도 중요하다. 일단 생각나는 것만 정리해보면,
(리액트 개발 기준)
- 컴포넌트 파일
- 레이아웃을 담당하는 페이지 파일
- 스타일
- 타입
- 상수
- 설정
- hook
- api연결 등 비즈니스 로직
- 유틸
- 등등...
생각나는대로만 적었는데도 정말 많다. 그렇다고 이 기준을 근거로 폴더구조를 잡으면 망하게 된다.
쉽게 생각해서 다이소를 생각해보자.
각 물건들의 재질을 나무, 플라스틱, 스티로품, 알루미늄 등등으로 나누어서 그 재료를 기준으로 물건을 진열한 것과 같다.
다이소..?
다이소? 그러면 다이소는 보통 어떻게 물건을 진열하는지에 대해 생각해볼 필요가 있다. 다이소는 우리가 어떤 물건을 찾기에 최적화된 진열배치를 가지고 있다.
다이소는 물건의 재질이나 특징으로 물건들을 모아두지 않는다.
예를 들어 의자를 생각해보자. 다이소는 의자를 한곳에 모아두지 않는다. 욕실용 의자는 욕실코너, 캠핑용 의자는 캠핑코너에 진열한다.
언뜻 의자를 구매하는 사람한테는 불편해 보일수도 있지만, 실제 구매자는 의자를 사는 경우 사용처가 확실하기 때문에 물건을 찾는데 헤매지 안흔ㄴ다.
다이소는 물건의 재질이 아닌, "용도"에 맞춰서 진열을 한다. 욕실용품, 주방용품, 캠핑 등등의 사용처를 기준으로 둔다.
조금 큰 규모의 다이소가 있어서 캠핑용품 코너가 매우크다고 생각해보자.
이 경우 캠핑용품 안에서도 용도를 나눌 수 있다.
삼겹살 굽기와 관련된 코너 (숯, 쌈장, 고기판 등), 수면을 위한 코너 (침낭, 담요 등)
이 세부 용도를 나눌 수 있고 사실 이 안에서도 더 나눌수도 있다.
FSD

FSD 파일구조는 위 다이소에서 소개한 진열방식을 FE 폴더구조에 잘 녹인 방식이라고 생각한다.
7개의 "용도"를 분리하고, 그 용도에 따라서 파일들을 차곡차곡 정리한다. 그 안에서 또다시 용도를 분리할만하게 큰 범위가 되면, 7개의 용도를 또다시 분리할 수 있다.
즉 마치 수학의 프랙탈처럼 같은 폴더이름이 계속 반복되게 된다.
이 경우의 폴더구조가 가지는 가장 강력한 장점은 "파일의 영향범위를 항상 알 수 있다"란 점이다.
해당 파일구조에서 특정 파일이 있다면 그 파일은 자신의 형제와 자식 파일에만 영향을 끼친다. 이는 프로젝트의 유지보수에서 매우 중요하다.
프랙탈 구조?
FSD의 아이디어는 정말 좋다고 생각한다. 근데 필자는 개인적으로 조금 치명적인 단점이 존재한다고 생각한다.
1. 폴더구조에 대해 공부해야함
2. 가끔 어떤 폴더에 넣어야할지 헷갈림
1번에서 말하는 공부는, 다이소를 예시로한 계층형식의 파일구조 개념이 아닌, 말 그대로 폴더이름자체에 공부를 해야한다는 점이다.
어떤 상황에 pages, ui, features 등을 넣어야 하는지 공부하고 해석하는 과정이 필요하다.
또, 프로젝트에 따라서는 7개의 폴더가 모두 필요 없을 수도 있다.

그래서 우리팀에서는 해당 구조의 장점을 골라낸 파일구조를 채택하고 있다.
먼저, 7개의 용도가 아닌 프로젝트에 필요하다고 생각하는 용도에 맞게 이를 분리한다.

- assets : 정적 리소스
- pages : 페이지 단위 컴포넌트
- components : 컴포넌트
- hooks : 커스텀 훅
- services : 서버 통신, 비즈니스 로직 처리
- styled : 스타일
- utils : 유틸 함수
- types : 타입 정의
만약에 작업이 필요한 경우, 우리팀에서는 pages를 기준으로 이를 잡았다. (필요시엔 도메인을 기준으로 해도 좋을 듯 하다)
만약 user-info 페이지를 만든다면, 그 페이지를 만드는 UserInfo.tsx 파일을 만든다. (index.tsx로 해도 무방 - 팀컨벤션에 따를 것)
그리고 이 UserInfo.tsx에만 사용되는 components들은 아래처럼 따로 분리한다.

또한, 해당 페이지를 만들기위해 사용되는 style, type, utils, hook 등도 필요하면 분리한다.
(style, type 등의 파일이 커지기 전에는 filename.style.tsx 이런식의 네이밍을 가지고 있다)

자, 이제 만약에 UserInfo.tsx에서 사용하는 UserName컴포넌트가 있는데, 이 컴포넌트가 매우 복잡해서 분리가 필요한 상황이라고 생각해보자. 그러면 아래와 같이 구분할 수 있다.
components안의 UserName.tsx임에도 그안에 components 폴더명이 반복된다. 프랙탈 구조가 나오게 되는 것이다
여기서 우리는 UserFirstName.tsx파일이 user-name폴더 안에서만 영향력이 있다는것을 명확히 알 수 있다.

그렇다면 공용 컴포넌트는 어떻게 관리할 수 있을까?
중요한건 이 폴더구조의 원리만 알고있으면 된다. 바로 최상위에 components를 만들면 된다.
폴더의 위치가 곧바로 그 폴더의 영향력과 1:1 매핑이 되므로 최상위의 components 폴더는 당연히 공용 shared의 역할을 할 수 있다.
해당 components는 특정 도메인이 아닌, 공용에서관리되는 얘들만 존재하게 된다. 만약 특정 도메인이나 페이지에서 사용되는 공용 컴포넌트라면, 그 안에 분리해야 한다.

당연히 공용 컴포넌트도 비대해지면 분리할 수 있다.

자! 이제 마지막으로 이 구조를 유지한 복잡한 폴더 구조를 봐보자. (GPT가 일을 참 잘하는 것 같다)

만약 당신이 이 대시보드 프로젝트의 관리자고, header의 api호출에 문제가 생겼다고 생각하면, 어느 파일을 보면 될까?
header.service.ts 파일을 보면된다. 만약 header위치에 service폴더가 없다면, 그 상위폴더로 올라가면서 탐색하면 된다.
또한 영향범위를 알기 때문에 개발자는 쉽게 header.service.ts 파일을 수정할 수 있을 것이다.
상위의 파일일수록, 개발자는 사이드 이팩트를 생각하면서 수정하게 되니 잠재적인 버그도 막을 수 있다.
정리
물론 이 폴더구조가 FE폴더구조의 정답이라고 생각하지 않는다. 애초에 정답이 없는 문제기도 하고, 모든 프로젝트마다 그에 어울리는 폴더구조는 다르다고 생각한다.
이 구조도 단점이 있다 ㅎㅎ. 만약에 "파일의 영향력"이라는 룰을 위반하는 파일들이 나타나면, 오히려 추적하는게 어려울 수도 있고 많은 depth에 의해 혼란이 가중될 수 있다. 꼼꼼한 관리가 필요한 폴더구조이다.
그럼에도 지금까지는 장점이 많은 구조라 생각한다.
1. 자유도 : 컨벤션, 폴더의 분류 등을 각 프로젝트 상황에 맞게 얼마든지 커스터마이징할 수 있다.
2. 영향력 : 파일의 영향도를 알기 때문에 사이드이팩트를 미리 예측할 수 있다.
3. 접근성 : 개발자가 어떤 기능을 구현/수정하는 경우 어디를 확인해야할 지 바로 파악이 가능하다.
4. 위치 고민 : 해당 파일을 언제 분리할지, 어디에 위치시킬지에 대한 고민을 줄여줄 수 있다.
프랙탈 폴더구조보다 다이소 폴더구조가 더 친근한거 같기도 하다 ㅎㅎ
'FrontEnd' 카테고리의 다른 글
| AI에 MCP로 날개달기 (Cursor + MCP로 깃,노션 등 연동하기) (7) | 2025.04.13 |
|---|---|
| [FrontEnd] MFA(Micro Frontend Architecture) 란?! (0) | 2025.03.08 |
| [ Front ] Zustand 상태관리 라이브러리 (1) | 2024.06.10 |
| [React] react에서 next처럼 라우팅하기 (0) | 2024.05.28 |