Next.js
설치 및 설정
섹션 제목: “설치 및 설정”새 프로젝트 생성
섹션 제목: “새 프로젝트 생성”# Using create-next-app (recommended)
npx create-next-app@latest my-app
cd my-app
# With TypeScript
npx create-next-app@latest my-app --typescript
# With tailwind CSS
npx create-next-app@latest my-app --tailwind
# Manual setup
npm init -y
npm install react react-dom next
프로젝트 구조
섹션 제목: “프로젝트 구조”my-app/
├── app/ # App router (Next.js 13+)
│ ├── page.js # Root route
│ ├── layout.js # Root layout
│ └── api/ # API routes
├── pages/ # Pages router (older)
├── public/ # Static files
├── styles/ # CSS/SCSS
├── components/ # React components
├── lib/ # Utility functions
├── next.config.js # Next.js config
├── package.json
└── tsconfig.json
개발 명령어
섹션 제목: “개발 명령어”| 명령어 | 설명 |
|---|---|
npm run dev | 개발 서버 시작 (localhost:3000) |
npm run build | 프로덕션용 빌드 |
npm run start | 프로덕션 서버 시작 |
npm run lint | ESLint 실행 |
npm test | 테스트 실행 (구성된 경우) |
페이지 및 라우팅 (App Router)
섹션 제목: “페이지 및 라우팅 (App Router)”기본 경로
섹션 제목: “기본 경로”# app/page.js - Home page (/)
export default function Home() {
return <h1>Home Page</h1>
}
# app/about/page.js - About page (/about)
export default function About() {
return <h1>About Page</h1>
}
# app/blog/[id]/page.js - Dynamic route (/blog/1, /blog/2, etc)
export default function BlogPost({ params }) {
return <h1>Blog Post: {params.id}</h1>
}
# app/docs/[[...slug]]/page.js - Catch-all route (/docs/a/b/c)
export default function Docs({ params }) {
return <pre>{JSON.stringify(params, null, 2)}</pre>
}
레이아웃 구성 요소
섹션 제목: “레이아웃 구성 요소”// app/layout.js - Root layout
export const metadata = {
title: 'My App',
description: 'Generated by create next app',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
// app/dashboard/layout.js - Dashboard layout
export default function DashboardLayout({ children }) {
return (
<div>
<nav>Dashboard Navigation</nav>
<main>{children}</main>
</div>
)
}
API 라우트
섹션 제목: “API 라우트”API 엔드포인트 생성
섹션 제목: “API 엔드포인트 생성”// app/api/hello/route.js
export async function GET(request) {
return Response.json({ message: 'Hello World' })
}
// app/api/users/route.js
export async function GET() {
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
]
return Response.json(users)
}
export async function POST(request) {
const data = await request.json()
// Process POST data
return Response.json({ created: true, data })
}
// app/api/users/[id]/route.js - Dynamic API route
export async function GET(request, { params }) {
const userId = params.id
// Fetch user by ID
return Response.json({ id: userId, name: 'User' })
}
export async function PUT(request, { params }) {
const data = await request.json()
// Update user
return Response.json({ updated: true, ...data })
}
export async function DELETE(request, { params }) {
const userId = params.id
// Delete user
return Response.json({ deleted: true, id: userId })
}
쿼리 매개변수 처리
섹션 제목: “쿼리 매개변수 처리”// app/api/search/route.js
export async function GET(request) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('q')
const limit = searchParams.get('limit') || '10'
// /api/search?q=next.js&limit=20
return Response.json({
query,
limit,
results: []
})
}
서버 측 렌더링 (SSR)
섹션 제목: “서버 측 렌더링 (SSR)”서버 컴포넌트
섹션 제목: “서버 컴포넌트”// app/blog/page.js - Server component by default
async function getBlogPosts() {
const res = await fetch('https://api.example.com/posts')
return res.json()
}
export default async function Blog() {
const posts = await getBlogPosts()
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
동적 서버 측 렌더링
섹션 제목: “동적 서버 측 렌더링”// Disable caching for specific routes
export const revalidate = 0 // No caching
export const revalidate = 60 // Cache for 60 seconds (ISR)
export default async function Page() {
const data = await fetch('https://api.example.com/data')
return <div>{/* content */}</div>
}
정적 사이트 생성 (SSG)
섹션 제목: “정적 사이트 생성 (SSG)”revalidate를 사용한 정적 생성
섹션 제목: “revalidate를 사용한 정적 생성”// app/posts/page.js - Revalidate every hour
export const revalidate = 3600
export default function Posts({ posts }) {
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
증분 정적 재생성 (ISR)
섹션 제목: “증분 정적 재생성 (ISR)”// app/blog/[slug]/page.js
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
export const revalidate = 3600 // Revalidate every hour
export default function BlogPost({ params }) {
return <h1>Post: {params.slug}</h1>
}
클라이언트 컴포넌트
섹션 제목: “클라이언트 컴포넌트”클라이언트 컴포넌트 사용
섹션 제목: “클라이언트 컴포넌트 사용”'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
)
}
클라이언트 측 데이터 가져오기
섹션 제목: “클라이언트 측 데이터 가져오기”'use client'
import { useEffect, useState } from 'react'
export default function UserList() {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const fetchUsers = async () => {
const res = await fetch('/api/users')
const data = await res.json()
setUsers(data)
setLoading(false)
}
fetchUsers()
}, [])
if (loading) return <p>Loading...</p>
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
이미지 최적화
섹션 제목: “이미지 최적화”Next.js Image 구성 요소 사용
섹션 제목: “Next.js Image 구성 요소 사용”import Image from 'next/image'
export default function MyImage() {
return (
<Image
src="/profile.jpg"
alt="Profile picture"
width={200}
height={200}
priority // Load immediately
/>
)
}
// With external image
export default function ExternalImage() {
return (
<Image
src="https://example.com/image.jpg"
alt="External image"
width={300}
height={300}
/>
)
}
메타데이터 및 SEO
섹션 제목: “메타데이터 및 SEO”메타 태그
섹션 제목: “메타 태그”// app/page.js
export const metadata = {
title: 'Home Page',
description: 'Welcome to my website',
keywords: 'next.js, react, web development',
openGraph: {
title: 'My Website',
description: 'Welcome',
url: 'https://mywebsite.com',
images: [
{
url: 'https://mywebsite.com/og.jpg',
width: 1200,
height: 630,
},
],
},
}
export default function Page() {
return <h1>Home</h1>
}
동적 메타데이터
섹션 제목: “동적 메타데이터”// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
}
}
export default function BlogPost({ params }) {
return <h1>{post.title}</h1>
}
환경 변수
섹션 제목: “환경 변수”환경 변수 사용
섹션 제목: “환경 변수 사용”# .env.local
DATABASE_URL=postgresql://user:pass@localhost/db
API_KEY=secret_key_123
NEXT_PUBLIC_API_URL=https://api.example.com
# .env.production
DATABASE_URL=postgresql://prod-user:pass@prod-db/db
변수 액세스
섹션 제목: “변수 액세스”// Server-side (any file)
const dbUrl = process.env.DATABASE_URL
const apiKey = process.env.API_KEY
// Client-side (must start with NEXT_PUBLIC_)
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// In client components
'use client'
export default function Component() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL
return <div>{apiUrl}</div>
}
미들웨어
섹션 제목: “미들웨어”라우트 미들웨어
섹션 제목: “라우트 미들웨어”// middleware.js (root directory)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Check authentication
const token = request.cookies.get('token')?.value
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
// Specify which routes use middleware
export const config = {
matcher: ['/dashboard/:path*', '/admin/:path*'],
}
인증 예제
섹션 제목: “인증 예제”기본 인증 흐름
섹션 제목: “기본 인증 흐름”// app/api/login/route.js
export async function POST(request) {
const { email, password } = await request.json()
// Verify credentials
if (email === 'user@example.com' && password === 'password') {
// Create token/session
const response = Response.json({ success: true })
response.cookies.set('token', 'jwt-token-here', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
})
return response
}
return Response.json({ error: 'Invalid credentials' }, { status: 401 })
}
// app/api/logout/route.js
export async function POST() {
const response = Response.json({ success: true })
response.cookies.delete('token')
return response
}
데이터베이스 통합
섹션 제목: “데이터베이스 통합”Prisma ORM 사용
섹션 제목: “Prisma ORM 사용”# Install Prisma
npm install @prisma/client
npm install -D prisma
# Initialize Prisma
npx prisma init
# Generate Prisma client after updating schema
npx prisma generate
npx prisma migrate dev --name init
API 라우트에서 Prisma 사용
섹션 제목: “API 라우트에서 Prisma 사용”// lib/db.js
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export default prisma
// app/api/users/route.js
import prisma from '@/lib/db'
export async function GET() {
const users = await prisma.user.findMany()
return Response.json(users)
}
export async function POST(request) {
const data = await request.json()
const user = await prisma.user.create({ data })
return Response.json(user)
}
Vercel 배포 (권장)
섹션 제목: “Vercel 배포 (권장)”# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Deploy to production
vercel --prod
# Environment variables
vercel env add DATABASE_URL
자체 호스팅 배포
섹션 제목: “자체 호스팅 배포”# Build for production
npm run build
# Start production server
npm run start
# Or with PM2
pm2 start npm --name "nextjs" -- start
pm2 save
Docker 배포
섹션 제목: “Docker 배포”# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
성능 최적화
섹션 제목: “성능 최적화”코드 분할 및 동적 가져오기
섹션 제목: “코드 분할 및 동적 가져오기”import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('@/components/Heavy'), {
loading: () => <p>Loading...</p>,
})
export default function Page() {
return <HeavyComponent />
}
글꼴 최적화
섹션 제목: “글꼴 최적화”import { Inter, Roboto } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
const roboto = Roboto({ weight: '400' })
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
)
}
모범 사례
섹션 제목: “모범 사례”- 더 나은 성능을 위해 기본적으로 서버 컴포넌트 사용
- 적절한 오류 경계 구현
- 코드 분할을 위해 동적 임포트 사용
- Next.js Image 컴포넌트로 이미지 최적화
- 적절한 캐싱 전략 설정 (ISR, revalidate)
- 민감한 데이터를 위해 환경 변수 사용
- API 라우트에서 적절한 오류 처리 구현
- Core Web Vitals 및 성능 메트릭 모니터링
참고 자료
섹션 제목: “참고 자료”최종 업데이트: 2026-03-30