Next.js 14의 App Router는 React Server Components를 기반으로 한 새로운 패러다임입니다. 서버 컴포넌트, 스트리밍, 데이터 페칭 전략을 마스터하여 고성능 풀스택 앱을 만들어보세요.
Next.js 14's App Router is a new paradigm based on React Server Components. Master Server Components, streaming, and data fetching strategies to build high-performance fullstack apps.
App Router 기본 구조 App Router Basic Structure
App Router는 app/ 디렉토리 기반의 파일 시스템 라우팅을 사용합니다.
폴더가 라우트 세그먼트가 되고, 특수 파일들이 UI를 정의합니다.
App Router uses file-system based routing in the app/ directory.
Folders become route segments, and special files define the UI.
app/
├── layout.tsx # 루트 레이아웃
├── page.tsx # / 페이지
├── loading.tsx # 로딩 UI
├── error.tsx # 에러 바운더리
├── blog/
│ ├── page.tsx # /blog 페이지
│ └── [slug]/
│ └── page.tsx # /blog/:slug 동적 라우트
└── api/
└── route.ts # API 라우트 핸들러
app/
├── layout.tsx # Root layout
├── page.tsx # / page
├── loading.tsx # Loading UI
├── error.tsx # Error boundary
├── blog/
│ ├── page.tsx # /blog page
│ └── [slug]/
│ └── page.tsx # /blog/:slug dynamic route
└── api/
└── route.ts # API route handler
Server Components vs Client Components Server Components vs Client Components
App Router에서 모든 컴포넌트는 기본적으로 서버 컴포넌트입니다.
클라이언트 상호작용이 필요한 경우에만 'use client' 지시어를 사용합니다.
In App Router, all components are Server Components by default.
Use the 'use client' directive only when client interactivity is needed.
async function PostsPage() {
// 서버에서 직접 데이터 페칭
const posts = await fetch('https://api.example.com/posts');
const data = await posts.json();
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default PostsPage;
async function PostsPage() {
// Fetch data directly on server
const posts = await fetch('https://api.example.com/posts');
const data = await posts.json();
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default PostsPage;
'use client';
import { useState } from 'react';
export function LikeButton({ postId }: { postId: string }) {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'}
</button>
);
}
'use client';
import { useState } from 'react';
export function LikeButton({ postId }: { postId: string }) {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'}
</button>
);
}
데이터 페칭 전략 Data Fetching Strategies
Next.js는 SSR, SSG, ISR 세 가지 렌더링 전략을 지원합니다. fetch 함수의 옵션으로 캐싱 동작을 제어합니다.
Next.js supports three rendering strategies: SSR, SSG, and ISR. Control caching behavior with fetch options.
const data = await fetch('https://api.example.com/data');
// SSR: 매 요청마다 새로 렌더링
const data = await fetch('https://api.example.com/data', {
cache: 'no-store'
});
// ISR: 지정 시간마다 재검증
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 } // 60초마다
});
const data = await fetch('https://api.example.com/data');
// SSR: Re-render on every request
const data = await fetch('https://api.example.com/data', {
cache: 'no-store'
});
// ISR: Revalidate at specified interval
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 } // every 60 seconds
});
레이아웃과 스트리밍 Layouts and Streaming
레이아웃은 여러 페이지에서 공유되는 UI입니다. Suspense와 함께 스트리밍을 사용하면 데이터 로딩 중에도 사용자에게 즉시 콘텐츠를 보여줄 수 있습니다.
Layouts are UI shared across multiple pages. Using streaming with Suspense allows showing content to users immediately while data is loading.
export default function RootLayout({
children,
}: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
export default function RootLayout({
children,
}: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<h1>대시보드</h1>
{/* 빠르게 로드되는 컴포넌트 */}
<QuickStats />
{/* 느린 데이터는 스트리밍 */}
<Suspense fallback={<ChartSkeleton />}>
<AnalyticsChart />
</Suspense>
</div>
);
}
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
{/* Fast loading component */}
<QuickStats />
{/* Stream slow data */}
<Suspense fallback={<ChartSkeleton />}>
<AnalyticsChart />
</Suspense>
</div>
);
}
Server Actions Server Actions
Server Actions는 서버에서 실행되는 비동기 함수로, 폼 제출과 데이터 뮤테이션을 처리합니다. API 라우트 없이 직접 서버 로직을 호출할 수 있습니다.
Server Actions are async functions that run on the server, handling form submissions and data mutations. Call server logic directly without API routes.
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title');
const content = formData.get('content');
// DB에 저장
await db.post.create({ data: { title, content } });
// 캐시 무효화 및 리다이렉트
revalidatePath('/posts');
redirect('/posts');
}
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title');
const content = formData.get('content');
// Save to DB
await db.post.create({ data: { title, content } });
// Invalidate cache and redirect
revalidatePath('/posts');
redirect('/posts');
}
import { createPost } from './actions';
export default function NewPostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="제목" />
<textarea name="content" placeholder="내용" />
<button type="submit">등록</button>
</form>
);
}
import { createPost } from './actions';
export default function NewPostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="Title" />
<textarea name="content" placeholder="Content" />
<button type="submit">Submit</button>
</form>
);
}
✨ Next.js App Router 핵심 포인트 ✨ Next.js App Router Key Points
- 서버 컴포넌트 기본: 번들 크기 감소, 직접 DB 접근
- 스트리밍: Suspense로 점진적 UI 로딩
- Server Actions: API 없이 서버 로직 호출
- 중첩 레이아웃: 효율적인 UI 재사용
- Server Components by default: Reduced bundle, direct DB access
- Streaming: Progressive UI loading with Suspense
- Server Actions: Call server logic without APIs
- Nested layouts: Efficient UI reuse
⚠️ 흔한 실수들 ⚠️ Common Mistakes
- 서버 컴포넌트에서
useState,useEffect사용 - 불필요한
'use client'남발 - 클라이언트 컴포넌트에서 민감한 데이터 노출
- 캐싱 전략 미고려로 인한 성능 저하
- Using
useState,useEffectin Server Components - Overusing
'use client'unnecessarily - Exposing sensitive data in Client Components
- Performance issues from not considering caching strategy
