소스 정보
- 저장소
- puk0806/gugbab-claude
- 최근 소스 활동
- 2026년 8월 12일 00:35
- 감지된 SKILL.md 언어
- 한국어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/puk0806/gugbab-claude --skill nextjs명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
DDD(Domain-Driven Design) 아키텍처 핵심 패턴 - 유비쿼터스 언어, 서브도메인, 바운디드 컨텍스트, Aggregate, Entity/VO, 도메인 서비스/이벤트, 레이어드 아키텍처
대규모 React/Next.js 프로젝트를 layer-first(types/·utils/·hooks/·api/·components/ 밑에 도메인이 반복되는 구조)에서 domain-first(feature/도메인 우선) 구조로 전환하는 설계 기준과 절차. Feature-Sliced Design 2.1 정본(layers 6종·slices·segments·import 규칙·@x 크로스임포트·public API), FSD를 쓰지 않는 경량 대안(features + shared 2~3계층 + ESLint import/no-restricted-paths), Next.js App Router 공존 전략(route group `()`·private folder `_`·colocation), Turborepo/Nx 모노레포에서 폴더↔패키지 승격 기준, colocation과 배럴 파일 성능 트레이드오프, 도메인 경계 역추출(import 그래프·change coupling·용어 클러스터), 전환 실패 패턴(shared 비대화·entities 남용·순환 의존·도메인=라우트 착각·조기 추상화). 도메인 개념 자체(바운디드 컨텍스트·유비쿼터스 언어)는 `architecture/ddd` 스킬을 참조한다.
소스 파일 수천 개 규모 프론트엔드 코드베이스를 멈추지 않고 점진 재구조화하는 실행 전략 - Strangler Fig / Branch by Abstraction / Parallel Change, ts-morph·jscodeshift codemod, PR 분할·검증 게이트·되돌리기, 테스트 없는 코드의 안전망, 작업 순서 설계와 위반 수 기반 진행 추적
| name | nextjs |
| description | Next.js 16.x App Router 핵심 패턴, 데이터 페칭, Cache Components 캐싱 전략, 15→16 Breaking Changes |
소스:
- App Router 문서: https://nextjs.org/docs/app
- Next.js 16 릴리즈: https://nextjs.org/blog/next-16
- Next.js 16.1 릴리즈: https://nextjs.org/blog/next-16-1
- Next.js 16.2 릴리즈: https://nextjs.org/blog/next-16-2
- Next.js 16.3 릴리즈: https://nextjs.org/blog/next-16-3
- v15 → v16 업그레이드 가이드: https://nextjs.org/docs/app/guides/upgrading/version-16
- 캐싱(Cache Components): https://nextjs.org/docs/app/getting-started/caching
- fetch API 레퍼런스: https://nextjs.org/docs/app/api-reference/functions/fetch
검증일: 2026-08-11 검증 대상 버전: Next.js 16.3.0 (2026-08-03 릴리즈, 현재 최신 stable)
app/
├── layout.tsx # 공유 레이아웃 (children 감싸기)
├── page.tsx # 라우트 UI
├── loading.tsx # 자동 Suspense 래핑 (로딩 UI)
├── error.tsx # 에러 바운더리 ('use client' 필수)
├── not-found.tsx # notFound() 호출 시 표시
├── route.ts # API Route Handler
├── template.tsx # 탐색 시마다 새 인스턴스 (layout과 차이)
└── (group)/ # URL에 영향 없는 폴더 그룹핑
└── dashboard/
└── page.tsx # /dashboard 라우트
서버 컴포넌트 (기본값):
✅ DB/API 직접 접근
✅ 민감 정보(API 키, 토큰)
✅ 대용량 의존성 (번들 크기 ↓)
✅ SEO 필요 콘텐츠
클라이언트 컴포넌트 ('use client'):
✅ useState, useEffect, useReducer
✅ 이벤트 핸들러 (onClick, onChange 등)
✅ 브라우저 API (localStorage, window 등)
✅ 실시간 인터랙션
// ✅ 서버에서 데이터 페칭, 클라이언트는 인터랙션만
// app/posts/page.tsx (Server)
async function PostsPage() {
const posts = await db.post.findMany() // 서버에서만
return <PostList posts={posts} /> // Client Component로 전달
}
// components/PostList.tsx (Client)
'use client'
function PostList({ posts }: { posts: Post[] }) {
const [filter, setFilter] = useState('all')
// ...
}
// 정적 (빌드 시 1회 페칭, CDN 캐시)
// Next.js 15+ 기본값은 'auto no cache' — 캐싱은 opt-in이다.
// · 라우트가 정적 prerender 되면 next build 때 1회만 페칭
// · 라우트에 Request-time API(cookies/headers/searchParams 등)가 감지되면 매 요청 페칭
// · dev 서버에서는 매 요청 페칭
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache' // Next.js 15+: 지속 캐싱을 원하면 명시 필수
})
// 동적 (라우트 정적 여부와 무관하게 매 요청마다 페칭)
const data = await fetch('https://api.example.com/data', {
cache: 'no-store'
})
// ISR (주기적 재검증)
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 } // 1시간마다 갱신
})
// 태그 기반 재검증
const data = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
// Server Action / Route Handler에서 사용
import { revalidateTag, revalidatePath, updateTag, refresh } from 'next/cache'
async function updatePost(id: string) {
await db.post.update({ where: { id }, data: { ... } })
// ⚠️ Next.js 16: revalidateTag는 두 번째 인자로 cacheLife 프로필이 필수다.
// 1-인자 형태는 deprecated이며 TypeScript 에러가 발생한다.
revalidateTag('posts', 'max') // stale-while-revalidate — 약간의 지연 허용
revalidatePath('/posts') // 경로 기반
revalidatePath('/posts/[id]', 'page') // 동적 라우트
}
| API | 도입/변경 | 용도 |
|---|---|---|
revalidateTag(tag, profile) | v16에서 2번째 인자 필수 | stale-while-revalidate. 블로그·카탈로그처럼 갱신 지연이 허용되는 콘텐츠 |
updateTag(tag) | v16 신규, Server Action 전용 | read-your-writes. 만료 + 같은 요청 내 즉시 갱신 → 사용자가 자기 변경을 바로 봄 |
refresh() | v16 신규, Server Action 전용 | 클라이언트 라우터만 새로고침 (예: 헤더의 알림 카운트) |
'use server'
import { updateTag } from 'next/cache'
export async function updateProfile(userId: string, profile: Profile) {
await db.users.update(userId, profile)
updateTag(`user-${userId}`) // 즉시 반영 — 폼·설정 저장에 적합
}
next.config.ts에 cacheComponents: true 설정 필요.
주의: Next.js 15에서 쓰던
experimental.dynamicIO/experimental.useCache플래그는 v16에서 제거됐다. 최상위cacheComponents: true로 대체한다. 단순 rename이 아니라,<Suspense>밖의 uncached 데이터에서 빌드 에러가 발생할 수 있는 모델 전환이다 (→/docs/app/guides/migrating-to-cache-components).
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
// DB 쿼리 캐싱 (data-level)
async function getCachedPosts(userId: string) {
'use cache'
return await db.post.findMany({ where: { userId } })
}
// cacheTag / cacheLife로 세밀한 제어 — v16에서 stable (unstable_ 접두사 제거)
import { cacheTag, cacheLife } from 'next/cache'
async function getPost(id: string) {
'use cache'
cacheTag('posts', `post-${id}`)
cacheLife('hours') // 1시간 프로필
return await db.post.findUnique({ where: { id } })
}
// 컴포넌트/페이지 전체 캐싱 (UI-level)
export default async function Page() {
'use cache'
cacheLife('hours')
const users = await db.query('SELECT * FROM users')
return < = />
}
use cache 변형 (v16.3 기준)
| 디렉티브 | 저장 위치 | 용도 |
|---|---|---|
'use cache' | 프리렌더 HTML + 인스턴스 인메모리 | 모든 사용자에게 동일한 결과 |
'use cache: private' | 브라우저(클라이언트)만 | cookies()·headers()·searchParams를 직접 읽는 함수에 수명 부여 |
'use cache: remote' | 공유 cache handler(내구성) | 인스턴스 간 공유 필요, 히트율이 높을 때만 이득 |
주의:
use cache스코프 안에서cookies()·headers()같은 uncached 소스에 직접 접근할 수 없다. 바깥에서 값을 뽑아 인자로 전달하면 그 값이 캐시 키의 일부가 된다.
주의: 공식 문서가 "이 API는 Next.js 16에서
use cache로 대체됐다"고 명시한다. 신규 코드에는 쓰지 말고, 기존 코드는 Cache Components +use cache로 마이그레이션한다. (제거되지는 않아 당장 동작은 한다.)
import { unstable_cache } from 'next/cache'
const getCachedPosts = unstable_cache(
async (userId: string) => db.post.findMany({ where: { userId } }),
['user-posts'], // 캐시 키
{ revalidate: 3600, tags: ['posts'] }
)
// ❌ Next.js 14
function Page({ params }: { params: { id: string } }) {
const { id } = params // 동기 접근
}
// ✅ Next.js 15+
async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params // 반드시 await
}
// searchParams도 동일
async function Page({
searchParams
}: {
searchParams: Promise<{ query?: string }>
}) {
const { query } = await searchParams
}
Next.js 16: v15에 있던 동기 접근 임시 호환이 완전히 제거됐다.
cookies()·headers()·draftMode()·params·searchParams모두 await 없이는 동작하지 않는다. 마이그레이션 codemod:npx @next/codemod@canary next-async-request-api .
PageProps / LayoutProps / RouteContext (v15.5+)npx next typegen으로 라우트별 전역 타입 헬퍼가 생성된다. 수동 Promise<{...}> 선언보다 안전하다.
// app/blog/[slug]/page.tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const { slug } = await props.params // slug 타입이 라우트에서 추론됨
const query = await props.searchParams
return <h1>{slug}</h1>
}
Next.js 16에는 캐싱 모델이 두 개 공존한다. 어느 쪽인지 먼저 확정하고 답해야 한다.
| 모델 | 활성 조건 | 공식 문서 |
|---|---|---|
| Cache Components (권장) | cacheComponents: true | /docs/app/getting-started/caching |
| 이전 모델 (4계층) | 플래그 없음 (기본) | /docs/app/guides/caching-without-cache-components |
cacheComponents 미사용 시)요청 → Request Memoization (렌더링 사이클 내 중복 제거)
→ Data Cache (fetch 결과, 지속 캐시)
→ Full Route Cache (정적 라우트 HTML/RSC)
→ Router Cache (클라이언트 탐색 캐시)
| 캐시 계층 | 저장 위치 | 지속 기간 | 무효화 방법 |
|---|---|---|---|
| Request Memo | 서버 메모리 | 요청 1회 | 자동 |
| Data Cache | 서버 파일시스템 | 지속 | revalidateTag/Path |
| Full Route | 서버 파일시스템 | 지속 (정적) | 재배포/revalidate |
| Router Cache | 브라우저 메모리 | 세션 | router.refresh() |
cacheComponents: true)암묵적 4계층 대신 명시적 use cache 하나로 통일된다.
빌드/프리렌더 → static shell 생성
├─ 'use cache' 결과 → static shell에 포함 (수명은 cacheLife가 결정)
├─ <Suspense> fallback → shell에 포함, 본문은 요청 시 streaming
└─ 예측 가능한 값(모듈 import·순수 계산) → 자동으로 shell에 포함
cookies() 읽기가 라우트 전체를 동적으로 만들지 않는다 — Suspense 경계 안쪽만 요청 시 렌더된다.주의 (SSR·SEO): 봇·크롤러는 User-Agent로 감지되어 shell을 재사용하지 않고 요청 시 전체 동적 렌더된다. shell이 빌드 타임에만 존재하는 데이터에 의존하면, 사람에게는 뜨는 페이지가 크롤러에게는 실패할 수 있다.
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('q')
const posts = await db.post.findMany({
where: query ? { title: { contains: query } } : {}
})
return NextResponse.json(posts)
}
export async function POST(request: NextRequest) {
const body = await request.json()
const post = await db.post.create({ data: body })
return NextResponse.json(post, { status: 201 })
}
// 동적 라우트: app/api/posts/[id]/route.ts
export async () {
{ id } = params
post = db..({ : { id } })
(!post) .({ : }, { : })
.(post)
}
// app/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
import { z } from 'zod'
const schema = z.object({
title: z.string().min(1).max(100),
content: z.string().min(1)
})
export async function createPost(prevState: unknown, formData: FormData) {
const result = schema.safeParse({
title: formData.get('title'),
content: formData.get('content')
})
if (!result.success) {
return { error: result.error.flatten() }
}
await db.post.create({ data: result.data })
revalidatePath('/posts')
return { success: true }
}
() {
[state, formAction, isPending] = (createPost, )
(
)
}
여기서는 프레임워크 관점의 기본형만 다룬다. OpenGraph·JSON-LD·sitemap·robots·canonical 등 SEO 관점의 상세는
frontend/seo-nextjs스킬을 참조한다.
// app/about/page.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'About',
description: '소개 페이지',
openGraph: {
title: 'About',
description: '소개 페이지',
images: ['/og-image.png'],
},
}
// app/posts/[id]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata(
{ params }: { params: Promise<{ id: string }> }
): Promise<Metadata> {
const { id } = await params
const post = await fetch(`https://api.example.com/posts/${id}`).then(r => r.json())
return {
title: post.title,
description: post.excerpt,
}
}
// loading.tsx: 자동으로 Suspense 래핑됨
export default function Loading() {
return <Skeleton />
}
// 세분화된 Streaming: 느린 컴포넌트만 suspense 처리
async function Page() {
return (
<main>
<Header /> {/* 즉시 표시 */}
<Suspense fallback={<CommentSkeleton />}>
<Comments /> {/* 준비되면 streaming */}
</Suspense>
</main>
)
}
// middleware.ts (루트) — Next.js 15까지
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))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*']
}
// proxy.ts (루트) — Next.js 16+
// middleware.ts는 deprecated, proxy.ts로 대체
// Node.js 런타임에서 실행
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
const token = request.cookies.get('auth-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/protected/:path*']
}
주의:
proxy.ts의 런타임은nodejs로 고정이며 설정할 수 없다. Edge 런타임이 필요하면 deprecated된middleware.ts를 유지해야 한다 (공식 문서: "We will follow up on a minor release with further edge runtime instructions").
설정 플래그 이름도 함께 바뀐다 — middleware가 들어간 옵션은 proxy로 리네이밍됐다.
// next.config.ts
const nextConfig: NextConfig = {
// skipMiddlewareUrlNormalize → skipProxyUrlNormalize
skipProxyUrlNormalize: true,
}
middleware.ts → proxy.ts 마이그레이션 (codemod가 파일명·함수명·플래그를 함께 처리):
npx @next/codemod@canary upgrade latest
소스: https://nextjs.org/blog/next-16 | https://nextjs.org/docs/app/guides/upgrading/version-16
| 항목 | 이전 (15) | 이후 (16) |
|---|---|---|
| Node.js 최소 버전 | 18.18.0 | 20.9.0 (Node 18 미지원) |
| TypeScript 최소 버전 | — | 5.1.0 |
| 브라우저 | — | Chrome/Edge/Firefox 111+, Safari 16.4+ |
| 기본 번들러 | Webpack (prod) / Turbopack (dev) | Turbopack (dev + build 모두 기본) |
| 영역 | 변경 |
|---|---|
| 동기 Request API | v15의 임시 동기 호환이 완전 제거. cookies·headers·draftMode·params·searchParams는 await 필수 |
| middleware.ts | deprecated → proxy.ts (함수명·설정 플래그도 proxy로) |
generateSitemaps | sitemap()이 받는 id가 Promise<string> 으로 변경 |
| OG·아이콘 이미지 | opengraph-image·twitter-image·icon·apple-icon의 params·id가 Promise로 변경 (generateImageMetadata의 params는 동기 유지) |
revalidateTag | 2번째 인자(cacheLife 프로필) 필수. 1-인자는 deprecated → TS 에러 |
| PPR | experimental.ppr / experimental_ppr 세그먼트 설정 제거 → cacheComponents: true |
experimental.dynamicIO / experimental.useCache | 제거 → cacheComponents |
unstable_rootParams | 제거 → next/root-params |
| 병렬 라우트 | 모든 slot에 default.js 필수. 없으면 빌드 실패 |
| AMP | 완전 제거 (next/amp, useAmp, amp 설정) |
next lint | 제거. ESLint/Biome 직접 사용, next build는 더 이상 lint하지 않음 |
next/image 기본값 변경 (v16)| 옵션 | 이전 | 이후 (16) |
|---|---|---|
images.minimumCacheTTL | 60초 | 4시간(14400초) |
images.imageSizes | [16, 32, ...] | 16 제거 |
images.qualities | 전체 허용 | [75]만 (다른 값은 가장 가까운 값으로 보정) |
images.maximumRedirects | 무제한 | 3 |
| 로컬 IP 최적화 | 허용 | 차단 (dangerouslyAllowLocalIP로만 해제, SSRF 위험) |
| 쿼리스트링 로컬 이미지 | 허용 | images.localPatterns.search 설정 필요 (열거 공격 방지) |
# Next.js 16에서 Turbopack이 dev/build 모두 기본값 — --turbopack 플래그 불필요
next dev # Turbopack으로 실행
next build # Turbopack으로 빌드
# Webpack으로 되돌리려면
next build --webpack
커스텀 Webpack 설정이 있는 경우 — next build가 실패한다 (오설정 방지 목적):
// next.config.js
const nextConfig = {
webpack: (config) => { /* 커스텀 설정 */ return config }
}
// → next build --turbopack (webpack 설정 무시) 또는 next build --webpack (Webpack 유지)
설정 위치도 이동했다: experimental.turbopack → 최상위 turbopack.
const nextConfig: NextConfig = {
turbopack: {
resolveAlias: { fs: { browser: './empty.ts' } },
},
}
주의: Turbopack은 Sass의 레거시 틸드(
~) prefix import를 지원하지 않는다.@import '~bootstrap/...'→@import 'bootstrap/...'.
useEffectEvent, Activity).reactCompiler 옵션이 experimental → stable로 승격. 단 기본값은 off (빌드 성능 데이터 수집 중).next dev — stable, 기본 on. 컴파일 산출물을 디스크에 저장해 재시작 시 컴파일 next experimental-analyzenext dev --inspect (Node 디버거 연결)serverExternalPackages가 전이 의존성까지 처리next upgrade 커맨드, 설치 용량 ~20MB 감소node_modules/next/dist/docs/의 번들된 문서를 먼저 읽도록 안내{ "mcpServers": { "next-devtools": { "command": "npx", "args": ["-y", "next-devtools-mcp@latest"] } } }
adapterPath(Build Adapters)가 최상위 stable 옵션으로 승격소스: https://nextjs.org/blog/next-16-3 Breaking change 없음 — 애플리케이션 코드 변경 없이 업그레이드하면 성능 이득을 얻는다.
| 항목 | 수치 | 비고 |
|---|---|---|
| dev 서버 메모리 | 최대 90% 감소 | disk caching(16.1 도입) + memory eviction, 둘 다 기본 on |
next build | 최대 5.5× 빠름 (반복 빌드) | FileSystem Cache가 next build까지 확장, 기본 on |
| SSR 처리량 | +22% 요청/초 | App Router 렌더링 레이어를 web streams → native Node.js streams로 교체 |
| 타입 체크 | TypeScript 7(네이티브 포트) 사용 가능 | pnpm add -D typescript@^7 — useTypeScriptCli 설정 |
| prefetch 요청 수 | 감소 | 일정 크기 이하 prefetch를 묶어서 전송(prefetchInlining) |
관련 설정: turbopackMemoryEviction, turbopackFileSystemCache, prefetchInlining.
catchError — 커스텀 에러 바운더리 (next/error)
기존 React 에러 바운더리는 notFound()·redirect() 호출을 가로채는 문제가 있었고, 클라이언트 상태만 리셋할 수 있었다. catchError는 이를 방해하지 않으며 실패한 Server Component를 재요청하는 retry() 를 제공한다.
'use client'
import { catchError, type ErrorInfo } from 'next/error'
function ErrorFallback(props: { title: string }, { error, retry }: ErrorInfo) {
return (
<div>
<h2>{props.title}</h2>
<p>{error.message}</p>
<button onClick={() => retry()}>다시 시도</button>
</div>
)
}
export default catchError(ErrorFallback)
Root Params — next/root-params
루트 레이아웃 위에 정의된 [lang] 같은 파라미터를 prop drilling 없이 모든 Server Component에서 읽는다. use cache 스코프 안에서도 동작한다.
// app/[lang]/posts/[slug]/page.tsx
import { lang } from 'next/root-params'
export default async function PostPage(props: PageProps<'/[lang]/posts/[slug]'>) {
const { slug } = await props.params
const language = await lang()
return <article>{language} / {slug}</article>
}
주의: 현재 Server Component만 지원한다. Route Handler·Server Action 지원은 향후 릴리즈 예정. v16에서 제거된
unstable_rootParams의 정식 후속 API다.
glob imports — import.meta.glob
Turbopack이 Vite 호환 import.meta.glob을 지원한다. 로컬 파일을 읽는 Server Component에 HMR이 적용된다.
const posts = import.meta.glob('./posts/*.md', { eager: true })
// .md는 next.config.js에 loader 등록 필요
SPA 수준의 탐색 반응성을 서버 주도 모델을 유지한 채 제공한다. 두 플래그로 활성화한다.
// next.config.ts
const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true,
}
| 기능 | 내용 |
|---|---|
| Instant Insights | DevTools가 즉시 렌더되지 않는 탐색을 자동 검출 + 수정 프롬프트 제공 |
| Partial Prefetching | 라우트 UI 어디서든 재사용 가능한 loading shell을 추출. <Link prefetch={true}>가 필요한 만큼만 prefetch |
| 개선된 ISR | generateStaticParams로 프리렌더하지 않은 URL도 첫 방문자에게 즉시 loading shell을 주고, 백그라운드에서 완성본으로 업그레이드 |
| Navigation Inspector | 탐색을 shell 상태에서 일시정지해 실제 로딩 상태를 육안 확인 |
instant() 테스트 헬퍼 | @next/playwright — 탐색 시 즉시 보여야 할 콘텐츠를 회귀 테스트로 고정 |
공식 문서는 이 동작들이 향후 메이저 버전에서 기본값이 될 예정이라고 명시한다.
next dev가 실행될 때마다 버전이 일치하는 AGENTS.md 블록을 직접 쓰고 유지한다. 이 블록은 node_modules/next/dist/docs/의 번들 문서를 가리킨다.
주의: 이 변경으로 Vercel이 "현재 문서를 앱에 주입"할 목적으로 제공하던 기존 Skills는 폐지(retired) 됐다.
AGENTS.md블록은next dev가 재생성하므로, diff에서 지우면 미커밋 변경으로 다시 생긴다 — 작업과 함께 커밋하는 편이 트리가 깨끗하다.
const nextConfig: NextConfig = {
reactCompiler: true,
experimental: {
turbopackRustReactCompiler: true, // React Compiler의 Rust 포트 (Babel 우회)
useOffline: true, // 네트워크 끊김 시 throw 대신 pending 유지 + 복구 시 재시도
},
}
next dev → ready 시간 cold 34%·warm 46% 단축 (Babel을 완전히 걷어낸 경우 기준)useOffline 훅(next/offline)으로 오프라인 상태 표시 가능// ❌ Server Component에서 useState 사용
async function ServerPage() {
const [count, setCount] = useState(0) // 에러: Hook 사용 불가
}
// ❌ Client Component에서 직접 DB 접근
'use client'
async function ClientPage() {
const data = await db.user.findMany() // 위험: 클라이언트에 DB 로직 노출
}
// ❌ 캐시 설정 없이 지속 캐싱을 기대
const data = await fetch('/api/static-config') // 캐싱은 opt-in — cache: 'force-cache' 명시 필요
// ❌ Next.js 15+에서 동기 params (16에서는 호환 계층도 제거됨)
function Page({ params }: { params: { id: string } }) {
const id = params.id // ⚠️ Promise를 await 없이 접근
}
// ❌ Next.js 16에서 middleware.ts 계속 사용
// → proxy.ts로 마이그레이션 필요 (codemod 제공). Edge 런타임이 꼭 필요할 때만 middleware 유지
// ❌ Next.js 16 + 커스텀 Webpack 설정 시 그냥 next build
// → 빌드가 실패한다. next build --webpack 또는 --turbopack 명시 필요
// ❌ Next.js 16에서 revalidateTag를 1-인자로 호출
revalidateTag('posts') // TS 에러 — cacheLife 프로필 인자 필수
revalidateTag('posts', 'max') // ✅ / 즉시 반영이 필요하면 Server Action에서 updateTag('posts')
() {
token = ( ()).()
}
serverRuntimeConfig/publicRuntimeConfig | 제거 → 환경변수(NEXT_PUBLIC_, 런타임 값은 connection() 후 읽기) |
| ESLint | @next/eslint-plugin-next 기본이 Flat Config |
scroll-behavior | Next.js가 더 이상 자동으로 override 하지 않음 → 원 동작을 원하면 <html data-scroll-behavior="smooth"> |
next build 출력 | size·First Load JS 지표 제거 (RSC 환경에서 부정확) — Lighthouse 등으로 측정 |
| dev/build 동시 실행 | 출력 디렉토리 분리(next dev → .next/dev). 같은 프로젝트에서 동시 실행 가능, lockfile로 중복 인스턴스 차단 |
next/legacy/image | deprecated → next/image |
images.domains | deprecated → images.remotePatterns |