Post

Next.js (2. Project Structure) 정리

Next.js (2. Project Structure) 정리

0. 프로젝트 구조


Next.js의 폴더 구조와 파일 이름만으로 라우팅과 기능을 결정한다.
따라서 폴더 및 파일 규칙을 이해하면 App Router 전체 구조를 이해하는 데 매우 큰 도움이 된다.

1. 폴더 및 파일 규칙


1-1. 최상위 폴더 (Top-level folders)


최상위 폴더는 프로젝트 루트(/)에 위치하며 애플리케이션 코드와 정적 자산을 구성하는 데 사용된다.

Route segments to path segments

폴더역할
app/App Router를 사용하는 메인 애플리케이션 폴더
pages/기존 Pages Router
public/정적 파일 폴더 (이미지, 폰트, 아이콘 등)
src/선택 사항. 코드와 설정 파일을 분리하고 싶을 때 사용

1-2. 최상위 파일 (Top-level files)


최상위 파일은 애플리케이션 설정, 의존성 관리, 프록시 실행, 모니터링 도구 연동, 환경 변수 정의 등 중요한 역할을 하는 파일들이다.

파일역할
next.config.jsNext.js 설정 파일
package.json프로젝트 의존성 및 스크립트
instrumentation.tsOpenTelemetry 및 모니터링 파일
proxy.tsNext.js 요청 프록시
.env환경 변수 (버전 관리에 포함하지 말 것)
.env.local로컬 환경 변수 (버전 관리에 포함하지 말 것)
.env.production프로덕션 환경 변수 (버전 관리에 포함하지 말 것)
.env.development개발 환경 변수 (버전 관리에 포함하지 말 것)
eslint.config.mjsESLint 설정 파일
.gitignoreGit에서 무시할 파일 및 폴더 목록
next-env.d.tsNext.js용 TypeScript 선언 파일 (버전 관리에 포함하지 말 것)
tsconfig.json / jsconfig.jsonTypeScript / JavaScript 경로 및 옵션 설정 파일

1-3. 라우팅 파일 (Routing Files)


Next.js에서 특정 이름과 파일특정 기능을 가진다.
폴더 안에 이 파일들을 추가하면 Next.js가 자동으로 라우팅과 UI 동작을 구성한다.

파일명확장자역할설명
layout.js / .jsx / .tsx레이아웃해당 폴더와 하위 라우트 전체를 감싸는 공통 UI.
주로 header, nav, footer 등을 배치
page.js / .jsx / .tsx페이지실제 URL에 노출되는 화면.
이 파일이 존재하면 해당 폴더는 라우트가 됨
loading.js / .jsx / .tsx로딩 UI페이지나 레이아웃이 서버에서 준비될 때 표시되는 스켈레톤 UI
not-found.js / .jsx / .tsx404 UI해당 라우트에서 데이터 또는 페이지를 찾지 못했을 때 표시
error.js / .jsx / .tsx에러 UI해당 라우트 내에서 발생하는 에러를 처리하는 Error Boundary
global-error.js / .jsx / .tsx전역 에러 UI앱 전체에서 발생하는 에러 처리용 Error Boundary
route.js / .tsAPI 엔드포인트서버 액션 기반의 API 라우트(/api 없이 가능)
template.js / .jsx / .tsx재렌더링되는 레이아웃페이지 전환 시 layout과 달리 매번 새로 렌더링됨
default.js / .jsx / .tsx병렬 라우트 fallback 페이지Parallel Routes에서 기본 fallback 페이지
  • page.tsx가 있어야 URL에 노출된다.
  • layout.tsx는 해당 폴더 및 하위 페이지 전체를 감싼다.
  • error / not-found / loading은 선택적으로 넣는 파일이지만 넣자마자 바로 작동한다.
  • route.ts/app/api/...가 아니라 현재 폴더를 기반으로 API 엔드포인트가 된다.
  • templatelayout의 가장 큰 차이는 캐싱 여부와 렌더 방식이다.

1-4. 중첩 라우트 (Nested routes)


Next.js는 폴더 구조 = URL 구조라는 원칙을 따른다.
폴더를 어떻게 중첩하느냐에 따라 URL도 동일하게 중첩된다.

  1. 폴더 = URL 세그먼트(segment)
    • 폴더 이름이 그대로 URL 경로가 된다.
  2. 중첩된 폴더 = 중첩된 URL
    • 폴더를 안으로 넣으면 URL도 그만큼 깊어진다.
  3. layout.tsx는 어떤 깊이에서든 하위 모든 경로를 감싼다.
    • 폴더 내부에 layout.tsx가 있으면, 그 폴더 아래의 모든 페이지를 감싸는 공통 UI가 된다.
  4. page.tsx 또는 route.ts가 존재하면 그 폴더는 “공개 라우트(public route)”가 된다.
    • URL로 접근 가능한 실제 라우트가 된다.
경로(Path)URL 패턴설명
app/layout.tsx애플리케이션 전체를 감싸는 루트 레이아웃 (모든 라우트 포함)
app/blog/layout.tsx/blog 및 하위 모든 경로 감쌈
app/page.tsx/루트 경로 페이지
app/blog/page.tsx/blog/blog 페이지
app/blog/authors/page.tsx/blog/authors/blog/authors 페이지

1-5. 동적 라우트 (Dynamic routes)


Next.js의 동적 라우팅은 폴더 이름을 대괄호로 감싸서 구현한다.
이렇게 하면 URL의 특정 부분을 변수처럼 사용할 수 있다.

동적 세그먼트 종류:

  1. [segment] - 단일 파라미터
    • URL의 한 부분을 변수로 매핑한다.
    • app/blog/[slug]/page.tsx
      /blog/my-first-post
  2. [...segment] - catch-all (모든 하위 경로 포함)
    • 여러 단계의 URL 세그먼트를 한 번에 받는다.
    • app/shop/[...slug]/page.tsx
      /shop/clothing
      /shop/clothing/shirts
  3. [[...segment]] - optional catch-all (선택적)
    • 경로가 있어도 되고 없어도 되는 경우
    • app/docs/[[...slug]]/page.tsx
      /docs
      /docs/layouts-and-pages
      /docs/api-reference/use-router

파라미터 접근 방법:

동적 세그먼트로 받은 값은 params prop을 통해 접근한다.

1
2
3
export default function Page({ params }) {
  return <div>{params.slug}</div>;
}

1-6. 라우트 그룹 및 비공개 폴더 (Route groups and private folders)


Next.js에서는 URL 구조는 유지하면서 코드만 깔끔하게 정리하고 싶을 때 두 가지 기능을 제공한다.

  • Route Groups → ((폴더명))
  • Private Folders → _(폴더명)
  1. Route Groups ((folder))

    URL을 바꾸지 않고 폴더 구조만 나누고 싶을 때 사용한다.

    규칙:

    • 폴더 이름을 괄호로 감싸면 URL에서 완전히 제외된다.
    • 주로 레이아웃을 그룹화하거나, 섹션별 UI 구조를 나눌 때 사용한다.
    PathURL설명
    app/(marketing)/page.tsx/(marketing)은 URL에 나오지 않음
    app/(shop)/cart/page.tsx/cart(shop) 그룹 내부에서 레이아웃 공유
  2. Private Folders _(folder)

    라우트로 노출되지 않는 UI 컴포넌트, 유틸 함수, 서버 로직 등을 해당 라우트 폴더 근처에 두고 싶을 때 사용한다.

    규칙:

    • 폴더 이름 앞에 _를 붙인다.
    • 이 폴더 안의 파일을 라우트로 인식되지 않는다.
    • 오로지 코드 보관용이다.
    PathURL설명
    app/blog/_components/Post.tsxURL로 접근 불가. UI 컴포넌트 보관용
    app/blog/_lib/data.tsURL로 접근 불가. 유틸 함수 보관용

1-7. 병렬 및 가로채기 라우트 (Parallel and Intercepted Routes)


Parallel Routes와 Intercepted Routes는 일반적인 페이지 전환이 아닌 특수한 UI 패턴을 구현할 때 필요한 기능이다.

대표적으로:

  • 사이드바 + 메인 화면 같이 보여주는 레이아웃
  • 리스트 화면 위에 디테일 화면을 모달로 띄우는 구조
  • 현재 URL은 그대로 유지한 채 다른 라우트의 UI를 끼워 넣는 기능
  1. Parallel Routes — @folder

    부모 레이아웃이 여러 개의 화면 슬롯(slot)을 렌더링하고 싶을 때 사용한다.

    패턴의미사용 사례
    @folder이름 있는 슬롯Sidebar + Main Content
  2. Intercepted Routes — URL은 유지하고, 다른 라우트를 현재 화면에 렌더링

    현재 URL을 바꾸지 않고 다른 라우트의 화면을 현재 위치에 끼워 넣는 기능이다.

    예를 들어, /products 리스트를 보고 있는 중에 상세 페이지를 모달로 띄우고 싶지만 URL은 /products 그대로 두고 싶은 경우

    패턴의미사용 사례
    (.)folder동일 레벨 라우트 인터셉트형제 라우트를 모달로 띄우기
    (..)folder부모 레벨 라우트 인터셉트부모의 자식 라우트를 오버레이로 띄우기
    (..)(..)folder두 단계 상위 라우트 인터셉트깊은 중첩에서 오버레이
    (...)folder루트에서 인터셉트어떤 경로든 현재 화면에 렌더링

1-8. 메타데이터 파일 규칙 (Metadata file conventions)


Next.js는 특정 파일 이름을 통해 아이콘, Open Graph 이미지, Twitter 이미지, SEO 관련 파일 등을 자동으로 인식하고 처리한다.

  1. App Icons (앱 아이콘)

    앱, 브라우저, Apple 기기 등에서 사용되는 아이콘들이다.

    파일명확장자설명
    favicon.ico브라우저 주소창 파비콘
    icon.ico / .jpg / .jpeg / .png / .svg일반 앱 아이콘
    icon.js / .ts / .tsxNext.js가 동적으로 생성하는 앱 아이콘
    apple-icon.jpg / .jpeg / .png애플(iOS) 홈 화면 아이콘
    apple-icon.js / .ts / .tsx동적으로 생성되는 Apple 아이콘
  2. Open Graph & Twitter Images (SNS 공유 이미지)

    SNS에 링크를 공유할 때 보여지는 카드 이미지들이다.

    파일명확장자설명
    opengraph-image.jpg / .jpeg / .png / .gifOG 이미지(정적 파일)
    opengraph-image.js / .ts / .tsxNext.js가 동적으로 생성하는 OG 이미지
    twitter-image.jpg / .jpeg / .png / .gifTwitter 카드 이미지
    twitter-image.js / .ts / .tsx동적으로 생성되는 Twitter 이미지
  3. SEO 파일

    검색 엔진 최적화(SEO)를 위한 필수 파일들이다.

    파일명확장자설명
    sitemap.xml정적 sitemap 파일
    sitemap.js / .tsNext.js가 동적으로 생성하는 sitemap
    robots.txt정적 robots.txt 파일
    robots.js / .ts동적으로 생성되는 robots 파일

2. 프로젝트 구성하기


Next.js는 프로젝트 파일을 어떻게 구성하거나 배치해야 하는지에 대해 특정한 방식을 강요하지 않는다.
다만 프로젝트를 체계적을 구성할 수 있도록 여러 기능을 제공한다.

2-1. 컴포넌트 계층 구조 (Component hierarchy)


Next.js는 특정 파일 이름(layout.js, page.js, error.js 등)이 갖는 역할을 기반으로 컴포넌트를 특정 순서와 규칙에 따라 렌더링한다.

  • 특수 파일들은 “정해진 순서”로 렌더링된다.
  • 중첩된 폴더는 부모 → 자식 순서로 재귀적으로 감싸지며 렌더링된다.
  1. 단일 라우트에서의 렌더링 순서
    • layout.js
    • template.js
    • error.js (React error boundary)
    • loading.js (React suspense boundary)
    • not-found.js (404 전용 ErrorBoundary)
    • page.js 또는 중첩 layout.js

    Component Hierarchy for File Conventions

  2. 중첩된 라우트에서의 렌더링 구조

    폴더가 중첩되면, 자식 라우트의 컴포넌트들은 부모 라우트의 컴포넌트 내부로 재귀적으로 들어간다.

    dashboard에도 layout, error, loading이 있고
    settings에도 layout, error, loading이 있으면 렌더링 구조는 아래 사진과 같다.

    Nested File Conventions Component Hierarchy

2-2. 코로케이션 (Colocation)


Next.js는 폴더 구조로 라우트 구조를 정의한다.

하지만, 폴더가 라우트 되는 것이 아니라, page.js 또는 route.js가 있어야만 라우트가 된다.

즉,
폴더 = URL 세그먼트(segment)
page.js / route.js = URL을 실제로 공개(public)하는 트리거

  1. 폴더만 있어서는 라우트가 아니다

    A diagram showing how a route is not publicly accessible until a page.js or route.js file is added to a route segment.

    → 위 구조만으로는 아래 모든 경로가 라우트가 아니다.

  2. page.js 또는 route.js를 추가하면 라우트가 된다.

    A diagram showing how page.js and route.js files make routes publicly accessible.

    → 이제 아래 모든 경로는 공개된다.

  3. page.js가 있어도, 다른 파일들은 라우트가 아니다.

    A diagram showing colocated project files are not routable even when a segment contains a page.js or route.js file.

결론은, app 폴더 안에 일반 파일을 넣어도 안전하다.

2-3. 비공개 폴더 (Private folders)


폴더 이름 앞에 _를 붙이면 라우팅 시스템에서 완전히 제외된다. _folderName

An example folder structure using private folders

코로케이션만으로도 app 안의 파일은 안전하지만, 비공개 폴더가 유용한 경우가 있다.

  • UI 로직과 라우팅 로직을 명시적으로 분리하고 싶을 때
  • 코드 에디터에서 파일을 그룹화하거나 정렬하고 싶을 때
  • 향후 Next.js 파일 규칙과의 이름 충돌을 방지하고 싶을 때

참고:

URL 세그먼트를 _로 시작하고 싶다면 %5F(언더스코어의 URL 인코딩)로 대신 표현한다.
예: %5FfolderName/_folderName

2-4. 라우트 그룹 (Route groups)


폴더 이름을 괄호로 감싸면 URL 구조에는 영향을 주지 않으면서 코드를 그룹화할 수 있다. (folderName)

An example folder structure using route groups

라우트 그룹이 유용한 경우:

  1. 섹션별 라우트 정리 — 마케팅, 어드민, 쇼핑몰 등 사이트 섹션 또는 팀별로 구분할 때
  2. 그룹별 레이아웃 분리 — 같은 URL 레벨에서 서로 다른 레이아웃을 각 그룹에 적용할 때
  3. 여러 루트 레이아웃 — 최상위 layout.js를 제거하고 각 그룹에 별도의 루트 레이아웃을 둘 때

2-5. src 폴더


Next.js는 애플리케이션 코드 전체(app 포함)를 선택적으로 src/ 폴더 안에 넣는 것을 지원한다.
이렇게 하면 애플리케이션 코드프로젝트 루트의 설정 파일(next.config.js, package.json 등)을 명확하게 분리할 수 있다.

An example folder structure with the src folder

3. 프로젝트 파일 구성 전략


Next.js는 유연한 폴더 구조를 지원한다.
팀과 프로젝트에 맞는 방식을 선택하고, 일관성 있게 유지하는 것이 핵심이다.

components, lib 등의 폴더명은 예시일 뿐 프레임워크에서 특별한 의미가 없다. ui, utils, hooks, styles 등 원하는 이름을 자유롭게 사용할 수 있다.

3-1. app 외부에 보관


모든 공유 코드를 프로젝트 루트에 두고, app/은 오직 라우팅만 담당하는 방식이다.

An example folder structure with project files outside of app

3-2. app 내부에 보관


모든 공유 코드를 app/ 디렉토리 안에 함께 두는 방식이다.

An example folder structure with project files inside app

3-3. 기능/라우트별 분리


전역 공유 코드는 app/ 루트에, 특정 라우트에서만 쓰이는 코드는 해당 세그먼트 안에 두는 방식이다.

An example folder structure with project files split by feature or route

3-4. 라우트 그룹 활용 패턴


라우트 그룹 (folder)을 활용하면 URL 구조를 바꾸지 않고 다양한 레이아웃 패턴을 구현할 수 있다.

  1. URL에 영향 없이 라우트 그룹화

    관련 라우트를 괄호 폴더로 묶으면 URL에서 폴더명이 제외된다.
    각 그룹 안에 layout.js를 추가하면 그룹마다 다른 레이아웃을 적용할 수 있다.

    Organizing Routes with Route Groups Route Groups with Multiple Layouts

  2. 특정 라우트에만 레이아웃 적용

    레이아웃을 공유할 라우트(예: account, cart)는 라우트 그룹 (예: (shop)) 안으로 이동하고, 공유하지 않을 라우트(예: checkout)는 그룹 밖에 두면 된다.

    Route Groups with Opt-in Layouts

  3. 특정 라우트에만 로딩 스켈레톤 적용

    loading.tsx를 라우트 그룹 안에 넣으면 해당 그룹의 페이지에만 적용된다.
    URL 경로 구조에는 영향을 주지 않는다.

    Folder structure showing a loading.tsx and a page.tsx inside the route group

  4. 여러 루트 레이아웃 만들기

    최상위 layout.js를 제거하고 각 라우트 그룹 안에 layout.js를 추가하면, 섹션마다 완전히 다른 UI 구조를 가질 수 있다.
    이때 각 루트 레이아웃에는 반드시 <html><body> 태그가 포함되어야 한다.

    Route Groups with Multiple Root Layouts

This post is licensed under CC BY 4.0 by the author.