Next.js 프로젝트 구조 (Project Structure and Organization)
Next.js 공식 문서 Project structure and organization 페이지를 기반으로, 폴더·파일 컨벤션과 프로젝트 정리 방법을 요약한 자료입니다.
1. 폴더 및 파일 컨벤션 (Folder and file conventions)
1.1 상위(top-level) 폴더
상위 폴더는 애플리케이션 코드와 정적 자산을 나누는 기준이 됩니다.
| 폴더 | 설명 |
|---|---|
app/ | App Router용 폴더. layout.tsx, page.tsx 등을 사용하여 라우트와 UI를 구성합니다. |
pages/ | 기존 Pages Router용 폴더. 새 프로젝트에서는 app/ 사용이 권장됩니다. |
public/ | 정적 자산(이미지, 폰트 등)을 두는 폴더. URL 기준 루트(/)에서 바로 서빙됩니다. |
src/ | 선택적 애플리케이션 소스 폴더. app/과 기타 코드를 src 아래로 모아 루트 설정 파일들과 분리할 수 있습니다. |
1.2 상위(top-level) 파일
상위 파일들은 애플리케이션 설정, 의존성 관리, 프록시 실행, 모니터링, 환경 변수 정의 등에 사용됩니다.
next.config.js: Next.js 설정 파일package.json: 의존성과 npm/pnpm 스크립트 정의instrumentation.ts: OpenTelemetry 및 기타 계측 설정proxy.ts: Next.js 요청 프록시 설정.env,.env.local,.env.production,.env.development: 환경 변수 파일eslint.config.mjs: ESLint 설정 파일.gitignore: Git에서 무시할 파일/폴더 목록next-env.d.ts: Next.js용 TypeScript 선언 파일tsconfig.json/jsconfig.json: TypeScript/JavaScript 설정 파일
1.3 라우팅 파일 (Routing files)
특수 파일 이름은 특정 라우팅 역할을 담당합니다.
| 파일 이름 | 확장자 | 역할 |
|---|---|---|
layout | .js .jsx .tsx | 라우트 세그먼트의 레이아웃(공통 UI) |
page | .js .jsx .tsx | 실제 페이지 컴포넌트(공개 라우트) |
loading | .js .jsx .tsx | Suspense 로딩 UI (스켈레톤 등) |
error | .js .jsx .tsx | 해당 세그먼트용 에러 경계 UI |
global-error | .js .jsx .tsx | 앱 전체에 적용되는 전역 에러 UI |
not-found | .js .jsx .tsx | 404 등 “찾을 수 없음” UI |
route | .js .ts | API 엔드포인트(HTTP 핸들러) |
template | .js .jsx .tsx | 다시 렌더링되는 레이아웃(템플릿) |
default | .js .jsx .tsx | 병렬 라우트의 기본/폴백 페이지 |
2. 라우팅 구조 (Routing structure)
2.1 중첩 라우트 (Nested routes)
- 각 폴더가 URL 세그먼트 하나에 대응합니다.
- 폴더를 중첩하면 URL도 중첩됩니다.
- 어느 수준에서든
layout파일이 있으면, 해당 레이아웃이 자식 세그먼트를 감싸게 됩니다. - 특정 세그먼트에
page또는route파일이 존재할 때 그 경로가 “공개 라우트”가 됩니다.
예시:
| 파일 경로 | URL | 설명 |
|---|---|---|
app/layout.tsx | (전체) | 모든 라우트를 감싸는 루트 레이아웃 |
app/blog/layout.tsx | /blog 이하 | 블로그 섹션 전용 레이아웃 |
app/page.tsx | / | 루트 페이지 |
app/blog/page.tsx | /blog | 블로그 메인 페이지 |
app/blog/authors/page.tsx | /blog/authors | 블로그 작성자 목록 등 |
2.2 동적 라우트 (Dynamic routes)
대괄호([])를 이용해 세그먼트를 파라미터로 만들 수 있습니다.
[segment]: 한 개의 파라미터[...segment]: 여러 세그먼트를 한 번에 받는 “catch‑all”[[...segment]]: 파라미터가 없어도 되는 “optional catch‑all”
예시:
| 파일 경로 | 매칭되는 URL 예 |
|---|---|
app/blog/[slug]/page.tsx | /blog/my-first-post |
app/shop/[...slug]/page.tsx | /shop/clothing, /shop/clothing/shirts |
app/docs/[[...slug]]/page.tsx | /docs, /docs/layouts-and-pages, /docs/api-reference/use-router |
2.3 라우트 그룹과 프라이빗 폴더 (Route groups & private folders)
-
Route group:
(folderName)- URL에는 포함되지 않고, 폴더 구조와 레이아웃 구성을 위한 용도로만 사용됩니다.
- 예:
app/(marketing)/page.tsx→ 실제 URL은/
-
Private folder:
_folderName- 라우팅 시스템에서 완전히 제외되는 폴더입니다.
- 라우트가 아닌 UI 컴포넌트나 유틸 파일을 같은 세그먼트 안에 안전하게 모아둘 수 있습니다.
- 예:
app/blog/_components/Post.tsx,app/blog/_lib/data.ts는 라우트로 노출되지 않습니다.
3. 병렬 라우트와 인터셉트 라우트 (Parallel & Intercepted Routes)
특정 UI 패턴(슬롯 레이아웃, 모달 라우팅 등)을 구현하기 위한 기능입니다.
3.1 병렬 라우트 (Parallel routes)
@slot패턴으로 이름이 붙은 폴더를 만들면, 부모 레이아웃에서 여러 슬롯에 서로 다른 라우트를 동시에 렌더링할 수 있습니다.- 예: 사이드바 + 메인 콘텐츠 구조
3.2 인터셉트 라우트 (Intercepted routes)
다른 라우트를 현재 레이아웃 안에서 “가로채서” 렌더링하지만, URL은 그대로 유지하는 패턴입니다. 보통 리스트 위에 상세 모달을 띄울 때 사용합니다.
문서에서 소개하는 패턴:
| 패턴 | 의미 / 용도 예시 |
|---|---|
@folder | 이름 있는 슬롯. 사이드바와 메인 영역 등 병렬 렌더링 |
(.)folder | 같은 레벨의 라우트를 모달 등으로 가로채기 |
(..)folder | 부모 레벨의 자식 라우트를 오버레이로 표시 |
(..)(..)folder | 두 단계 위에서부터 자식을 인터셉트 |
(...)folder | 루트 기준으로 임의의 라우트를 현재 뷰 안에서 표시 |
4. 메타데이터 파일 컨벤션 (Metadata file conventions)
4.1 앱 아이콘 (App icons)
| 이름 | 유형 | 설명 |
|---|---|---|
favicon | .ico | 파비콘 파일 |
icon | .ico .jpg .jpeg .png .svg | 앱 아이콘 정적 파일 |
icon | .js .ts .tsx | 코드를 통해 생성하는 앱 아이콘 |
apple-icon | .jpg .jpeg .png | 애플 기기용 아이콘 정적 파일 |
apple-icon | .js .ts .tsx | 코드를 통해 생성하는 애플 아이콘 |
4.2 Open Graph 및 Twitter 이미지
| 이름 | 유형 | 설명 |
|---|---|---|
opengraph-image | .jpg .jpeg .png .gif | OG 이미지 정적 파일 |
opengraph-image | .js .ts .tsx | 코드로 생성하는 OG 이미지 |
twitter-image | .jpg .jpeg .png .gif | 트위터 카드 이미지 정적 파일 |
twitter-image | .js .ts .tsx | 코드로 생성하는 트위터 이미지 |
4.3 SEO 관련 파일
| 이름 | 유형 | 설명 |
|---|---|---|
sitemap | .xml | 정적 사이트맵 파일 |
sitemap | .js .ts | 코드로 생성하는 사이트맵 |
robots | .txt | 정적 robots 규칙 파일 |
robots | .js .ts | 코드로 생성하는 robots 설정 |
5. 프로젝트 구조화 (Organizing your project)
Next.js는 프로젝트 구조에 대해 강한 규칙을 강요하지 않습니다. 다만, 몇 가지 기능을 제공하여 팀에 맞는 구조를 만들 수 있도록 돕습니다.
5.1 컴포넌트 계층 구조 (Component hierarchy)
특수 파일로 정의된 컴포넌트들은 다음 순서로 중첩되어 렌더링됩니다.
layout.jstemplate.jserror.js(에러 경계)loading.js(Suspense 경계)not-found.js(“찾을 수 없음” UI)page.js또는 더 안쪽의layout.js
중첩 라우트에서는 부모 세그먼트의 컴포넌트 안에 자식 세그먼트의 컴포넌트가 재귀적으로 중첩됩니다.
5.2 Colocation
app디렉터리에서 폴더 구조는 라우트 구조를 정의합니다.page.js또는route.js가 있는 세그먼트만 실제로 공개 라우트가 됩니다.page.js/route.js파일에서 반환하는 콘텐츠만 클라이언트로 전송되므로, 같은 폴더 안에 다른 프로젝트 파일을 함께 두더라도 라우트로 노출되지 않습니다.- 즉,
app디렉터리 안에 컴포넌트, 유틸, 훅 등을 함께 “colocate”해도 안전합니다. - 원한다면 프로젝트 파일을
app밖에 두고 라우팅만app안에 둘 수도 있습니다.
5.3 Private 폴더
- 폴더 이름 앞에 밑줄을 붙이면
_folderName형태의 private 폴더가 됩니다. - 라우팅에서 완전히 제외되며, 구현 세부 사항을 모아두는 용도로 사용합니다.
- 활용 예:
- UI 로직과 라우팅 로직을 분리
- 프로젝트 전반에서 내부 파일을 일관된 패턴으로 정리
- 코드 에디터에서 파일을 그룹화
- 향후 Next.js 특별 파일 이름과의 충돌 가능성 줄이기
추가 팁:
app밖의 파일에도 같은 언더스코어 패턴을 사용해 “private” 의미를 담을 수 있습니다.- URL에서 언더스코어로 시작하는 세그먼트를 사용하고 싶다면, 폴더명을
%5FfolderName처럼 URL 인코딩된 형태로 사용할 수 있습니다. - Private 폴더를 사용하지 않을 경우, Next.js의 특수 파일 이름 규칙을 알고 있는 것이 좋습니다.
5.4 Route 그룹
(folderName)형식의 폴더는 URL에서 생략됩니다.- 주요 용도:
- 마케팅, 관리자, 대시보드 등 섹션/의도/팀별로 라우트를 조직화
- 같은 URL 계층에서 여러 개의 중첩 레이아웃을 만들기
- 여러 root layout을 구성하거나, 특정 라우트 집합에만 레이아웃을 적용하기
5.5 src 폴더
- 애플리케이션 코드를
src하위로 모을 수 있습니다 (src/app,src/components등). - 루트에는 설정 파일들만 두고, 실제 코드들은
src안에 두어 구조를 명확히 분리하는 패턴입니다.
6. 프로젝트 구조 예시 (Examples)
문서에서는 대표적인 구조 전략을 몇 가지 예로 보여 줍니다. 핵심은 팀에 맞는 전략을 하나 정해서 일관되게 유지하는 것입니다.
예시에서 사용하는
components,lib등의 폴더 이름은 Next.js에서 특별한 의미를 갖지 않습니다. 필요에 따라ui,utils,hooks,styles등 다른 이름을 사용해도 됩니다.
6.1 app 밖에 프로젝트 파일 두기
app디렉터리는 라우팅 전용으로 사용합니다.- 공통 컴포넌트, 유틸, 스타일 등은 루트의 별도 폴더에 둡니다.
app/
layout.tsx
page.tsx
components/
Button.tsx
lib/
api.ts
styles/
globals.css
6.2 app 안의 상위 폴더에 두기
app디렉터리 내부에components,lib등 공용 폴더를 만들어 둡니다.
app/
layout.tsx
page.tsx
components/
Button.tsx
lib/
api.ts
6.3 기능 또는 라우트 단위로 나누기
- 전역적으로 공유되는 코드만 루트에 두고,
특정 라우트에서만 쓰이는 코드는 해당 세그먼트 아래에 함께 둡니다.
app/
layout.tsx
page.tsx
dashboard/
layout.tsx
page.tsx
_components/
Charts.tsx
_lib/
fetchDashboard.ts
6.4 URL을 바꾸지 않고 라우트 구조만 정리하기
(marketing),(shop)같은 route group을 사용하여 URL에는 영향을 주지 않으면서 관련 라우트를 묶습니다.- 각 그룹 안에
layout.tsx를 두어 서로 다른 레이아웃을 적용할 수 있습니다.
6.5 특정 세그먼트만 레이아웃에 참여시키기
- 예를 들어
(shop)그룹 안에account,cart라우트만 넣고,checkout은 그룹 밖에 두면,
account/cart만 공통 레이아웃을 공유하고 checkout은 다른 레이아웃을 사용할 수 있습니다.
6.6 특정 라우트에만 로딩 스켈레톤 적용하기
/(overview)같은 그룹을 만들고, 그 안에loading.tsx와page.tsx를 두면,
해당 그룹에 속한 라우트에만loading.tsx가 적용됩니다.
6.7 여러 개의 루트 레이아웃 만들기
- 최상위
layout.js를 제거하고, 각 route group 안에layout.js를 두면,
(marketing),(shop)등이 각각 독립적인 root layout을 갖게 됩니다. - 이때 각 root layout에는
<html>과<body>태그를 직접 포함해야 합니다.