THE DEVLOG

scribbly.

Next JS 치트 시트

2025.05.10 18:28:56

Routing 파일

각 Route Segment별로 아래와 같은 파일들을 정의할 수 있다.

파일 이름확장자역할 설명
layout.js, .jsx, .tsx공통 UI 레이아웃 정의. 현재 디렉토리와 하위 경로에 공통 적용됨. 헤더, 사이드바, 레이아웃 컴포넌트 정의에 사용됨
page.js, .jsx, .tsx해당 경로의 실제 페이지. 사용자 요청 시 이 파일이 SSR되어 보여짐
loading.js, .jsx, .tsxSuspense 기반의 로딩 UI. 해당 경로의 데이터를 fetch하는 동안 보여짐
not-found.js, .jsx, .tsx404 UI. 페이지를 찾을 수 없을 때 렌더링됨
error.js, .jsx, .tsx경로별 에러 UI. 이 경로에서 발생한 에러를 잡아 렌더링함
global-error.js, .jsx, .tsx앱 전체에서 잡히지 않은 에러 처리 UI. 보통 루트 디렉토리 app/ 하위에 위치
route.js, .tsAPI Route. 서버에서 실행되는 핸들러 함수 (GET, POST 등). app/api/**/route.ts 식으로 사용
template.js, .jsx, .tsx재사용 안되는 개별 레이아웃. 페이지 간 이동할 때 새로 마운트되며 상태가 초기화됨
default.js, .jsx, .tsx병렬 라우트의 기본 fallback. 조건에 따라 보여줄 분기 UI 중 기본값 역할
  • layout: 공통 UI 구조
  • page: 페이지 진입점
  • loading: fetch 중 로딩 상태
  • not-found: 404 처리
  • error: 해당 경로 에러 처리
  • global-error: 앱 전체 에러 처리
  • route: API 요청 처리
  • template: 상태 초기화가 필요한 레이아웃
  • default: 병렬 라우트의 기본 대체 UI

Route Segment의 파일들은 nested된 하위 Route Segement에도 영향을 준다.

layout.tsx: 공통 레이아웃을 정의한다

layout.tsx 파일은 그 디렉토리와 하위 디렉토리의 모든 페이지에 대해 공통적으로 적용되는 UI 틀(Layout) 을 정의한다. 일반적으로 Header, Footer, Sidebar 등의 공통 구조를 포함시킨다.

  • 예시:

    /app/layout.tsx → 사이트 전체 공통 레이아웃
    /app/blog/layout.tsx → 블로그 페이지 전용 레이아웃
    
  • 예제: 최상위 app/layout.tsx
    // app/layout.tsx
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="ko">
          <body>
            <header>공통 헤더</header>
            <main>{children}</main>
            <footer>공통 푸터</footer>
          </body>
        </html>
      );
    }
    
  • 예제: 블로그 전용 레이아웃 app/blog/layout.tsx
    // app/blog/layout.tsx
    export default function BlogLayout({ children }: { children: React.ReactNode }) {
      return (
        <section style={{ display: "flex" }}>
          <aside style={{ width: 200, background: "#f3f3f3" }}>블로그 사이드바</aside>
          <div style={{ flex: 1 }}>{children}</div>
        </section>
      );
    }
    
  • 특징:

    • children을 반드시 props로 받아야 한다. 이 children 자리에 해당 라우트의 page.tsx가 삽입된다.
    • layout은 클라이언트와 서버 양쪽 모두에서 사용할 수 있으며, React Server Component로 작성할 수 있다.
    • 상위 layout.tsx는 하위 layout.tsx와 중첩되며, 바깥쪽부터 안쪽으로 감싸지는 구조다.

page.tsx: 실제 페이지를 정의한다

page.tsx 파일은 특정 URL에 대응되는 실제 렌더링 대상 페이지를 정의한다. 사용자가 브라우저에서 어떤 경로로 접근할 때, 해당 경로에 일치하는 page.tsx 파일이 실행되어 React 컴포넌트가 클라이언트에게 렌더링된다.

  • 예시:

    /app/about/page.tsx  →  https://도메인/about
    /app/blog/[slug]/page.tsx → https://도메인/blog/abc
    
    
  • 예제: /blog/page.tsx
    // app/blog/page.tsx
    export default function BlogPage() {
      return (
        <main>
          <h1>블로그 메인</h1>
          <p>최근 게시글 목록을 보여줍니다.</p>
        </main>
      );
    }
    
  • 예제: /blog/[slug]/page.tsx
    // app/blog/[slug]/page.tsx
    type PageProps = {
      params: { slug: string };
    };
    
    export default function BlogDetailPage({ params }: PageProps) {
      return (
        <main>
          <h1>{decodeURIComponent(params.slug)} 게시글</h1>
          <p>이곳에 게시글 내용을 렌더링합니다.</p>
        </main>
      );
    }
    
  • 특징:

    • export default로 React 컴포넌트를 반환해야 한다.
    • params, searchParams 등 라우팅 정보를 props로 받을 수 있다.
    • 반드시 page.tsx 파일명이어야 한다. about.tsx처럼 커스텀 파일명은 인식되지 않는다.

layout.tsxpage.tsx의 동작 관계

  • 레이아웃은 한번 렌더링되면 라우트 전환 간에도 유지된다.
  • 반면 페이지는 라우트 변경 시마다 다시 렌더링된다.
  • 따라서 무거운 상태나 네비게이션 요소 등은 layout.tsx에 넣고, 동적으로 바뀌는 콘텐츠는 page.tsx에 배치하는 것이 일반적이다.
/app
 ├── layout.tsx        → 전체 앱 레이아웃 (Header, Footer 포함)
 ├── page.tsx          → / 홈 페이지
 ├── about
 │   └── page.tsx      → /about 페이지
 └── blog
     ├── layout.tsx    → 블로그 영역 전용 레이아웃 (ex. 좌측 사이드바 포함)
     ├── page.tsx      → /blog 페이지
     └── [slug]
         └── page.tsx  → /blog/[slug] 상세 페이지

위 구조에서 /blog/post-1으로 접근하면 다음 순서대로 컴포넌트가 계층적으로 렌더링된다:

app/layout.tsx
 └── blog/layout.tsx
      └── blog/[slug]/page.tsx

기타 Routing 파일

loading.tsx: 로딩 상태 UI를 정의한다

loading.tsx 파일은 해당 경로의 데이터를 가져오는 동안 보여줄 로딩 스켈레톤 컴포넌트를 정의한다. Next.js는 이 파일을 자동으로 React.Suspense의 fallback처럼 처리한다.

  • 예시:

    /app/blog/loading.tsx → /blog 경로의 데이터를 fetch하는 동안 로딩 UI 표시
    
  • 예제:

    // app/blog/loading.tsx
    export default function Loading() {
      return <p>잠시만 기다려 주세요... 블로그 데이터를 불러오는 중입니다.</p>;
    }
    
  • 특징:

    • 서버 컴포넌트 또는 클라이언트 컴포넌트로 작성 가능하다.
    • 해당 경로에서 데이터를 fetch하거나 서버에서 응답이 늦는 경우 자동으로 보여진다.

not-found.tsx: 404 Not Found 페이지 정의

not-found.tsx경로에 해당하는 페이지가 없을 때 렌더링되는 UI다. Next.js에서 제공하는 notFound() 함수를 호출하면 이 UI로 리다이렉션된다.

  • 예시:

    /app/blog/not-found.tsx → 블로그에서 존재하지 않는 slug 접근 시
    
  • 예제:

    // app/blog/not-found.tsx
    export default function BlogNotFound() {
      return <h1>존재하지 않는 블로그 글입니다.</h1>;
    }
    
  • 예제: 페이지에서 수동 호출

    import { notFound } from "next/navigation";
    
    export default function BlogPost({ params }) {
      const post = getPostBySlug(params.slug);
      if (!post) return notFound();
    
      return <div>{post.title}</div>;
    }
    

error.tsx: 경로 단위 에러 UI

error.tsx는 해당 경로 내에서 발생한 런타임 에러를 잡아주는 에러 바운더리 역할을 한다. 특정 디렉토리 내부에서만 적용된다.

  • 예시:

    /app/blog/error.tsx → 블로그 관련 페이지에서만 발생한 에러를 처리
    
  • 예제:

    // app/blog/error.tsx
    "use client";
    
    export default function BlogError({ error }: { error: Error }) {
      return (
        <div>
          <h2>블로그를 불러오는 중 오류가 발생했습니다.</h2>
          <p>{error.message}</p>
        </div>
      );
    }
    
  • 특징:

    • 반드시 "use client" 지시어가 필요하다.
    • error 객체를 prop으로 받을 수 있다.

global-error.tsx: 앱 전역 에러 처리

global-error.tsx루트 디렉토리에 위치하며, error.tsx에서 처리되지 않은 에러를 처리하는 전역 에러 바운더리이다.

  • 예시:

    /app/global-error.tsx
    
  • 예제:

    // app/global-error.tsx
    "use client";
    
    export default function GlobalError({ error }: { error: Error }) {
      return (
        <html>
          <body>
            <h1>앱 전역 오류 발생</h1>
            <p>{error.message}</p>
          </body>
        </html>
      );
    }
    

template.tsx: 새로 마운트되는 일회성 레이아웃

template.tsxlayout.tsx와 유사하지만, 페이지 간 이동 시 상태를 유지하지 않고 새로 마운트된다. 로그인 페이지, 모달처럼 독립적인 컨텍스트가 필요한 경우에 사용된다.

  • 예시:

    /app/(auth)/login/template.tsx
    
  • 예제:

    // app/(auth)/login/template.tsx
    export default function LoginTemplate({ children }: { children: React.ReactNode }) {
      return <div className="login-layout">{children}</div>;
    }
    
  • 특징:

    • 같은 라우트라도 이동할 때마다 다시 렌더링된다 (즉, 상태 초기화).
    • layout과 달리 상태가 유지되지 않는다.

default.tsx: 병렬 라우트의 fallback UI

default.tsx는 병렬 라우트에서 선택적으로 보여질 분기 UI 중 기본값을 정의하는 데 사용된다. route segment를 선택적으로 보여줘야 할 때 유용하다.

  • 예시:

    /app/(tabs)/@details/default.tsx → 기본으로 보여질 탭
    
  • 예제:

    // app/(tabs)/@details/default.tsx
    export default function DetailsDefault() {
      return <div>아무 탭도 선택되지 않았습니다.</div>;
    }
    

예시 폴더구조

app/
├── layout.tsx                # 전체 앱 공통 레이아웃
├── page.tsx                  # 홈 (/)
├── loading.tsx               # 홈 페이지 Suspense 로딩 UI
├── global-error.tsx          # 앱 전체 에러 처리

├── about/
│   ├── page.tsx              # /about 페이지
│   ├── loading.tsx           # /about 로딩 UI
│   └── error.tsx             # /about 에러 처리

├── blog/
│   ├── layout.tsx            # /blog 공통 레이아웃 (ex. 사이드바)
│   ├── page.tsx              # /blog 메인
│   ├── loading.tsx           # /blog 로딩 UI
│   ├── error.tsx             # /blog 전용 에러 UI
│   └── not-found.tsx         # /blog 404 처리

│   └── [slug]/               # /blog/:slug
│       ├── page.tsx          # 블로그 게시글 상세
│       └── not-found.tsx     # 존재하지 않는 게시글 처리

├── (auth)/                   # 병렬 라우트 그룹 (예: 로그인/회원가입 등)
│   └── login/
│       ├── page.tsx          # /login
│       ├── template.tsx      # 로그인 시 상태 초기화용 레이아웃
│       └── error.tsx         # 로그인 에러 처리

├── (tabs)/                   # 병렬 라우트 예시
│   ├── layout.tsx
│   ├── @details/
│   │   ├── page.tsx          # /tabs/@details 탭 내용
│   │   └── default.tsx       # 아무 탭 선택 안 했을 때 fallback
│   └── @settings/
│       └── page.tsx          # /tabs/@settings 탭 내용
<br />