Next.js 폴더 구조 (폴더 아키텍쳐/ feat. medium, FSD)

최기환·2024년 1월 7일
6

Next.js

목록 보기
3/4

Next.js 폴더 구조(타 프론트엔드에도 적용 가능 합니다!)

작년 Next.js 를 공부하고 프로젝트를 하기 시작했을 때 프로젝트를 정리하기 위해서 다양한 폴더 구조들을 사용해 왔다. 스스로 이런 폴더 구조는 어떨까 하고 생각하며 적용하다 레퍼런스가 필요하다는 생각에 medium을 살펴보았고 다음 글을 발견하게 되었다! FSD라고 불리는 아키텍쳐 이며 Feature Sliced

A Front-End Application Folder Structure that Makes Sense

이 글을 읽고 obsidian에 정리해둔 채 유용하게 사용했고 현재까지도 이러한 구조로 프로젝트를 설계한다.

폴더 구조

src 폴더: root 디렉토리

  • assets
  • components: 앱 전체에서 공유되는 모든 컴포넌트
  • hooks: 앱 전체에서 공유되는 모든 hook
  • config: 앱 설정 파일
  • features: 앱의 features를 포함하는 폴더. 이 폴더 안에 대부분의 앱 코드가 저장됨
  • lib: 앱에서 사용되는 써드 파티 라이브러리 설정 파일
  • services: services를 담는 폴더
  • stores: 전역 state stores
  • test: mocks, helpers, utilities, configurations를 위한 테스트 코드
  • types: 공유되는 타입스크립트 타입
  • utils: 공유되는 utility 함수들
  • providers: Context 주입을 위해 사용되는 Provider들

🎯 services는 애플리케이션의 핵심 비즈니스 로직이나 외부 API와의 통신, 데이터 변환, 유틸리티 함수 등의 기능을 의미한다.

여기서 나는 servicesutils 의 정확한 차이점이 뭘까? 하는 생각이 들었다.

utilsservices 의 차이점

  1. Services:
    • 목적: 애플리케이션의 핵심 비즈니스 로직이나 외부 API와의 통신, 데이터베이스 연산 등의 기능을 제공한다.
    • 특징
      - 주로 외부 시스템이나 서비스와의 통신을 담당한다.
      - HTTP 요청, 데이터베이스 쿼리, 인증 및 인가와 같은 비즈리스 로직을 포함할 수 있다.
  2. Utils:
    • 목적: 애플리케이션 전반에 걸쳐 재사용될 수 있는 범용적인 함수나 유틸리티를 제공한다.
    • 특징:
      - 상태나 외부 의존성 없이 독립적으로 동작하는 함수나 로직을 포함한다.
      - 애플리케이션의 핵심 로직과는 거리가 있을 수 있다.
    • 예시:
      - 날짜 형식 변환 함수
      - 문자열 검증 유틸리티
      - 배열이나 객체를 처리하는 헬퍼 함수

layout 이나 pageapp 폴더에서 파일의 형태로 관리되기에 폴더 구조가 따로 필요 없다고 생각했다.

이 폴더 구조를 다음 명령어를 루트 디렉토리에서 실행하여 간단하게 만들어낼 수 있다.

mkdir -p src/{hooks,utils,assets,config,lib,services,test,components,features,stores,types, providers}

아직 잘 이해가 가지 않을 수 있다. 자세히 설명하겠다.

이 폴더 구조 적용시 유의해야할 중요한 점이 세가지가 있다.

알아둬야할 세가지 중요한 점:

  • Pages는 그 자체로 이미 모듈화 되어 있다. 페이지 안의 로직은 최소한으로 유지되어야 한다.
  • 확장성과 유지보수를 위해 대부분의 코드를 features 폴더안에 위치시킨다. 모든 feature 폴더는 domain-specific 코드를 포함해야 한다.
  • feature 폴더 안의 components, hooks, stores, services 등의 모든 것들은 다른 feature에 공유되어서는 안된다. 다만 꼭 필요하다면 진입점인 index.ts 파일을 통해서만 이루어져야 한다!

Features Folder

앞서 언급한대로 앱의 대부분의 코드는 features 폴더안에 위치해야 한다.

  • api: 모든 fetch 로직은 여기에 위치한다.
  • components: 특정 feature의 컴포넌트들
  • hooks(==composables): 특정 feature의 훅들 (나는 composables이 아닌 hooks로 사용한다. Vue에서 hookcomposable이라 부른다고 했던거 같다....)
  • stores: state managements 코드
  • types: features에 특정되는 타입 정의
  • index.ts: feature의 진입 포인트. feature의 public API로 동작한다, 또한 이 파일은 어플리케이션에서 공개적으로 사용될 부분만 export 해야한다.

아래는 지양해야할 코드와 지향해야할 코드 예시이다.

# Bad 🚫 🚫 🚫  
import { UserProfile } from '@/features/profile/components/UserProfile.tsx'  
  
# Good ✅ ✅ ✅  
import { UserProfile } from '@/features/profile'

-> 보면 알겠지만 features에 있는 profilescomponets에서 뭔가를 import하면 안된다. components에 뭔가 내가 꼭 export 해야할 컴포넌트가 있다면 profiles 폴더 안의 index.ts 파일에서 import한 후 export 하고 그 index.ts 파일에서 불러와 사용해야 한다. 즉

# @/features/profile/index.ts
import { default as UserProfile } from '@/features/profile/components/UserProfile.tsx'

이렇게 index.ts에 진입점을 만들고

# some components
import { UserProfile } from '@/features/profile/index.ts'

와 같은 형식이 되어야 한다는 의미이다.

그리고 편리하게도 이러한 룰을 ESLint를 통해 강제할 수 있다.

rules: {  
		"no-restricted-imports": [  
		"error",
		{  
			patterns: ["@/features/*/*"]
		}  
	],  
	"import/no-cycle": "error",  
	...  
}
profile
프론트엔드 개발자

0개의 댓글