이 페이지에서는 Next.js의 모든 폴더 및 파일 규칙에 대한 전반적인 개요를 살펴보고, 여러분의 프로젝트를 어떻게 구성하면 좋을지 권장 사항들을 알려드릴 거예요.
👨🏫 강사의 팁: > Next.js는 '파일 기반 라우팅(File-based Routing)'이라는 아주 강력한 특징을 가지고 있어요. 즉, 우리가 폴더와 파일을 어떻게 만들고 이름을 짓느냐에 따라 실제 웹사이트의 주소(URL)가 결정된다는 뜻이죠. 그래서 이 규칙들을 잘 알아두는 것이 Next.js 정복의 첫걸음입니다!
최상위 폴더들은 여러분의 애플리케이션 코드와 정적 자산(이미지, 폰트 등)을 체계적으로 정리하는 데 사용됩니다.

| 폴더명 | 설명 |
|---|---|
app | 앱 라우터 (App Router) 용 폴더입니다. |
pages | 페이지 라우터 (Pages Router) 용 폴더입니다. |
public | 서비스에 제공될 정적 자산(이미지 등)을 보관합니다. |
src | 애플리케이션 소스 코드를 담는 선택적(Optional) 폴더입니다. |
💡 강사의 부연 설명:
현재 Next.js는app디렉토리를 사용하는 앱 라우터(App Router) 방식을 강력하게 권장하고 있어요.pages는 과거 버전에서 쓰던 방식인데 하위 호환성을 위해 남겨둔 것이랍니다. 우리는 최신 기술인app디렉토리 위주로 학습할 거예요!
그리고src폴더는 선택사항이라고 되어있지만, 실무에서는src/app형태로 사용하는 것을 강력히 추천합니다. 설정 파일들과 소스 코드를 분리할 수 있어서 프로젝트가 훨씬 깔끔해지거든요.
최상위 파일들은 보통 프로젝트 설정, 의존성 패키지 관리, 프록시 실행, 모니터링 도구 연동, 그리고 환경 변수 정의 등을 위해 사용돼요.
| 파일명 | 설명 |
|---|---|
| Next.js 전용 설정 파일들 | |
next.config.js | Next.js의 핵심 설정 파일이에요. |
package.json | 프로젝트의 의존성(설치된 패키지들)과 실행 스크립트를 관리해요. |
instrumentation.ts | OpenTelemetry 및 모니터링 연동을 위한 파일이에요. |
proxy.ts | Next.js의 요청 프록시 설정 파일이에요. |
.env | 모든 환경에 공통으로 쓰이는 환경 변수 (버전 관리(Git)에 올리면 안 돼요!) |
.env.local | 로컬 개발 환경을 위한 환경 변수 (버전 관리에 올리면 안 돼요!) |
.env.production | 실제 배포(Production) 환경을 위한 환경 변수 (버전 관리에 올리면 안 돼요!) |
.env.development | 개발(Development) 환경을 위한 환경 변수 (버전 관리에 올리면 안 돼요!) |
eslint.config.mjs | 코드 컨벤션을 검사하는 ESLint 설정 파일이에요. |
.gitignore | Git에서 추적하지 않고 무시할 파일/폴더들을 지정해요. |
next-env.d.ts | Next.js를 위한 TypeScript 선언 파일이에요. (이 파일은 직접 수정하거나 Git에 올리지 않아요) |
tsconfig.json | TypeScript 설정 파일이에요. |
jsconfig.json | JavaScript 설정 파일이에요. |
👨🏫 강사의 팁: >
.env파일들 옆에 괄호로 "버전 관리에 올리면 안 된다"라고 적힌 거 보이시죠? 이곳에는 주로 데이터베이스 비밀번호나 API 키 같은 민감한 정보가 들어가기 때문에 절대 GitHub 같은 공개 저장소에 푸시(Push)하시면 안 됩니다!.gitignore파일에 반드시.env*를 추가해 주세요.
화면에 특정 경로(Route)를 띄워주려면 page 파일을 추가하세요. 헤더나 내비게이션바, 푸터 같은 공통 UI를 위해서는 layout 파일을, 로딩 중 보여줄 스켈레톤 화면에는 loading 파일을, 에러 처리를 위해서는 error 파일을, 그리고 백엔드 API를 만들고 싶다면 route 파일을 사용하면 됩니다.
| 파일명 | 확장자 | 설명 |
|---|---|---|
layout | .js .jsx .tsx | 공통 레이아웃 (Layout) |
page | .js .jsx .tsx | 실제 화면에 보여지는 페이지 (Page) |
loading | .js .jsx .tsx | 로딩 중 보여줄 UI (Loading UI) |
not-found | .js .jsx .tsx | 404 페이지 (Not found UI) |
error | .js .jsx .tsx | 에러 발생 시 보여줄 UI (Error UI) |
global-error | .js .jsx .tsx | 앱 전체에서 발생한 심각한 에러 UI |
route | .js .ts | API 엔드포인트 (백엔드 역할) |
template | .js .jsx .tsx | 상태가 초기화되며 다시 렌더링되는 레이아웃 |
default | .js .jsx .tsx | 병렬 라우트(Parallel route)에서 대체될 기본 페이지 |
💡 강사의 부연 설명:
방금 보신 파일 이름들(page,layout,loading등)은 Next.js에서 미리 약속한 특수한 예약어예요. 파일 이름을 반드시 이렇게 지어주셔야 프레임워크가 알아듣고 동작합니다. 참고로 여러분이 타입스크립트를 배우셨으니, 실무에서는 주로.tsx확장자를 사용하게 되실 거예요!
폴더 이름이 곧 URL 경로(segment)가 됩니다. 폴더 안에 폴더를 중첩해서 만들면 URL 경로도 중첩돼요. 어느 깊이(level)에 있든 layout 파일은 자신의 하위 경로들을 감싸게 됩니다. 그리고 해당 폴더 안에 page 파일이나 route 파일이 존재할 때만, 비로소 사용자가 접속할 수 있는 공개적인(Public) 경로가 만들어져요.
| 파일 경로 (Path) | URL 패턴 (주소) | 참고 사항 (Notes) |
|---|---|---|
app/layout.tsx | — | 앱 전체를 감싸는 최상위 루트 레이아웃이에요. |
app/blog/layout.tsx | — | /blog 주소와 그 하위 경로들을 감싸는 레이아웃이에요. |
app/page.tsx | / | 메인 홈 화면 경로예요. |
app/blog/page.tsx | /blog | 블로그 메인 화면 경로예요. |
app/blog/authors/page.tsx | /blog/authors | 블로그 작성자 목록 화면 경로예요. |
경로의 일부를 변수처럼 다루고 싶다면 대괄호([])를 사용해 보세요. 단일 파라미터는 [segment]로, 여러 경로를 전부 다 잡으려면 [...segment](Catch-all)로, 있어도 되고 없어도 되는 선택적 경로는 [[...segment]](Optional catch-all)로 표시합니다. 여기서 받아온 값들은 페이지 컴포넌트의 params 프롭(prop)을 통해 접근할 수 있어요.
| 파일 경로 (Path) | 매칭되는 URL 패턴 예시 |
|---|---|
app/blog/[slug]/page.tsx | /blog/my-first-post, /blog/react-guide |
app/shop/[...slug]/page.tsx | /shop/clothing, /shop/clothing/shirts 등 하위 경로 전체 다! |
app/docs/[[...slug]]/page.tsx | /docs (없어도 됨), /docs/layouts-and-pages, /docs/api/use-router |
👨🏫 강사의 팁: > 동적 라우팅은 쇼핑몰의 상품 상세 페이지(
/product/1,/product/2)나 블로그 포스팅 상세 페이지를 만들 때 필수적으로 사용하는 기법입니다. 대괄호 안에 들어간 이름(예:slug,id)이 나중에 코드 안에서 변수명이 된다고 생각하시면 이해하기 쉬워요.
URL 주소를 바꾸지 않으면서 내부적으로 코드를 깔끔하게 묶고 싶다면 괄호를 사용하는 라우트 그룹 (group)을 사용하세요. 그리고 URL로 접근할 수 없도록 파일을 라우팅 시스템에서 완전히 제외시키고 싶다면 언더스코어를 사용하는 프라이빗 폴더 _folder를 사용하면 됩니다.
| 파일 경로 (Path) | URL 패턴 | 참고 사항 (Notes) |
|---|---|---|
app/(marketing)/page.tsx | / | 그룹 폴더명 (marketing)은 실제 URL에서 생략됩니다! |
app/(shop)/cart/page.tsx | /cart | (shop) 이라는 목적에 맞게 레이아웃을 공유할 때 유용해요. |
app/blog/_components/Post.tsx | — | 라우팅 대상이 아닙니다. UI 컴포넌트들을 모아두기 좋은 안전한 곳이죠. |
app/blog/_lib/data.ts | — | 라우팅 대상이 아닙니다. 유틸리티 함수들을 두기 좋아요. |
💡 강사의 부연 설명:
라우트 그룹(폴더명)은 정말 유용해요! 예를 들어 로그인, 회원가입 페이지는 동일한 디자인(레이아웃)을 사용하겠죠? 이들을 묶기 위해app/(auth)/login,app/(auth)/signup이렇게 만들 수 있습니다. URL은/auth/login이 아니라 깔끔하게/login으로 나오면서도, 개발자는 코드를 묶어서 관리할 수 있게 해주는 아주 똑똑한 기능입니다.
이 기능들은 슬롯 기반의 레이아웃이나, 모달 창(Modal) 상태에서 라우팅을 처리하는 등 조금 더 특별하고 구체적인 UI 패턴을 구현할 때 사용합니다.
상위 레이아웃에서 이름이 지정된 특정 영역(슬롯)을 렌더링하려면 @slot 문법을 사용하세요. 그리고 URL 주소를 변경하지 않고 현재 레이아웃 안에서 다른 경로의 화면을 띄워주고 싶을 때는 가로채기(Intercept) 패턴을 사용합니다. 예를 들어, 사진 목록에서 사진을 클릭했을 때 페이지 이동 없이 현재 화면 위에 모달창으로 상세 사진을 보여주는 경우에 아주 유용해요.
| 패턴 (공식문서 링크) | 의미 | 대표적인 사용 사례 (Typical use case) |
|---|---|---|
@folder | 이름이 지정된 슬롯 | 사이드바 + 메인 콘텐츠를 한 화면에 렌더링할 때 |
(.)folder | 같은 레벨 가로채기 | 모달 창으로 형제 경로의 화면을 미리 보여줄 때 |
(..)folder | 부모 레벨 가로채기 | 현재 화면 위에 부모의 자식 경로를 오버레이로 띄울 때 |
(..)(..)folder | 두 단계 위 가로채기 | 깊게 중첩된 구조에서 오버레이 띄울 때 |
(...)folder | 최상위 루트부터 가로채기 | 현재 뷰에서 전혀 다른 경로의 화면을 띄워줄 때 |
👨🏫 강사의 팁: > 이 부분은 Next.js에서도 상당히 고급 기술(Advanced)에 속합니다. 인스타그램이나 핀터레스트에서 사진을 눌렀을 때, 배경 페이지는 그대로 있으면서 주소창만 바뀌고 모달이 뜨는 걸 보신 적 있죠? 바로 그걸 구현하는 기술이에요. 처음 공부하실 때는 '아, 이런 것도 가능하구나' 하고 가볍게 넘어가셔도 괜찮습니다!
| 규칙 (문서 링크) | 확장자 | 설명 |
|---|---|---|
favicon | .ico | 웹 브라우저 탭에 들어가는 작은 아이콘(파비콘) 파일이에요. |
icon | .ico .jpg .jpeg .png .svg | 앱 아이콘 이미지 파일이에요. |
icon | .js .ts .tsx | 코드로 동적 생성하는 앱 아이콘이에요. |
apple-icon | .jpg .jpeg, .png | 애플 기기(iOS 등)를 위한 앱 아이콘 파일이에요. |
apple-icon | .js .ts .tsx | 코드로 동적 생성하는 애플 앱 아이콘이에요. |
| 규칙 (문서 링크) | 확장자 | 설명 |
|---|---|---|
opengraph-image | .jpg .jpeg .png .gif | 카카오톡 등에서 링크 공유 시 보이는 대표 이미지 파일이에요. |
opengraph-image | .js .ts .tsx | 코드로 동적 생성하는 오픈 그래프 이미지예요. |
twitter-image | .jpg .jpeg .png .gif | 트위터 공유 시 보이는 대표 이미지 파일이에요. |
twitter-image | .js .ts .tsx | 코드로 동적 생성하는 트위터 이미지예요. |
| 규칙 (문서 링크) | 확장자 | 설명 |
|---|---|---|
sitemap | .xml | 검색 엔진에게 우리 사이트의 지도(사이트맵)를 제공하는 파일이에요. |
sitemap | .js .ts | 코드로 동적 생성하는 사이트맵이에요. |
robots | .txt | 검색 엔진 봇(크롤러)에게 접근 권한을 알려주는 robots 파일이에요. |
robots | .js .ts | 코드로 동적 생성하는 robots 파일이에요. |
Next.js는 프로젝트 파일들을 어디에 배치하고 어떻게 구성할지에 대해 특정한 방식을 강요하지 않습니다 (unopinionated). 하지만 여러분이 프로젝트를 잘 구성할 수 있도록 돕는 몇 가지 아주 훌륭한 기능들을 제공하고 있어요.
특수한 규칙을 가진 파일들에 정의된 컴포넌트들은 다음과 같이 아주 정해진 계층(순서)에 따라 화면에 렌더링됩니다.
layout.js (가장 바깥쪽을 감쌈)template.jserror.js (React의 Error Boundary 역할)loading.js (React의 Suspense Boundary 역할)not-found.js (존재하지 않는 페이지일 때 보여줄 React Error Boundary)page.js 또는 중첩된(nested) 하위 layout.js (가장 안쪽에 위치)
이 컴포넌트들은 중첩된 라우트 구조에서 재귀적으로 렌더링돼요. 쉽게 말해, 현재 경로의 컴포넌트들은 항상 상위(부모) 경로의 컴포넌트들 '안쪽(inside)'에 중첩되어 들어간다는 뜻입니다.

💡 강사의 부연 설명:
위 설명이 조금 복잡하게 들릴 수 있는데요, 그림을 보면 이해가 쉽습니다! 에러가 나거나(error.js) 데이터를 불러오는 중일 때(loading.js), 이 상태들이 레이아웃 안쪽에서만 일어나기 때문에 헤더나 푸터 같은 공통 레이아웃은 그대로 유지되면서 부드러운 사용자 경험을 줄 수 있다는 것이 핵심이에요.
app 디렉토리 안에서는 중첩된 폴더들이 곧 라우트(경로) 구조를 정의해요. 각 폴더는 URL 주소의 한 부분에 대응하는 하나의 경로 조각(segment)이 되는 거죠.
하지만 명심하세요! 폴더를 통해 구조를 잡았다고 하더라도, 해당 폴더 안에 page.js 또는 route.js 파일이 추가되기 전까지는 이 경로는 외부에서 절대 접근할 수 없습니다 (not publicly accessible).

게다가 경로가 공개적으로 접근 가능해졌다고 하더라도, 브라우저(클라이언트)로 전송되는 데이터는 오직 page.js나 route.js가 반환(return)한 콘텐츠뿐입니다.

이게 무슨 뜻이냐고요? 바로 우리의 프로젝트 파일(컴포넌트, 스타일, 테스트 파일 등)들을 app 디렉토리 안의 경로 폴더들 안에 안심하고 함께 둬도(colocated) 된다는 뜻이에요. 실수로 라우팅되어 화면에 노출될 위험이 전혀 없거든요!

알아두면 좋은 팁 (Good to know): 프로젝트 파일들을
app폴더 안에 함께 둘 수 있다는 거지, 반드시 그래야만 하는 것은 아닙니다. 만약 기존 방식이 더 편하다면 프로젝트 파일들을app디렉토리 바깥에 따로 보관하셔도 전혀 문제없습니다.
👨🏫 강사의 팁: > 이건 과거
pages디렉토리 시절과 비교하면 엄청난 발전이에요! 예전에는 컴포넌트 파일을 라우팅 폴더 안에 두면 그 컴포넌트가 그대로 페이지가 되어버리는 대참사가 종종 있었거든요. 이제는page.tsx라는 이름만 아니면 무조건 안전합니다.
폴더 이름 앞에 언더스코어(_)를 붙이면 프라이빗 폴더를 만들 수 있어요. 예: _folderName
이렇게 하면 Next.js 라우팅 시스템에게 "이 폴더는 내가 내부 구현용으로 따로 쓰는 거니까 라우팅할 때 무시해 줘!"라고 알려주는 것과 같습니다. 결과적으로 해당 폴더와 그 안의 모든 하위 폴더들은 라우팅 대상에서 완전히 제외됩니다.

방금 배웠듯이 app 디렉토리 안의 파일들은 기본적으로 안전하게 함께 둘 수 있기 때문에, 컴포넌트를 보관하기 위해 굳이 프라이빗 폴더를 필수로 써야 하는 건 아니에요. 하지만 다음과 같은 상황에서 아주 유용하게 쓰일 수 있습니다:
알아두면 좋은 팁 (Good to know):
- 프레임워크가 강요하는 규칙은 아니지만, 프라이빗 폴더 바깥에 있는 일반 파일들도 똑같이 이름 앞에 언더스코어(
_)를 붙여서 개발자들끼리 "이건 프라이빗한 파일이야"라고 표시하는 패턴을 고려해 볼 수도 있습니다.- 만약 의도적으로 언더스코어(
_)로 시작하는 URL 주소를 만들고 싶다면, 폴더 이름 앞에%5F(언더스코어의 URL 인코딩 문자)를 붙여주시면 됩니다:%5FfolderName.- 프라이빗 폴더를 사용하지 않으실 거라면, 뜻하지 않은 이름 충돌을 피하기 위해 Next.js의 특수 파일 규칙 (예:
page,layout등)을 꼼꼼히 알아두시는 것이 좋습니다.
폴더 이름을 괄호로 감싸면 라우트 그룹을 만들 수 있어요: (folderName)
이 폴더는 오직 코드들을 깔끔하게 '정리하기 위한 목적'으로만 존재하며, 실제 생성되는 라우트의 URL 주소에는 절대 포함되지 않습니다.

라우트 그룹은 이럴 때 강력합니다:
src 폴더 (src folder)Next.js는 애플리케이션의 핵심 코드들(당연히 app 폴더 포함)을 선택 사항인 src 폴더 안에 보관하는 구조를 완벽하게 지원합니다. 이렇게 하면 프로젝트 최상단(Root)에 복잡하게 널브러져 있는 여러 설정 파일들(Config files)과 진짜 개발 소스 코드를 아주 깔끔하게 분리할 수 있어요.

👨🏫 강사의 팁: > 개인적으로 개발자분들께
src폴더 사용을 적극 추천합니다! 프로젝트 규모가 커질수록.prettierrc,eslint.config,tailwind.config등 수많은 설정 파일이 최상단에 쌓이게 되는데,src폴더 하나로 소스 코드를 격리해두면 마음까지 편안해진답니다.
아래 섹션에서는 실무에서 많이 쓰이는 대표적인 구조 전략들을 가볍게 살펴볼 거예요. 가장 중요한 핵심은 여러분과 여러분의 팀에게 가장 잘 맞는 전략을 딱 하나 선택하고, 전체 프로젝트에 걸쳐서 일관성 있게 유지하는 것입니다.
알아두면 좋은 팁 (Good to know): 아래 예시들에서 볼 수 있는
components나lib같은 폴더명은 그냥 일반적인 예시(Placeholder)일 뿐입니다. 프레임워크가 강제하는 특별한 의미는 전혀 없으니, 여러분의 프로젝트 성향에 맞춰ui,utils,hooks,styles등 원하는 이름으로 자유롭게 지어 쓰시면 됩니다.
app 외부에 프로젝트 파일 저장하기이 전략은 진짜 화면을 그리는 라우팅 로직만 app 디렉토리 안에 남겨두고, 컴포넌트나 유틸리티 같은 모든 애플리케이션 코드는 프로젝트 최상위(Root)에 만들어둔 별도의 공유 폴더들에 저장하는 방식입니다.

app 내부 최상단에 프로젝트 파일 저장하기이 전략은 반대로 모든 애플리케이션 코드를 공유 폴더에 담아 app 디렉토리 안쪽의 최상단에 위치시키는 방식이에요. (app/components/, app/lib/ 이런 식이죠)

글로벌하게 공통으로 쓰이는 코드들만 최상단 app에 두고, 특정 페이지에서만 쓰이는 구체적인 코드들은 해당 페이지를 담당하는 라우트 경로 폴더 안으로 깊숙이 분산(split)시켜 저장하는 방식입니다.

💡 강사의 부연 설명:
이 방식은 보통 'Feature-Sliced Design' 또는 관심사 분리 관점에서 아주 훌륭한 패턴이에요.dashboard화면에서만 쓰이는 버튼 컴포넌트는dashboard폴더 안에 두는 거죠. 찾기도 쉽고 관리하기도 아주 수월해집니다.
URL 주소는 그대로 두면서 관련된 페이지 코드들만 예쁘게 묶어서 정리하고 싶다면, 라우트 그룹을 만드세요. 괄호로 묶은 폴더명(예: (marketing) 또는 (shop))은 실제 생성되는 주소(URL)에서 흔적도 없이 사라집니다.

(marketing) 그룹과 (shop) 그룹 안에 있는 라우트들이 비록 URL 상에서는 동일한 계층(root / 바로 아래)에 있게 되더라도, 각각의 그룹 폴더 안에 layout.js를 따로 만들어주면 그룹별로 완전히 다른 레이아웃 디자인을 입혀줄 수 있습니다!

여러 페이지 중에서 공통 레이아웃이 필요한 특정 라우트들만 골라내고 싶다면, 새로운 라우트 그룹(예: (shop))을 만든 뒤 같은 레이아웃을 공유할 경로들(예: account, cart)을 그 그룹 폴더 안으로 쏙 넣어주세요. 그룹 바깥에 둔 경로(예: checkout)는 그 레이아웃의 영향을 전혀 받지 않게 됩니다.

loading.js 파일을 사용해서 화면이 렌더링되기 전 로딩 스켈레톤(뼈대 화면)을 보여줄 때, 특정한 경로에만 이를 적용하고 싶다면 새로운 라우트 그룹(예: /(overview))을 만드세요. 그리고 그 라우트 그룹 안으로 loading.tsx 파일을 옮기면 됩니다.

이제 이 loading.tsx 파일은 URL 구조를 전혀 해치지 않으면서도, 대시보드의 수많은 페이지 중에서 오직 대시보드 → 개요(overview) 페이지가 뜰 때만 부드럽게 작동할 거예요.
때로는 최상위 루트 레이아웃(Root layout) 자체를 여러 개로 나누고 싶을 때가 있습니다. 그럴 때는 앱 전체를 감싸고 있던 가장 상위의 app/layout.js 파일을 과감히 지워버리고, 각 라우트 그룹 폴더 안에 각각의 layout.js를 새롭게 추가해 주세요.
이 방식은 하나의 애플리케이션 안에서 완전히 다른 형태의 UI나 완전히 다른 사용자 경험(UX)을 제공해야 하는 섹션들을 분리할 때 매우 유용합니다. (주의: 이 경우 각각의 루트 레이아웃 파일 안에 <html> 태그와 <body> 태그를 반드시 직접 넣어주셔야 해요!)

위 다이어그램 예시를 보시면, (marketing) 파트와 (shop) 파트가 서로 남남처럼 각자만의 고유한 루트 레이아웃을 가지게 된 것을 보실 수 있습니다.
문서의 전체 의미론적 개요(Semantic overview)를 확인하고 싶으시다면, https://nextjs.org/docs/sitemap.md 를 참고해 주세요.
제공되는 모든 공식 문서의 전체 색인(Index)을 확인하시려면, https://nextjs.org/docs/llms.txt 를 참고해 주세요.