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
'Tech Notes' 카테고리의 다른 글
| React useEffect 무한루프 완전 해결 가이드: 원인부터 해결책까지 (2) | 2025.08.18 |
|---|---|
| DB 기반 캐싱 vs 메모리 캐싱: 언제, 어떻게 써야 할까? (3) | 2025.08.15 |
| SQL 쿼리 작성 방식 비교 - 파라미터 바인딩 vs 템플릿 리터럴 (0) | 2025.08.12 |
| PostgreSQL JSONB 타입에 문자열 저장하는 완벽 가이드 (3) | 2025.08.12 |
| 📚TypeORM 핵심 개념 정리 (1) | 2025.08.11 |