프로젝트 구조와 구성

프로젝트 구조와 구성 (Project Structure and Organization)

Next.js에는 폴더와 파일이 라우트와 특별한 역할을 갖게 하는 컨벤션(convention) 이 있어요. 이 글에서는 App Router 기준 Next.js의 폴더·파일 컨벤션 전체를 훑어보고, 프로젝트를 어떻게 구성하면 좋은지 권장 사항을 살펴볼게요.

출처: Next.js 공식 문서 - Project Structure

폴더·파일 컨벤션

최상위 폴더

최상위 폴더는 애플리케이션 코드와 정적 자산을 정리하는 데 쓰여요.

폴더 역할
app App Router
pages Pages Router
public 서빙할 정적 자산
src 선택적인 애플리케이션 소스 폴더

최상위 파일

최상위 파일은 앱 설정, 의존성 관리, 프록시 실행, 모니터링 통합, 환경 변수 정의 등에 쓰여요.

파일 역할
next.config.js Next.js 설정 파일
package.json 프로젝트 의존성과 스크립트
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 TypeScript 설정 파일
jsconfig.json JavaScript 설정 파일

라우팅 파일 (Routing Files)

라우트를 노출하려면 page, 공유 UI(헤더·내비·푸터)에는 layout, 스켈레톤에는 loading, 에러 경계에는 error, API에는 route 파일을 사용해요.

파일 확장자 역할
layout .js .jsx .tsx Layout
page .js .jsx .tsx Page
loading .js .jsx .tsx Loading UI
not-found .js .jsx .tsx Not found UI
error .js .jsx .tsx Error UI
global-error .js .jsx .tsx Global error UI
route .js .ts API 엔드포인트
template .js .jsx .tsx 다시 렌더링되는 레이아웃
default .js .jsx .tsx 병렬 라우트 폴백 페이지

중첩 라우트 (Nested Routes)

폴더가 URL 세그먼트를 정의하고, 폴더를 중첩하면 세그먼트도 중첩돼요. 어느 레벨이든 레이아웃이 하위 세그먼트를 감싸고, pageroute 파일이 있으면 라우트가 공개돼요.

경로 URL 패턴 비고
app/layout.tsx 루트 레이아웃이 모든 라우트를 감쌈
app/blog/layout.tsx /blog와 하위를 감쌈
app/page.tsx / 공개 라우트
app/blog/page.tsx /blog 공개 라우트
app/blog/authors/page.tsx /blog/authors 공개 라우트

동적 라우트 (Dynamic Routes)

세그먼트를 대괄호로 감싸 파라미터화해요. 단일 파라미터는 [segment], catch-all은 [...segment], 선택적 catch-all은 [[...segment]]를 써요. 값은 params prop으로 접근해요.

경로 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

라우트 그룹과 프라이빗 폴더

라우트 그룹 (group)으로 URL을 바꾸지 않고 코드를 정리하고, 프라이빗 폴더 _folder로 라우팅되지 않는 파일을 함께 둘 수 있어요.

경로 URL 패턴 비고
app/(marketing)/page.tsx / 그룹은 URL에서 생략됨
app/(shop)/cart/page.tsx /cart (shop) 안에서 레이아웃 공유
app/blog/_components/Post.tsx 라우팅되지 않음 — UI 유틸의 안전한 자리
app/blog/_lib/data.ts 라우팅되지 않음 — 유틸의 안전한 자리

프로젝트 구성하기

Next.js는 프로젝트 파일을 어떻게 구성하고 콜로케이션(colocate)할지에 대해 의견이 없는(unopinionated) 편이지만, 구성을 돕는 여러 기능을 제공해요.

컴포넌트 계층 (Component Hierarchy)

특별 파일에 정의된 컴포넌트는 일정한 계층으로 렌더링돼요.

  • layout.js
  • template.js
  • error.js (React 에러 경계)
  • loading.js (React suspense 경계)
  • not-found.js ("not found" UI용 React 에러 경계)
  • page.js 또는 중첩된 layout.js

컴포넌트는 중첩 라우트에서 재귀적으로 렌더링돼요. 즉, 한 라우트 세그먼트의 컴포넌트는 부모 세그먼트의 컴포넌트 안에 중첩돼요.

콜로케이션 (Colocation)

app 폴더에서 중첩 폴더는 라우트 구조를 정의해요. 각 폴더는 URL 경로의 해당 세그먼트에 매핑되는 라우트 세그먼트예요.

다만 라우트 구조가 폴더로 정의돼도, 라우트 세그먼트에 page.jsroute.js 파일을 추가하기 전까지 라우트는 공개되지 않아요. 게다가 라우트가 공개돼도 page.js/route.js반환하는 콘텐츠만 클라이언트로 보내져요.

즉, 프로젝트 파일app 폴더 안의 라우트 세그먼트에 안전하게 콜로케이션해도 실수로 라우팅되지 않아요. 물론 app 안에 파일을 콜로케이션할 수는 있지만 반드시 그럴 필요는 없어요. 원하면 app 폴더 밖에 둘 수도 있어요.

프라이빗 폴더 (Private Folders)

폴더 이름 앞에 밑줄을 붙이면(_folderName) 프라이빗 폴더가 돼요. 이것은 그 폴더가 프라이빗 구현 세부 사항이고 라우팅 시스템이 고려하지 않아야 함을 뜻하며, 그 폴더와 모든 하위 폴더를 라우팅에서 제외시켜요.

app 폴더의 파일은 기본적으로 안전하게 콜로케이션되므로, 프라이빗 폴더가 콜로케이션에 필수는 아니에요. 하지만 UI 로직과 라우팅 로직 분리, 프로젝트·Next.js 생태계 전반의 내부 파일 일관된 구성, 코드 편집기에서 정렬·그룹화, 미래의 Next.js 파일 컨벤션과의 이름 충돌 방지에 유용해요.

참고로 URL이 밑줄로 시작하는 세그먼트를 만들고 싶으면 %5F(밑줄의 URL 인코딩)로 시작하는 폴더명(%5FfolderName)을 쓰면 돼요.

라우트 그룹 (Route Groups)

폴더를 괄호로 감싸면((folderName)) 라우트 그룹이 돼요. 이것은 그 폴더가 조직 목적이며 라우트 URL 경로에 포함되지 않아야 함을 뜻해요.

라우트 그룹은 이런 곳에 유용해요.

  • 사이트 섹션·의도·팀별로 라우트 정리 (예: 마케팅 페이지, 관리자 페이지)
  • 같은 라우트 세그먼트 레벨에서 중첩 레이아웃 활성화:
    • 같은 세그먼트에서 여러 중첩 레이아웃(여러 루트 레이아웃 포함) 만들기
    • 공통 세그먼트의 일부 라우트에만 레이아웃 적용하기

src 폴더

Next.js는 애플리케이션 코드(app 포함)를 선택적인 src 폴더에 저장하는 걸 지원해요. 이렇게 하면 대부분 프로젝트 루트에 있는 설정 파일과 애플리케이션 코드를 분리할 수 있어요.

조직 전략 예시

공통 전략 몇 가지를 간단히 소개할게요. 결론은 팀과 자신에게 맞는 전략을 고르고 프로젝트 전반에 일관되게 쓰라는 거예요. 아래 예시에서 components·lib 폴더는 일반화된 자리 표시자일 뿐, 그 이름이 특별한 프레임워크 의미를 갖는 건 아니에요. 프로젝트에 따라 ui, utils, hooks, styles 같은 폴더를 써도 무방해요.

  • app 밖에 프로젝트 파일 두기 — 모든 앱 코드를 프로젝트 루트의 공유 폴더에 두고, app 폴더는 라우팅 전용으로 유지.
  • app 안의 최상위 폴더에 두기 — 모든 앱 코드를 app 폴더 루트의 공유 폴더에 저장.
  • 기능·라우트별로 분리하기 — 전역 공유 코드는 루트 app에 두고, 더 특화된 코드는 그것을 쓰는 라우트 세그먼트로 분리.
  • URL 경로를 바꾸지 않고 라우트 정리하기 — 괄호의 폴더(예: (marketing), (shop))는 URL에서 생략돼요. 그룹 안에 layout.js를 추가하면 같은 URL 계층을 공유하면서도 그룹마다 다른 레이아웃을 만들 수 있어요.
  • 특정 세그먼트에 레이아웃 적용하기 — 같은 레이아웃을 공유할 라우트를 새 라우트 그룹(예: (shop))으로 묶고, 그룹 밖의 라우트(예: checkout)는 레이아웃을 공유하지 않게 해요.
  • 특정 라우트에 로딩 스켈레톤 적용하기 — 새 라우트 그룹(예: /(overview))을 만들고 loading.tsx를 그 안으로 옮기면, URL 구조를 바꾸지 않고 해당 라우트에만 loading.js가 적용돼요.
  • 여러 루트 레이아웃 만들기 — 최상위 layout.js를 제거하고 각 라우트 그룹 안에 layout.js를 추가하면, 완전히 다른 UI·경험을 갖는 섹션으로 앱을 분할할 수 있어요. 각 루트 레이아웃에 <html>·<body> 태그를 넣어야 해요.

더 알아보기 (Learn more)