Tan Kim

Next.js

React 기반 풀스택 프레임워크. SSR, SSG, ISR, App Router 등을 제공한다.

렌더링 방식

방식 설명 사용 사례
SSG (Static) 빌드 타임에 HTML 생성 블로그, 문서
SSR (Server) 요청마다 서버에서 렌더링 개인화 페이지, 실시간 데이터
ISR (Incremental) SSG + 일정 주기 재생성 상품 목록, 뉴스
CSR (Client) 브라우저에서 렌더링 대시보드, 인터랙티브 UI

App Router 구조 (Next.js 13+)

app/
├── layout.tsx          루트 레이아웃 (전체 공통)
├── page.tsx            / 페이지
├── loading.tsx         로딩 UI (Suspense)
├── error.tsx           에러 UI
├── not-found.tsx       404
├── globals.css
├── (auth)/             라우트 그룹 (URL에 영향 없음)
│   ├── login/page.tsx
│   └── register/page.tsx
├── dashboard/
│   ├── layout.tsx      /dashboard 전용 레이아웃
│   └── page.tsx
└── api/
    └── users/
        └── route.ts    API Route

Server vs Client Component

// Server Component (기본값) — 서버에서만 실행
// DB 접근, API 키 사용 가능
// useState, useEffect, 이벤트 핸들러 사용 불가
async function UserList() {
  const users = await db.query('SELECT * FROM users')
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
 
// Client Component — 브라우저에서 실행
// 파일 최상단에 'use client' 선언 필수
'use client'
 
import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(c => c+1)}>{count}</button>
}

데이터 페칭

// Server Component에서 직접 fetch
async function Page() {
  // SSG (기본) — 빌드 타임 캐시
  const data = await fetch('https://api.example.com/data')
 
  // SSR — 매 요청마다 새로 가져옴
  const data = await fetch('https://api.example.com/data', {
    cache: 'no-store'
  })
 
  // ISR — 60초마다 재생성
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }
  })
 
  return <div>{JSON.stringify(data)}</div>
}

API Route

// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server'
 
export async function GET(request: NextRequest) {
  const users = await db.findAll()
  return NextResponse.json(users)
}
 
export async function POST(request: NextRequest) {
  const body = await request.json()
  const user = await db.create(body)
  return NextResponse.json(user, { status: 201 })
}
 
// app/api/users/[id]/route.ts
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const user = await db.findById(Number(params.id))
  if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 })
  return NextResponse.json(user)
}

동적 라우팅

app/
├── users/[id]/page.tsx        → /users/1
├── blog/[...slug]/page.tsx    → /blog/a/b/c
└── [[...slug]]/page.tsx       → / 또는 /a/b/c
// app/users/[id]/page.tsx
export default function UserPage({ params }: { params: { id: string } }) {
  return <div>User {params.id}</div>
}
 
// 정적 경로 미리 생성 (SSG)
export async function generateStaticParams() {
  const users = await fetch('/api/users').then(r => r.json())
  return users.map((u: User) => ({ id: String(u.id) }))
}

미들웨어

// middleware.ts (루트)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')
 
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
 
  return NextResponse.next()
}
 
export const config = {
  matcher: ['/dashboard/:path*', '/api/:path*'],
}

Server Actions

// 서버에서 실행되는 함수를 Client Component에서 직접 호출
'use server'
 
async function createUser(formData: FormData) {
  const name = formData.get('name') as string
  await db.create({ name })
  revalidatePath('/users')  // 캐시 갱신
}
 
// Client Component에서 사용
'use client'
 
export function UserForm() {
  return (
    <form action={createUser}>
      <input name="name" />
      <button type="submit">추가</button>
    </form>
  )
}

next.config.ts 주요 설정

const config = {
  images: {
    remotePatterns: [
      { hostname: 'example.com' },
    ],
  },
  redirects: async () => [
    { source: '/old', destination: '/new', permanent: true },
  ],
  env: {
    CUSTOM_VAR: 'value',
  },
}
 
export default config

환경 변수

# .env.local
DATABASE_URL=...          # 서버 전용
NEXT_PUBLIC_API_URL=...   # 클라이언트에서도 접근 가능 (NEXT_PUBLIC_ 접두사)

메모