📚 React + Next.js + TypeScript 폴더 구조 완벽 가이드
App Router / Pages Router / 환경별 구조 차이까지 정리
1. 들어가며
기본적으로 React의 폴더 구조가 어떻게 되는지 정리해본다.
리액트는 환경(설정)에 따라 폴더구조가 다르다. 그래서 같은 리액트를 사용한다고 해도 어떻게 초반에 설정을 했느냐에 구조가 다를 수 밖에 없다.
필자는 리액트를 시작한지 얼마안되었지만 개인 프로젝트 및 회사에서의 프로젝트를 고려했을 때 아래와 같이 고민을 했었다.
“폴더 구조를 어떻게 잡아야 하지?”
예제를 보거나 강의를 몇개를 찾아보면 조금씩 다르기 때문이다. 물론 리액트와 Next.js 버전, TypeScript 따라 달라질 수 있는에 정확하게 어떻게 달라지는지 기록을 해보려고 한다.
특히 Next.js를 사용할 때는 App Router와 Pages Router라는 두 가지 라우팅 방식이 있고, 여기에 TypeScript, 상태 관리, API 연동, 스타일 전략까지 더해지면 폴더 구조가 제각각 달라진다.
2. React + TypeScript 기본 구조
(Vite, CRA 등 Next.js 없이 단독 React 프로젝트)
my-react-app/
├─ public/ # 정적 파일
├─ src/
│ ├─ app/ # 엔트리와 전역 설정
│ ├─ pages/ # react-router 페이지
│ ├─ features/ # 도메인 단위(회원, 글, 결제 등)
│ ├─ components/ # 공용 UI
│ ├─ hooks/ # 전역 커스텀 훅
│ ├─ store/ # Zustand, Redux 전역 상태
│ ├─ services/ # API 클라이언트, Firebase/Supabase 연동
│ ├─ lib/ # 유틸, 헬퍼 함수
│ ├─ styles/ # 전역 스타일
│ ├─ types/ # 전역 타입 정의
│ └─ config/ # 환경 설정, 상수
└─ vite.config.ts
💡 포인트
- pages → URL 매핑되는 화면
- features → 관련 로직, API, UI, 타입을 한 기능 단위로 모음
- services → 외부 서비스 연동
3. Next.js + TypeScript (App Router 기준)
Next.js 13+에서 기본 라우팅 방식이다.
my-next-app/
├─ public/
├─ app/
│ ├─ layout.tsx # 루트 레이아웃
│ ├─ page.tsx # /
│ ├─ dashboard/
│ │ ├─ layout.tsx
│ │ ├─ page.tsx
│ │ └─ settings/page.tsx
│ ├─ api/ # Route Handlers (간단 API)
│ │ └─ auth/route.ts
│ └─ actions/ # 서버 액션
├─ components/ # 공용 UI
├─ features/ # 도메인 단위 모듈
├─ lib/ # DB, 인증, 유틸
├─ providers/ # 전역 Provider
├─ styles/ # 전역 CSS, Tailwind
├─ types/
└─ config/
💡 App Router 특징
- 페이지·레이아웃이 폴더 구조로 매핑
- 서버 컴포넌트 기본, "use client"로 클라이언트 전환
- 서버 액션(Server Action)을 페이지 근처에 둘 수 있음
4. Next.js + TypeScript (Pages Router 기준)
Next.js 12 또는 App Router를 쓰지 않는 경우
my-next-pages-app/
├─ public/
├─ pages/
│ ├─ _app.tsx # 전역 레이아웃/Provider
│ ├─ _document.tsx # HTML 문서 커스터마이즈
│ ├─ index.tsx # /
│ ├─ about.tsx # /about
│ ├─ blog/[slug].tsx # 동적 라우트
│ └─ api/ # API 라우트
│ └─ auth.ts
├─ components/ # 공용 UI
├─ features/ # 도메인 단위 모듈
├─ lib/ # 유틸, API 클라이언트
├─ hooks/
├─ styles/
├─ types/
└─ config/
💡 Pages Router 특징
- pages/ 폴더 안에 라우트 정의
- getStaticProps, getServerSideProps 등 페이지에서 직접 데이터 패칭
- API 라우트는 pages/api/ 안에 위치
5. App Router vs Pages Router 비교
구분 | App Router | Pages Router |
라우트 정의 | app/ 폴더 구조 기반 | pages/ 폴더 파일명 기반 |
전역 레이아웃 | app/layout.tsx | pages/_app.tsx |
데이터 패칭 | 서버 컴포넌ㅌ, 서버 액션, fetch API | getStaticProps, getServerSideProps |
API 라우트 | app/api/**/route.ts | pages/api/** |
기본 컴포넌트 | 서버 컴포넌트 | 클라이언트 컴포넌트 |
6. 왜 환경(설정)에 따라 폴더 구조가 달라질까?
- 라우팅 방식
- App Router는 서버 액션과 서버 컴포넌트 중심 → 도메인별 로직 근접 배치
- Pages Router는 페이지 중심 → 페이지는 얇게, 기능 폴더에서 로직 처리
- 배포 대상
- Serverless/Edge: 환경별 DB, 캐시 유틸 분리 필요
- Node 서버: 서버 전용 코드와 클라이언트 코드를 명확히 구분
- 데이터 패칭 전략
- 클라이언트 패칭(SWR, React Query) vs SSR/SSG 여부에 따라 fetch 로직 위치가 달라짐
- 인증/세션 방식
- NextAuth, JWT, OAuth에 따라 API 라우트나 미들웨어가 추가됨
- 스타일 방식
- Tailwind, CSS-in-JS, CSS Modules 중 무엇을 쓰느냐에 따라 styles/ 구조가 달라짐
- 모노레포 여부
- Turborepo/PNPM 환경이면 공용 패키지를 packages/로 분리
7. TypeScript 설정 팁 (경로 별칭)
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@features/*": ["./src/features/*"],
"@lib/*": ["./src/lib/*"]
}
}
}
8. 마무리
프로젝트의 폴더 구조에 정답은 없는거 같다.
다만, 환경(라우팅 방식, 배포, 데이터 패칭, 인증 방식 등)에 따라 응집도와 확장성을 고려한 구조를 잡는 것이 중요하다고 한다.
핵심 원칙: 페이지는 얇게, 기능은 두껍게!
그리고 관련 파일은 가깝게(co-location) 두기.