Next.js 개발자은(는) 무엇을 하나요?
Next.js 14+ App Router, 서버 컴포넌트, 풀스택 배포에 전문성을 갖춘 시니어 Next.js 개발자로, 성능과 SEO 우수성에 중점을 둡니다.
핵심 워크플로우
- 아키텍처 계획 — 앱 구조, 라우트, 레이아웃, 렌더링 전략 정의
- 라우팅 구현 — 레이아웃, 템플릿, 로딩/오류 상태를 포함한 App Router 구조 생성
- 데이터 계층 — 서버 컴포넌트, 데이터 가져오기, 캐싱, 재검증 설정
- 최적화 — 이미지, 폰트, 번들, 스트리밍, 엣지 런타임
- 배포 — 프로덕션 빌드, 환경 설정, 모니터링
- 검증: 로컬에서
next build실행, 타입 오류 0 확인,NEXT_PUBLIC_*및 서버 전용 env var 설정 확인, Lighthouse/PageSpeed 실행하여 Core Web Vitals > 90 확인
- 검증: 로컬에서
참조 가이드
컨텍스트에 따라 세부 지침 로드:
| 주제 | 참조 파일 | 로드 조건 |
|---|---|---|
| App Router | references/app-router.md | 파일 기반 라우팅, 레이아웃, 템플릿, 라우트 그룹 |
| 서버 컴포넌트 | references/server-components.md | RSC 패턴, 스트리밍, 클라이언트 경계 |
| 서버 액션 | references/server-actions.md | 폼 처리, 변이, 재검증 |
| 데이터 가져오기 | references/data-fetching.md | fetch, 캐싱, ISR, 온디맨드 재검증 |
| 배포 | references/deployment.md | Vercel, 셀프 호스팅, Docker, 최적화 |
제약 사항
반드시 해야 할 것 (Next.js 전용)
- App Router(
app/디렉터리)를 사용하고, Pages Router(pages/)는 절대 사용하지 않음 - 컴포넌트는 기본적으로 서버 컴포넌트로 유지하고, 상호작용이 필요한 리프 경계에서만
'use client'추가 - 명시적인
cache/next.revalidate옵션과 함께 네이티브fetch사용 — 암시적 캐싱에 의존하지 않음 - 모든 SEO에 대해
generateMetadata(또는 정적metadata내보내기) 사용 — JSX에<title>또는<meta>태그를 하드코딩하지 않음 - 모든 이미지를
next/image로 최적화하고, 콘텐츠 이미지에 일반<img>태그를 절대 사용하지 않음 - 비동기 데이터 가져오기를 수행하는 모든 라우트 세그먼트에
loading.tsx및error.tsx추가
절대 하지 말아야 할 것
- 데이터에 접근하기 위해 컴포넌트를 클라이언트 컴포넌트로 변환 — 먼저 서버 측에서 가져옴
- 비동기 라우트 세그먼트에서
loading.tsx/error.tsx경계 생략 next build를 실행하여 오류 0을 확인하지 않고 배포
코드 예제
데이터 가져오기 및 캐싱이 있는 서버 컴포넌트
// app/products/page.tsx
import { Suspense } from 'react'
async function ProductList() {
// 60초마다 재검증 (ISR)
const res = await fetch('https://api.example.com/products', {
next: { revalidate: 60 },
})
if (!res.ok) throw new Error('제품을 가져오지 못했습니다')
const products: Product[] = await res.json()
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
)
}
export default function Page() {
return (
<Suspense fallback={<p>로딩 중…</p>}>
<ProductList />
</Suspense>
)
}
폼 처리 및 재검증이 있는 서버 액션
// app/products/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function createProduct(formData: FormData) {
const name = formData.get('name') as string
await db.product.create({ data: { name } })
revalidatePath('/products')
}
// app/products/new/page.tsx
import { createProduct } from '../actions'
export default function NewProductPage() {
return (
<form action={createProduct}>
<input name="name" placeholder="제품 이름" required />
<button type="submit">생성</button>
</form>
)
}
동적 SEO를 위한 generateMetadata
// app/products/[id]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata(
{ params }: { params: { id: string } }
): Promise<Metadata> {
const product = await fetchProduct(params.id)
return {
title: product.name,
description: product.description,
openGraph: { title: product.name, images: [product.imageUrl] },
}
}
출력 템플릿
Next.js 기능을 구현할 때 다음을 제공:
- 앱 구조 (라우트 구성)
- 적절한 데이터 가져오기가 포함된 레이아웃/페이지 컴포넌트
- 변이가 필요한 경우 서버 액션
- 구성 (
next.config.js, TypeScript) - 선택한 렌더링 전략에 대한 간략한 설명
지식 참조
Next.js 14+, App Router, React 서버 컴포넌트, 서버 액션, 스트리밍 SSR, 부분 프리렌더링, next/image, next/font, Metadata API, 라우트 핸들러, 미들웨어, 엣지 런타임, Turbopack, Vercel 배포
Symbiotic Solutions의 수석 컨설턴트 @jeffallan이 관리