Next.js 16은 2025년 10월, Next.js Conf 2025를 앞두고 릴리스되었습니다. Turbopack의 기본 번들러 전환, 명시적 캐싱 모델 도입,
middleware.ts→proxy.ts리네이밍 등 꽤 굵직한 변화가 있었는데요. Next.js 15에서 예고됐던 변경사항들이 본격적으로 적용된 버전이기도 합니다. 하나씩 살펴보겠습니다.
Next.js 15에서 params, searchParams, cookies(), headers(), draftMode() 등이 비동기(Promise)로 변경되었지만, 동기적 접근도 경고만 띄우고 허용해줬습니다. Next.js 16부터는 동기 접근이 완전히 제거됩니다. 반드시 await를 사용해야 합니다.
// page.tsx
export default function Page({ params, searchParams }) {
const { slug } = params; // ⚠️ 경고는 뜨지만 동작
const { query } = searchParams; // ⚠️ 동일
return {slug} - {query};
}
// page.tsx (Server Component)
export default async function Page({ params, searchParams }) {
const { slug } = await params;
const { query } = await searchParams;
return {slug} - {query};
}
Client Component에서는 React.use()를 사용합니다.
'use client';
import { use } from 'react';
export default function Page({ params }) {
const { slug } = use(params);
return {slug};
}
cookies(), headers() 역시 동일합니다.
import { cookies, headers } from 'next/headers';
export default async function Page() {
const cookieStore = await cookies();
const headersList = await headers();
// ...
}
opengraph-image, twitter-image, icon, apple-icon의 Image 함수와 generateSitemaps에서 전달되는 params도 이제 Promise입니다. 놓치기 쉬우니 주의하세요.
npx next typegen
이 명령어를 실행하면 전역으로 사용 가능한 타입 헬퍼가 자동 생성되어, async params와 searchParams에 대한 타입 안전한 접근이 가능해집니다.
middleware.ts → proxy.ts 리네이밍아마 가장 눈에 띄는 변경사항일 겁니다. middleware.ts가 proxy.ts로 이름이 바뀌었습니다.
"middleware"라는 이름이 Express.js의 미들웨어와 혼동을 일으킨다는 피드백이 많았습니다. 실제로 Next.js의 middleware는 앱 앞단의 네트워크 프록시 역할을 하는 것이지, Express 미들웨어처럼 요청 파이프라인 중간에 끼어드는 것이 아닙니다. "proxy"라는 이름이 이 역할을 더 정확하게 표현합니다.
파일명과 함수명만 바꾸면 됩니다. 로직은 동일합니다.
// Before: middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
if (!request.cookies.get('token')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*'],
};
// After: proxy.ts
import { NextRequest, NextResponse } from 'next/server';
export function proxy(request: NextRequest) {
if (!request.cookies.get('token')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*'],
};
proxy.ts는 Node.js 런타임에서 실행됩니다middleware.ts를 유지할 수 있습니다npx @next/codemod upgrade major
Next.js 16에서 가장 중요한 아키텍처 변화입니다. 기존의 암묵적 캐싱(implicit caching) 모델이 명시적 캐싱(explicit caching) 모델로 완전히 바뀌었습니다.
Next.js 13~15의 App Router는 기본적으로 거의 모든 것을 캐시했습니다. fetch()의 기본값이 cache: 'force-cache'였고, 정적 렌더링이 디폴트였죠. 이건 예측하기 어렵고, 많은 개발자들이 혼란을 겪었습니다.
"use cache" 디렉티브이제 모든 동적 코드는 기본적으로 요청 시점에 실행됩니다. 캐싱이 필요한 부분만 "use cache"를 선언하면 됩니다.
// app/posts/page.tsx
"use cache";
export default async function PostsPage() {
const posts = await fetchPosts();
return ;
}
// 같은 페이지 내에서 컴포넌트마다 다른 캐싱 전략 가능
async function CachedSidebar() {
"use cache";
const data = await fetchSidebarData();
return ;
}
export default async function Page() {
// 이 부분은 매 요청마다 실행
const user = await getCurrentUser();
return (
Welcome, {user.name}
{/* 이 부분만 캐시됨 */}
);
}
async function getProducts() {
"use cache";
const res = await fetch('https://api.example.com/products');
return res.json();
}
"use cache"에도 종류가 있습니다.
| 디렉티브 | 설명 |
|---|---|
"use cache" | 기본값. 인메모리 LRU 캐시 사용 |
"use cache: remote" | 플랫폼이 제공하는 원격 캐시 핸들러 사용 (네트워크 라운드트립 발생) |
"use cache: private" | 컴플라이언스 요구사항이 있거나, 런타임 데이터를 인자로 전달하기 어려운 경우 |
cacheLife로 캐시 수명 제어기존의 export const dynamic = 'force-static' 패턴은 "use cache" + cacheLife()로 대체됩니다.
import { cacheLife } from 'next/cache';
async function getStaticContent() {
"use cache";
cacheLife('max'); // 최대 기간 캐싱
return await fetchContent();
}
"use cache" 안에서 런타임 API 직접 사용 불가// ❌ 잘못된 사용
async function getCachedData() {
"use cache";
const cookie = await cookies(); // 에러!
return await fetchData(cookie.get('token'));
}
// ✅ 올바른 사용 — 외부에서 읽어서 인자로 전달
async function getCachedData(token: string) {
"use cache";
return await fetchData(token);
}
export default async function Page() {
const cookieStore = await cookies();
const token = cookieStore.get('token')?.value ?? '';
const data = await getCachedData(token);
return {data};
}
revalidateTag() API 변경revalidateTag() 함수의 시그니처가 변경되었습니다. 이제 두 번째 인자로 cacheLife 프로파일을 요구하여, stale-while-revalidate 동작을 명시적으로 제어합니다.
// Before (Next.js 15)
revalidateTag('posts');
// After (Next.js 16)
revalidateTag('posts', 'max'); // cacheLife 프로파일 명시 필요
experimental.ppr 플래그 제거이전까지 실험적으로 제공되던 experimental.ppr 설정이 제거되었습니다. Partial Pre-Rendering은 이제 Cache Components 설정을 통해 제어됩니다.
// next.config.ts
// Before (Next.js 15)
const config = {
experimental: {
ppr: true,
},
};
// After (Next.js 16) — cacheComponents 옵션 사용
const config = {
cacheComponents: true,
};
Next.js 16에서 Turbopack이 기본 번들러가 되었습니다. 더 이상 --turbopack 플래그가 필요 없습니다.
next dev → 자동으로 Turbopack 사용next build → 자동으로 Turbopack 사용커스텀 Webpack 설정이 있는 프로젝트는 계속 Webpack을 사용할 수 있습니다.
| 제거된 기능 | 대안 |
|---|---|
| AMP 지원 | 별도 구현 필요 |
| 레거시 Runtime Configs | 환경 변수 사용 |
next lint 래퍼 | ESLint 직접 사용 |
@next/font 외부 패키지 | next/font 빌트인 사용 |
동기 params/searchParams | await 또는 React.use() |
| 항목 | Next.js 15 | Next.js 16 |
|---|---|---|
| Node.js | 18.17+ | 20.9+ |
| TypeScript | 4.5.0+ | 5.1.0+ |
| React | 19 RC | 18.2+ (App Router는 19.2 권장) |
AI 에이전트가 앱의 컨텍스트를 이해하고 디버깅을 도와주는 Model Context Protocol 통합이 추가되었습니다. 브라우저와 서버 로그를 통합 뷰로 제공하고, 에러 스택 트레이스를 AI에게 자동 전달합니다. Cursor, Claude 같은 AI 코딩 도구와 연동하여 개발 워크플로우를 크게 개선할 수 있습니다.
React Compiler가 1.0에 도달했고 Next.js 16에서 통합 지원됩니다. 기본값은 비활성화 상태이지만, 설정으로 활성화할 수 있습니다.
Next.js 15에서 16으로 업그레이드할 때 확인할 항목들입니다.
params, searchParams에 await 추가cookies(), headers(), draftMode()에 await 추가middleware.ts → proxy.ts로 리네이밍, 함수명도 proxy로 변경experimental.ppr 설정 제거, cacheComponents 옵션으로 전환export const dynamic = 'force-static' → "use cache" + cacheLife() 전환revalidateTag() 호출부에 cacheLife 프로파일 추가npx @next/codemod upgrade major
이 명령어 하나로 대부분의 마이그레이션을 자동 처리할 수 있습니다. 의존성 업데이트, codemod 적용, 파일 리네이밍까지 안내해줍니다.
Next.js 16은 15에서 예고했던 변경사항들을 본격적으로 적용한 버전입니다. 특히 캐싱 모델의 전환은 App Router 도입 이래 가장 큰 아키텍처 변화라고 할 수 있습니다. 암묵적이고 예측하기 어려웠던 캐싱이 "use cache" 디렉티브를 통해 완전히 명시적으로 바뀌면서, 개발자가 직접 캐싱 전략을 통제할 수 있게 되었습니다.
Turbopack의 기본 번들러 전환도 체감 성능에 큰 영향을 주는 변화입니다. 아직 커스텀 Webpack 설정이 필요한 프로젝트도 있겠지만, 신규 프로젝트라면 Turbopack의 빌드 속도 개선을 바로 누릴 수 있습니다.
마이그레이션이 부담스럽다면 npx @next/codemod upgrade major 부터 돌려보세요. 생각보다 많은 부분을 자동으로 처리해줍니다.
참고 자료: Next.js 16 공식 블로그 · Upgrading Guide · Next.js 16.1