본문 바로가기

2024-07-29 넥스트.JS의 폴더 구조

codeConnection 2024. 7. 29.

Next.js를 배운 지 한 달이 넘어 가는 이 시점에서 Next.js의 폴더 구조를 다시 공부한다는 것이 이상하게 보일 수 있지만,

폴더 구조는 팀 프로젝트를 할 때마다 컨벤션에 약간의 차이가 발생해서 이번 프로젝트에서는 왜 이렇게 폴더구조를 설정하였는지까지 하나 하나 기술적 의사 결정 사유를 놓고 토론하느라 꽤 많은 시간이 흘렀다.

 

지금까지의 팀프로젝트에서 이런 경험은 없었다. 그리고 이런 것을 프로젝트의 아키텍쳐를 구성하는 과정임을 알게 되었다.

 

기본적인 Next.js의 폴더구조와 지금 우리 프로젝트의 현재의 아키텍쳐를 보면서 살펴보도록 하겠다.

 

Next.js로 프로젝트를 생성하면 기본적으로 위처럼 폴더 구조가 생성이 된다.

자바스크립트, 타입스크립트 사용 유무에 따라서 확장자는 달라진다.

 

.
├─ .next ▶️ 넥스트의 빌드 결과물 폴더(.은 숨김 표시)
├─ node_modules ▶️ 프로젝트 관련 JS 라이브러리가 설치된 폴더
├─ public ▶️ 이미지, 폰트와 같은 정적 자원들을 배치하는 곳. baseURL의 상대 경로로 설정
├─ src
   └─ app
      ├─ favicon.ico
      ├─ globals.css
      ├─ layout.css
      ├─ page.js
      └─ page.module.css
├─ .eslintrc.json ▶️ ESLint 설정 파일
├─ .gitignore ▶️ 깃 이그노어 파일
├─ jsconfig.json ▶️ VSCode 설정 파일
├─ next.config.mjs ▶️ 넥스트 설정 파일
├─ package-lock.json ▶️ 라이브러리 의존 관계 설정 파일(개발자가 절대 직접 수정하지 않음)
└─ package.json ▶️ NPM 프로젝트 설정 파일

 

src/app 폴더는 라우팅이 되는 폴더이다. 이곳에 폴더를 만들면 하나의 세그먼트를 갖게 되고 라우팅이 설정되게 된다.

 

우리 프로젝트의 아키텍쳐는 다음과 같다.

 

├─ .eslintrc.js
├─ .gitignore
├─ .vscode
│  ├─ extensions.json
│  └─ settings.json
├─ README.md
├─ backup
│  └─ sw_backup.js
├─ init-https.sh
├─ next.config.mjs
├─ package.json
├─ postcss.config.mjs
├─ public ⭐️ 공용 assets 파일
│  ├─ bundle.js
│  ├─ images
│  │  ├─ 김태리.jpg
│  │  ├─ 박은빈.jpeg
│  │  ├─ 송강호.jpg
│  │  ├─ 이주빈.jpg
│  │  └─ 최민식.png
│  ├─ manifest.json
│  ├─ next.svg
│  ├─ svg
│  │  ├─ Arrow_back.svg
│  │  ├─ Chat.svg
│  │  ├─ Close.svg
│  │  ├─ Home.svg
│  │  ├─ Mypage.svg
│  │  ├─ Notifications_unread.svg
│  │  ├─ Search.svg
│  │  ├─ Settings.svg
│  │  └─ Trip.svg
│  ├─ sw.js
│  ├─ sw
│  │  ├─ sw.js.map
│  │  └─ sw_old.js
│  ├─ test_icon.png
│  ├─ vercel.svg
│  ├─ workbox-bd7e3b9b.js
│  └─ workbox-bd7e3b9b.js.map
├─ server.js
├─ src
│  ├─ api-services ⭐️ route handler를 향해 보내는 fetch 함수 모음
│  │  └─ auth
│  │     ├─ getBuddyClient.ts
│  │     ├─ getBuddyServer.ts
│  │     └─ updateBuddyInfo.ts
│  ├─ app ⭐️ 라우팅 폴더
│  │  ├─ (providers)
│  │  │  ├─ (authenticated)
│  │  │  │  ├─ chat
│  │  │  │  │  └─ page.tsx
│  │  │  │  ├─ layout.tsx
│  │  │  │  └─ write
│  │  │  │     └─ page.tsx
│  │  │  ├─ (conditional)
│  │  │  │  ├─ layout.tsx
│  │  │  │  └─ profile
│  │  │  │     └─ [id]
│  │  │  │        └─ page.tsx
│  │  │  ├─ (public)
│  │  │  │  ├─ login
│  │  │  │  │  └─ page.tsx
│  │  │  │  ├─ onboarding
│  │  │  │  │  └─ page.tsx
│  │  │  │  ├─ page.tsx
│  │  │  │  ├─ recover
│  │  │  │  │  └─ page.tsx
│  │  │  │  ├─ search
│  │  │  │  │  └─ page.tsx
│  │  │  │  ├─ searchResult
│  │  │  │  │  └─ page.tsx
│  │  │  │  ├─ signup
│  │  │  │  │  └─ page.tsx
│  │  │  │  └─ tutorial
│  │  │  │     └─ page.tsx
│  │  │  ├─ layout.tsx
│  │  │  └─ loading.tsx
│  │  ├─ api ⭐️ api route handler
│  │  │  └─ auth
│  │  │     ├─ buddy
│  │  │     │  └─ route.ts
│  │  │     ├─ callback
│  │  │     │  └─ route.ts
│  │  │     ├─ confirm
│  │  │     │  └─ route.ts
│  │  │     ├─ login
│  │  │     │  └─ route.ts
│  │  │     ├─ logout
│  │  │     │  └─ route.ts
│  │  │     ├─ provider
│  │  │     │  └─ route.ts
│  │  │     ├─ recover-redirect
│  │  │     │  └─ route.ts
│  │  │     ├─ recover
│  │  │     │  └─ route.ts
│  │  │     └─ signup
│  │  │        └─ route.ts
│  │  ├─ favicon.ico
│  │  ├─ globals.css ⭐️ 전역 스타일링 파일
│  │  ├─ layout.tsx
│  │  └─ store.ts
│  ├─ components ⭐️ 컴포넌츠 모음
│  │  ├─ atoms ⭐️ 더 이상 분해할 수 없는 컴포넌트
│  │  │  ├─ Calendar.tsx
│  │  │  ├─ DateSearchButton.tsx
│  │  │  ├─ HomePageSearchBar.tsx
│  │  │  ├─ LocationSearchButton.tsx
│  │  │  ├─ SwitchButton.tsx
│  │  │  ├─ TapMenuButton.tsx
│  │  │  ├─ auth 
│  │  │  │  ├─ GoogleLogInButton.tsx
│  │  │  │  └─ KaKaoLogInButton.tsx
│  │  │  ├─ common
│  │  │  │  ├─ Footer.tsx
│  │  │  │  ├─ Header.tsx
│  │  │  │  ├─ O_Button.tsx
│  │  │  │  ├─ O_Chip.tsx
│  │  │  │  ├─ O_Input.tsx
│  │  │  │  ├─ O_Submit-button.tsx
│  │  │  │  └─ PreferTheme.tsx
│  │  │  ├─ profile
│  │  │  │  ├─ BuddyTemperature.tsx
│  │  │  │  ├─ EditProfileButton.tsx
│  │  │  │  ├─ MyTrips.tsx
│  │  │  │  └─ ProfileImage.tsx
│  │  │  ├─ tutorial
│  │  │  │  ├─ Tuto1Image.tsx
│  │  │  │  ├─ Tuto1Text.tsx
│  │  │  │  ├─ Tuto2Image.tsx
│  │  │  │  ├─ Tuto2Text.tsx
│  │  │  │  ├─ Tuto3Image.tsx
│  │  │  │  ├─ Tuto3Text.tsx
│  │  │  │  ├─ Tuto4Image.tsx
│  │  │  │  ├─ Tuto4Text.tsx
│  │  │  │  ├─ Tuto5Image.tsx
│  │  │  │  ├─ Tuto5Text.tsx
│  │  │  │  └─ TutoTitle.tsx
│  │  │  └─ write
│  │  │     ├─ Center2xlText.tsx
│  │  │     ├─ Cneter2xlTwoLineText.tsx
│  │  │     ├─ Left2xlText.tsx
│  │  │     ├─ LeftSmGrayText.tsx
│  │  │     ├─ LocationList.tsx
│  │  │     ├─ LocationToggleButton.tsx
│  │  │     ├─ ProgressIndicator.tsx
│  │  │     ├─ SelectBuddyCounts.tsx
│  │  │     └─ SlateEditor.tsx
│  │  ├─ molecules ⭐️ atoms를 붙여서 만든 컴포넌트
│  │  │  ├─ H_chips.tsx
│  │  │  ├─ TapMenu.tsx
│  │  │  ├─ auth
│  │  │  │  ├─ AuthPageBottom.tsx
│  │  │  │  └─ AuthPageWrapper.tsx
│  │  │  ├─ common
│  │  │  │  └─ MobileHeader.tsx
│  │  │  ├─ homepage
│  │  │  │  ├─ HomePageBanner.tsx
│  │  │  │  ├─ HomePageBuddies.tsx
│  │  │  │  ├─ HomePageStories.tsx
│  │  │  │  ├─ HomePageTitle.tsx
│  │  │  │  └─ HomePageTrips.tsx
│  │  │  ├─ profile
│  │  │  │  ├─ BuddyFollow.tsx
│  │  │  │  └─ BuddyProfile.tsx
│  │  │  ├─ search
│  │  │  │  ├─ SearchMainPageChipsTitle.tsx
│  │  │  │  └─ SearchPageTitle.tsx
│  │  │  └─ write
│  │  │     ├─ SelectDestinationDescription.tsx
│  │  │     ├─ WelcomeImage.tsx
│  │  │     └─ WelcomeMessage.tsx
│  │  └─ organisms ⭐️ 컴포넌트가 하나의 페이지 단위로까지 만들어진 경우
│  │     ├─ auth
│  │     │  ├─ LogInForm.tsx
│  │     │  ├─ PasswordResetForm.tsx
│  │     │  └─ SignUpForm.tsx
│  │     ├─ common
│  │     │  └─ CustomAlert.tsx
│  │     ├─ homepage
│  │     │  └─ HomePageContainer.tsx
│  │     ├─ pwa
│  │     │  └─ InstallPromptHandler.tsx
│  │     ├─ search
│  │     │  ├─ DateSearchPage.tsx
│  │     │  ├─ LocationSearchPage.tsx
│  │     │  └─ SearchMainPage.tsx
│  │     ├─ tutorial
│  │     │  ├─ TutorialPage1.tsx
│  │     │  ├─ TutorialPage2.tsx
│  │     │  ├─ TutorialPage3.tsx
│  │     │  ├─ TutorialPage4.tsx
│  │     │  └─ TutorialPage5.tsx
│  │     └─ write
│  │        ├─ CompletePage.tsx
│  │        ├─ SelectAdditionalBuddyThemes.tsx
│  │        ├─ SelectDatePage.tsx
│  │        ├─ SelectDestination.tsx
│  │        ├─ SelectRegionPage.tsx
│  │        ├─ SelectTripThemesPage.tsx
│  │        ├─ WelcomePage.tsx
│  │        └─ WriteTrip.tsx
│  ├─ constants ⭐️ 쿼리키 모아 놓는 곳
│  │  ├─ common.constants.ts
│  │  └─ query.constants.ts
│  ├─ contexts ⭐️ 컨텍스트 모아 놓는 곳
│  │  └─ auth.context.tsx
│  ├─ data ⭐️ 자체 data 파일
│  │  ├─ location.ts
│  │  ├─ mbtis.ts
│  │  └─ themes.ts
│  ├─ hooks ⭐️ 훅 모음
│  │  ├─ auth.hooks.ts
│  │  ├─ useAccordion.tsx
│  │  ├─ useCheckPwa.ts
│  │  ├─ useFunnelNextStep.tsx
│  │  ├─ usePreferTheme.tsx
│  │  ├─ useSelectBuddyCounts.tsx
│  │  ├─ useSelectRegion.tsx
│  │  └─ useTapScroll.ts
│  ├─ middleware.ts
│  ├─ providers
│  │  └─ QueryProvider.tsx
│  ├─ types ⭐️ 타입 모음
│  │  ├─ Auth.types.ts
│  │  ├─ Location.types.ts
│  │  ├─ Mbtis.types.ts
│  │  ├─ Slate.types.d.ts
│  │  ├─ Themes.types.ts
│  │  └─ supabase.ts
│  └─ utils ⭐️ 유틸 함수 모음
│     ├─ common
│     │  ├─ getPathnameServer.ts
│     │  └─ handleChipClick.ts
│     ├─ pwa
│     │  └─ isPWA.ts
│     ├─ regexs.ts
│     ├─ supabase
│     │  ├─ client.ts
│     │  ├─ middleware.ts
│     │  └─ server.ts
│     ├─ ui
│     │  ├─ openCustomAlert.ts
│     │  └─ tailwind_merge.ts
│     └─ validation.ts
├─ supabase
│  ├─ .gitignore
│  ├─ config.toml
│  └─ seed.sql
├─ tailwind.config.ts ⭐️ 테일윈드 설정 파일
├─ tsconfig.json
├─ webpack.config.js
└─ yarn.lock

 

1. 모듈 패턴 (Module Pattern)

  • 프로젝트의 각 기능을 독립적인 모듈로 나누어 관리한다.
  • components, constants, contexts, hooks, utils 등의 폴더 구조가 이를 반영한다.
  • 정규식 패턴도 하나의 파일에 모두 모아서 재사용성을 높이도록 한다.
  • 이로 인해 코드의 재사용성과 유지보수성이 향상된다.

2. 컨테이너-프레젠테이션 패턴 (Container-Presentation Pattern)

  • components 폴더 내부의 구조가 이를 반영한다.
  • atoms, molecules, organisms은 주로 프레젠테이션 컴포넌트(단순히 UI를 그리는 역할)로 구성되어 있고, providers나 contexts 폴더에 있는 파일들은 주로 컨테이너 컴포넌트(상태 관리 및 비즈니스 로직 담당)로 구성된다

3. 훅 패턴 (Hook Pattern)

  • hooks 폴더에 있는 커스텀 훅(useCheckPwa.ts)은 재사용 가능한 로직을 추출하여 독립적인 함수로 만든다. 이는 React의 기능을 확장하고 코드 중복을 줄이는데 도움이 된다

4. 싱글톤 패턴 (Singleton Pattern)

  • providers 폴더의 QueryProvider.tsx와 같은 파일은 애플리케이션 내에서 단일 인스턴스만 생성되어 사용되며, 전역 상태를 관리한다.
  • supabase의 client 인스턴스 또한 싱글톤 패턴을 사용하여 애플리케이션 내에서 단일 인스턴스로만 생성되어 메모리를 효율적으로 사용한다.

5. 프록시 패턴 (Proxy Pattern)

  • middleware.ts 파일들은 요청이 실제 서버에 도달하기 전에 이를 가로채어 추가적인 로직을 수행한다. 이는 프록시 패턴의 일종으로, 주로 로깅, 인증 등의 기능을 구현하는데 사용된다.

6. 팩토리 패턴 (Factory Pattern)

  • utils 폴더 내부의 파일들에서 특정 객체를 생성하는 로직을 별도의 함수로 분리하여, 객체 생성과 관련된 코드를 단순화하고 유지보수성을 높입니다.

7. 아토믹 패턴 (Atomic Pattern)

  • Atoms, Molecules, Organisms 패턴을 사용하여 특정 컴포넌트를 유지보수 할 때 다른 컴포넌트에 미치는 영향을 최소화한다.
  • 단일 책임 원칙에 따라 작은 단위로 나누어진 컴포넌트는 독립적으로 동작하여, 팀원 간의 컴포넌트 중복, 충돌 등의 문제를 최소화 할 수 있.
  • 우리는 우리의 최종 프로젝트 볼륨 증가를 예측하고, 유지보수성과 디버깅 효율성을 높였다.

댓글