본문 바로가기
Tech Notes

Next.js App Router 실행 원리 완전 가이드

by miracle-tech 2025. 8. 15.
728x90
반응형

Next.js App Router 실행 원리 완전 가이드

🏗️ 1. App Router 아키텍처

파일 시스템 기반 라우팅

src/app/
├── layout.tsx          # 루트 레이아웃
├── page.tsx           # 홈페이지 (/)
├── globals.css        # 전역 스타일
├── loading.tsx        # 로딩 UI
├── error.tsx          # 에러 UI
├── not-found.tsx      # 404 페이지
│
├── about/
│   └── page.tsx       # /about 페이지
│
├── products/
│   ├── page.tsx       # /products 페이지
│   ├── [id]/
│   │   └── page.tsx   # /products/[id] 동적 라우트
│   └── layout.tsx     # products 하위 레이아웃
│
└── api/               # API 라우트
    ├── users/
    │   └── route.ts   # /api/users
    └── body-analysis/
        └── route.ts   # /api/body-analysis

🔄 2. API 라우트 실행 흐름

2.1 기본 구조

// src/app/api/body-analysis/route.ts

import { NextRequest, NextResponse } from 'next/server';

// 각 HTTP 메서드별로 named export
export async function GET(request: NextRequest) {
  return NextResponse.json({ message: 'GET 요청 처리' });
}

export async function POST(request: NextRequest) {
  const body = await request.json();
  return NextResponse.json({ message: 'POST 요청 처리', data: body });
}

export async function PUT(request: NextRequest) {
  return NextResponse.json({ message: 'PUT 요청 처리' });
}

export async function DELETE(request: NextRequest) {
  return NextResponse.json({ message: 'DELETE 요청 처리' });
}

2.2 실행 흐름 상세

클라이언트 요청 (fetch('/api/body-analysis', { method: 'POST' }))
           ↓
1. Next.js 라우터가 URL 패턴 매칭 (/api/body-analysis)
           ↓
2. src/app/api/body-analysis/route.ts 파일 탐색
           ↓
3. HTTP 메서드 확인 (POST)
           ↓
4. export const POST 함수 실행
           ↓
5. 미들웨어 처리 (있다면)
           ↓
6. 함수 로직 실행
           ↓
7. Response 반환
           ↓
클라이언트로 응답 전송

🎯 3. 페이지 렌더링 실행 흐름

3.1 Server Components vs Client Components

Server Components (기본값)

// src/app/users/page.tsx
// 서버에서 실행, 클라이언트로 HTML 전송

async function UsersPage() {
  // 서버에서 직접 데이터베이스 접근 가능
  const users = await getUsersFromDB();
  
  return (
    <div>
      <h1>사용자 목록</h1>
      {users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

export default UsersPage;

Client Components

// src/app/dashboard/page.tsx
'use client'; // 클라이언트 컴포넌트 선언

import { useState, useEffect } from 'react';

function Dashboard() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    // 클라이언트에서 API 호출
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers);
  }, []);
  
  return <div>대시보드</div>;
}

export default Dashboard;

3.2 페이지 렌더링 흐름

사용자가 /users 접속
           ↓
1. Next.js가 src/app/users/page.tsx 탐색
           ↓
2. Server Component인지 Client Component인지 판단
           ↓
3-A. Server Component인 경우:
     - 서버에서 컴포넌트 실행
     - 데이터 페칭 (DB 직접 접근 가능)
     - HTML 생성
     - 클라이언트로 전송
           ↓
3-B. Client Component인 경우:
     - 초기 HTML 생성 (hydration 준비)
     - JavaScript 번들과 함께 전송
     - 클라이언트에서 hydration 실행
           ↓
4. 레이아웃 적용 (layout.tsx)
           ↓
5. 최종 페이지 렌더링

⚡ 4. 렌더링 최적화 메커니즘

4.1 Static Generation (SSG)

// 빌드 타임에 미리 생성
async function StaticPage() {
  const data = await getStaticData();
  return <div>{data}</div>;
}

export default StaticPage;

4.2 Server-Side Rendering (SSR)

// 요청 시마다 서버에서 렌더링
async function DynamicPage() {
  const data = await getRealTimeData();
  return <div>{data}</div>;
}

export default DynamicPage;

4.3 Incremental Static Regeneration (ISR)

// 정적 생성 + 주기적 재생성
async function ISRPage() {
  const data = await getData();
  return <div>{data}</div>;
}

// 1시간마다 재생성
export const revalidate = 3600;

export default ISRPage;

🔀 5. 라우팅 시스템 상세

5.1 동적 라우팅

파일 구조:
├── products/
│   ├── [id]/
│   │   └── page.tsx     # /products/123
│   ├── [...slug]/
│   │   └── page.tsx     # /products/a/b/c (catch-all)
│   └── [[...slug]]/
│       └── page.tsx     # /products 또는 /products/a/b/c (optional catch-all)
// src/app/products/[id]/page.tsx
interface Props {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
}

async function ProductPage({ params, searchParams }: Props) {
  const product = await getProduct(params.id);
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>검색 파라미터: {JSON.stringify(searchParams)}</p>
    </div>
  );
}

export default ProductPage;

5.2 Route Groups

src/app/
├── (marketing)/          # URL에 영향 없음
│   ├── about/
│   └── contact/
├── (shop)/
│   ├── products/
│   └── cart/
└── layout.tsx           # 전체 레이아웃

🛠️ 6. 미들웨어 실행 흐름

6.1 미들웨어 정의

// middleware.ts (프로젝트 루트)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 인증 체크
  const token = request.cookies.get('auth-token');
  
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  // API 요청 로깅
  if (request.nextUrl.pathname.startsWith('/api/')) {
    console.log(`API 요청: ${request.method} ${request.nextUrl.pathname}`);
  }
  
  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/:path*']
};

6.2 미들웨어 실행 순서

요청 → 미들웨어 → 페이지/API 라우트 → 응답

🎨 7. 스타일링 실행 흐름

7.1 CSS 모듈

// styles/component.module.css
.container {
  padding: 1rem;
}

// Component.tsx
import styles from './styles/component.module.css';

function Component() {
  return <div className={styles.container}>내용</div>;
}

7.2 Tailwind CSS

// 클래스명이 자동으로 처리됨
function Component() {
  return <div className="p-4 bg-blue-500">내용</div>;
}

🔧 8. 빌드 및 배포 과정

8.1 개발 모드 (npm run dev)

파일 변경 감지 → Hot Reload → 브라우저 자동 새로고침

8.2 프로덕션 빌드 (npm run build)

1. TypeScript 컴파일
2. 정적 페이지 사전 렌더링
3. JavaScript/CSS 번들링 및 최적화
4. 이미지 최적화
5. .next 폴더에 빌드 결과 생성

8.3 실행 (npm run start)

빌드된 파일 서빙 → 요청별 SSR 실행 → 응답

🚀 9. 성능 최적화 메커니즘

9.1 자동 코드 분할

// 동적 import로 자동 분할
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <p>로딩 중...</p>,
});

9.2 이미지 최적화

import Image from 'next/image';

function MyImage() {
  return (
    <Image
      src="/photo.jpg"
      alt="설명"
      width={500}
      height={300}
      priority // LCP 최적화
    />
  );
}

9.3 폰트 최적화

import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({ children }) {
  return (
    <html lang="ko" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

📊 10. 실행 흐름 요약

사용자 요청
    ↓
미들웨어 처리
    ↓
라우트 매칭 (파일 시스템 기반)
    ↓
┌─────────────────┬─────────────────┐
│   페이지 라우트    │   API 라우트      │
│                │                │
│ 1. 레이아웃 로드   │ 1. HTTP 메서드   │
│ 2. 서버/클라이언트 │    확인          │
│    컴포넌트 판단  │ 2. 해당 함수     │
│ 3. 데이터 페칭   │    실행          │
│ 4. HTML 렌더링   │ 3. Response     │
│                │    반환          │
└─────────────────┴─────────────────┘
    ↓
응답 전송 (HTML/JSON)
    ↓
클라이언트 hydration (필요시)

이것이 Next.js App Router의 핵심 실행 원리입니다! 🎯

728x90