[리액트] 네이버는 폴더구조를 어떻게 구성 하고있을까

Date:     Updated:

카테고리:

태그:


🚀Introduction

많은 개발자들의 고민인것 같다. 네이버는 어떻게 하고있을까? 지금 이게 best-practice일까? 이게 정말 최선일까? 다른 고수들은 어떻게 하고있을까?

필자는 네이버에 다니고있지도 않고, 세계최고의 천재개발자들로 구성된 집단에 속한적이 있지는 않아서, 정답을 알수는 없다. 애초에 정답이라는게 있을까? 의문이 든다. 그래도 꽤 괜찮은 방법정도를 찾고 집착하지 않는것도 프로그래밍의 근본적인 부분과 연결되어 있다고 생각한다.

충분히 괜찮은 방법을 빠르게 찾고, 남은 시간을 소중한 사람들과 보내는것 이 좋지 않을까? 라는 생각이 스쳐지나가지만, 글의 주제와 상당히 벗어나므로, 괜찮은 리액트 폴더구조에 대해 생각해본 과정과 결과를 공유해보고자 한다.


⭐️LV.0 가장 간단한 구조

beginner

리액트 프로젝트를 생성하고, 별다른 조정이 없는 상태이다. 이제 막 시작한 프로젝트, 혹은 프론트엔드 개발자가 되기 위해 공부를 해보고 있는 상태의 폴더구조일 것이다.

1회성 프로젝트, 간단한 백오피스, 팀원들을 위한 telegram 알람봇 페이지, 오늘 학식을 보여주는 웹페이지 등 1~2페이지 분량의 간단한 프로그램의 경우가 아니라면 아마 이런 구조의 프로젝트를 가지고 있는 서비스는 거의 없을 것 같다.

아마 이 글을 읽기위해 시간을 내시는 분들의 경우 해당사항이 없지 않을까 싶다.


⭐️LV.1 페이지-레이아웃 구조

intermediate

대 황금 Next.js가 도래함에 따라 리액트에서도 위와같은 구조를 많이 채택하게 되는 것 같다. page단위로 앱을 만들고, 재활용되는 부분은 layout을 이용한다.

auth 관련된 페이지 외에도, 해당 서비스가 보여줄수 있는 핵심 페이지들이 있을것이고, 어느정도 디자인을 고려해서 만들어진 상태일 것이다. 아직은 그렇게 복잡한 프로그램은 아닌 상태일 것이며, 그렇게 문제가 있어보이지도 않는다.


⭐️LV.2 복잡해진 구조

intermediate2

아마 대부분의 프로젝트들이 여기에 속할 것이다. 단순 페이지만으로 쪼개기에는, 각각의 페이지에서 자주 재활용되는 컴포넌트들도 있고, 통일성있는 디자인을 위해 디자인 시스템을 갖추고(버튼 ui등), service 레이어도 깊어진 상태일 것이다.

테스트코드들도 조금 더 튼튼해졌으며, 유틸, 폰트, 컬러, 등도 한번 정리를 해둔 상태인 것이다. 이러한 구조에서 가지고 있는 문제점이 있다.

새로운 기능을 만들 때, 너무 많은 곳을 참조해야 한다는 것이다. 공용 ui 컴포넌트 Card.tsx를 만든다고 가정해보자.

card-ui

Card.tsx 옆에, 이 폴더 외에는 절대 호출될 일이 없는, Card와 관련성이 높아보이는 코드끼리 모아두면 좋지 않을까? 카드를 수정하다가, test코드도 한번 손봐주거나, 스토리북을 한번 더 확인해보게 될 가능성이 높아지게 될 것 같다. UI에 관해서 이정도 코드 모아주기(?)는 어느정도 이루어지고 있는것으로 알고 있다.

todo-entity

진짜 문제는, 하나의 entity를 나타내는 관련 코드들이 너무 광범위하게 흩어져있다는 것이다.

TODO라는 기능 하나만을 생각해도 너무 많은 폴더에 흩어져 있다. 예를들어

“본인의 todo를 적고, 남들과 공유하고, 타이머를 설정하고, 벌금을 설정하고, 결제를 할수 있으며, 스티커를 붙일 수 있는 슈퍼todo 앱”을 만든다고 생각해보자. 하나의 기능이 저렇게 광범위하게 있으면 어떻게 될까?

가독성도 떨어지고, 실수할 가능성도 너무 높아진다. 관심사 분리 관점에서 별로 좋지 않다는 생각이 먼저 들게 되는 것 같다.


⭐️LV.3 기능별로 분리된 구조

어떻게 해결하면 좋을까? feature별로 폴더를 나눠주면 된다. 대략 이런식이다.

toFeature

이제는 todo라는 기능(feature)은 locally하게 사용되는 자신만의 hook, type, test, component 를 가지고 있으며, 마치 하나의 프로젝트처럼 구조를 가지고 있다. 이제 마치 다른 프로젝트를 하고있는 양, payment기능(결제), 타이머기능 개발자는 더이상 todo와 관련된 코드를 만날일도 없다.

하지만 모든 기능이 거대하지는 않다. 간단한 기능도 있다.

paymentNtimer

todo에 비하면 payment기능은 덜복잡할수도 있다. payment기능에 비하면 timer는 매우 간단한 기능일수도 있다. 그렇다면 폴더구조도 다를수밖에 없다. 파일 몇개로 끝낼 수 있는 기능을 굳이 폴더로 쪼개야 할까? (개인취향이지만)강박이 있는게 아니라면 해당 기능 담당자가 적절하게 배치하면 될 것 같다. 이처럼 크고작은 기능들을 적절하게 쪼개두고, page에서 해당 기능을 호출해서 사용하면 된다.

다양한 곳에서 사용될 수 있는 유틸함수는 최상단으로, 해당 기능에서만 locally하게 사용될법한 기능은 feature하단으로, 애매모호한 기능들은 같이 일하는 분들과 상의해서 적절한 곳에 배치하면 될 것 같다. 애매모호에 대한 판단이 또 다를수도 있다. 가독성을 해치지 않는다면, 동료의 판단을 믿고 나쁘지 않은 위치에 적절하게 비슷한 내용끼리 모아두는것으로 충분하지 않나 싶다.

이것도 하나의 방법론일 뿐이다. 프로젝트 특성에 따라 잘 변형해서 사용하면 좋을 것 같다.

⭐️LV.3-1 FSD(근본)

사실 위의 feature 별로 폴더구조를 나누는 생각은 누구의 머릿속에서나 나올수 있는 개념이다. 하지만 해당 개념을 좀더 구체화 시킨 아키텍쳐가 FSD(Feature-Sliced-Design)이다.

feature단위로 구성된 몇가지 예시 프로젝트들을 확인할 수 있다.. 그리고 전체적으로 다음과 같이 엔티티를 기반으로 한 폴더구조를 제안하고 있다.

FSD-archi

entity 별로도 나뉘어져 있고, feature 별로도 나뉘어져 있다. 위 아키텍처는 다음과 같은 특징이 있다.

  1. app은 (되도록)page만 import 한다.
  2. page에서는 UI, features, entities, shared 를 import 할 수 있다.
  3. UI 에서는, features, entities, shared를 import 할 수 있다.
  4. features에서는 entitiesshared를 import할 수 있다.
  5. entitiesshared만 import 할 수 있다.

그렇다. 상위계층구조에서는 하위 구조를 import할 수 있지만, 하위구조에서는 상위구조의 내용들을 import 할 수 없다. 정의된 entity와 여기저기서 사용될 수 있는 shared를 이용하여 feature를 구성하고, feature들과 entity, shared 들을 조합해서 어떤 기능이 있으며, 엔티티를 표현해줄 수 있는 UI를 짤 수 있는 것이다.

처음에는, 굳이 이렇게 복잡하게 짜야해? 라고 생각했지만, 앱의 크기가 커지면 엔티티와 기능들, 그리고 UI등을 확실히 구분해주는게 커뮤니케이션 및 프로젝트관리 관점에서 편할 것 같다는 생각이 들게 되었다.

관련해서 참고해볼만한 레포는 여기를 봐보면 좋을 것 같다(⭐️67 ↑). 하지만 레포를 보면 5가지 계층밖에 없다. UI계층이 빠져있는것을 볼 수 있다. 또한 FSD 예제로 유명한 여기를 봐보면 UI와 entity가 빠진 4계층으로만 FSD를 볼 수 있다.

그렇다…! 굳이 다 따라할 필요 없이 내마음대로 쓰면 된다…! 폴더구조에도 silver-bullet이 있다고 착각했던 것이다. 그렇다면 우리는 어떻게 적용해 볼 수 있을까?

⭐️LV.3-2 FSD 변형

FSD 구조는 설득력있는 좋은 구조다. 하지만 팀원들 모두의 컨센서스를 맞출수도 없고, 폴더구조를 갈아엎는게 두려울수도 있다. 이럴때는, 아까 나왔던 feature단위로 쪼개는 구조를 부분적용하면 된다. 요즘처럼 프론트엔드 취직(및 이직)이 어려운 시장에서 아마 대부분 Next.js정도는 공부해보고, 또 써봤을 것이다.

결국 팀원들 모두 알고있을법하고, 익숙하며, 대형 프로젝트도 충분히 핸들링 할수있는 page & layout 구조로 가는것도 좋은 선택이라고 생각한다.

꼭 FSD를 지킬필요 있나…!!!!

featureOverview

FSD였으면 여기저기서 쓰이게 될, 최상단의 hooks나 utill 등을 shared로 넣거나, 그 성격에 entity 혹은 feature에 찢어서 넣을수도 있을 것 같다. 하지만 해당 entity, UI들 및 관련코드를 한쪽에 몰아두는 것 만으로 충분할 것 같다.

FSD_next

이정도로 구성해두고, 우리에게 익숙한 page 혹은 layout등에서 import해서 사용해도 웬만큼 큰 프로그램은 감당되지 않을까 싶다.

FSD변형? 무튼 누구나 할 수 있는, feature단위로 폴더를 쪼개둔 리액트 플젝들은 여기저기서 쉽게 찾아볼 수 있다. 그중 star수가 (아마)제일 많은 레포인 여기(bulletproof-react)를 참조해보면 좋을 것 같다.(⭐️25k↑)

⭐️LV.4 모노레포

프로젝트가 더더더 커지면, 모노레포가 도입되기도 한다. 모노레포를 써본적이 없어서, supabase의 구조를 한번 공유해본다.

전체적인 구조에서 프론트엔드는 apps쪽을 보면 좋을 것 같다. design-system레포가 따로 구성되어있고, docsstudio도 따로 레포가 구성되어있는 모습을 볼 수 있다.

mono-repo

이처럼 단순 기능별 폴더로 구분하는것이 아닌, (상위 구조 입장에서 폴더지만)하나의 레포단위로 구성할수 있어 보인다.

⭐️정리

글이 길어졌는데 이것도 결국 가장 일반적인 웹페이지의 형태들을 기준으로 작성된 것이며, 본인의 프로젝트 성격에 따라 자유롭게 하는게 더 좋은선택일 것 같다. page개념이 전혀 없는 웹 에디터(온라인 포토샵 등) 일수도 있고, 복잡한 코인차트를 보여주는 (기능은 많은)2page짜리 앱일수도 있다. 딱히 복잡한 기능은 없지만 page만 엄청 많은 형태일수도 있다. 국내에 몇 없을 크기의 초대형 플젝이라면 모노레포를 적용해 아싸리 다른 방식을 사용할수도 있을 것 같다.

만약 폴더구조 관련해서 아직 고민이라면, software를 만드는 만큼 말랑말랑한 사고를 가지고, 굳이 무엇을 따라할필요 없이, 팀원들과 이리저리 시도해보면 좋지 않을까 싶다.


맨 위로 이동하기

react 카테고리 내 다른 글 보러가기

댓글 남기기