목차
- Next.js 16 proxy.ts: middleware에서 바뀐 것들
- Next.js 16 비동기 전용 Dynamic API 변경사항
- Turbopack 기본 번들러 전환과 Next.js 16 빌드 성능
- Next.js 16 Cache Components: “use cache” 디렉티브
- Next.js 16 updateTag와 revalidateTag 캐시 갱신 비교
- Next.js 16 마이그레이션 체크리스트
- Next.js 16 업그레이드 이후 다음 단계
// proxy.ts
export default function proxy(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url));
}
이 코드가 뭘까? Next.js 16 새 기능 변경사항 중 가장 먼저 눈에 띄는 부분이다. 기존 middleware.ts가 proxy.ts로 이름이 바뀌었다. 파일명만 바뀐 게 아니라 런타임도 달라졌고, 내보내는 함수명도 변경되었다. Next.js 15까지 쓰던 코드가 16에서 그대로 동작하지 않는 breaking change가 여러 개 포함되어 있으므로, 업그레이드 전에 반드시 확인이 필요하다.
이 글에서는 Next.js 16의 주요 breaking change 3가지와 신규 API 2가지를 코드 예제 중심으로 정리한다.
Next.js 16 proxy.ts: middleware에서 바뀐 것들

Next.js 16에서 가장 큰 변경은 middleware.ts가 proxy.ts로 이름이 변경된 것이다. 단순 리네임이 아니라 런타임 자체가 달라진 셈이다.
변경 항목을 구체적으로 나열하면:
- 파일명:
middleware.ts→proxy.ts - 내보내는 함수:
middleware→proxy - 런타임: Edge Runtime → Node.js Runtime
- 설정 키:
skipMiddlewareUrlNormalize→skipProxyUrlNormalize
기존 middleware.ts는 Edge 런타임 전용으로 deprecated 상태가 되었다. Next.js 16 릴리스 노트에서 이 변경의 배경을 확인할 수 있다.
// proxy.ts
export default function proxy(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url));
}
이 코드에서 주목할 점은 함수명이 proxy라는 것이다. 기존에 export function middleware()로 내보내던 방식과 호환되지 않는다. next.config.ts에서 URL 정규화 스킵 옵션을 쓰고 있었다면 키 이름도 함께 바꿔야 하는 구조다.
Next.js 16에서 middleware.ts는 Edge 런타임 전용 deprecated 상태다. Node.js 런타임 기능(DB 직접 접근, fs 모듈 등)을 쓰려면 반드시 proxy.ts로 전환해야 한다.
proxy.ts 마이그레이션 순서
실제로 전환할 때는 아래 순서를 따르면 된다:
middleware.ts를proxy.ts로 파일명 변경export function middleware를export default function proxy로 수정next.config.ts에서skipMiddlewareUrlNormalize를skipProxyUrlNormalize로 변경- Edge 전용 API 사용 여부를 확인하고, Node.js 런타임에서 동작하는지 테스트
Next.js 16 비동기 전용 Dynamic API 변경사항
Next.js 16에서 두 번째로 큰 breaking change는 Dynamic API의 동기 접근이 완전 제거된 것이다. cookies(), headers(), draftMode(), params, searchParams 모두 await로만 접근할 수 있게 되었다.
Next.js 15에서는 동기 접근 시 deprecation 경고만 나왔지만, 16에서는 에러가 발생한다. 기존 코드가 상당히 깨질 수 있는 부분이다.
// page.tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const { slug } = await props.params
const query = await props.searchParams
return <h1>Blog Post: {slug}</h1>
}
위 코드에서 props.params와 props.searchParams 모두 await를 붙여야 한다. sitemap의 id 파라미터와 opengraph-image의 params도 Promise로 변경된 것이 포인트다.
| API | Next.js 15 | Next.js 16 |
|---|---|---|
cookies() | 동기 접근 가능 (경고) | await 필수 |
headers() | 동기 접근 가능 (경고) | await 필수 |
params | 동기 접근 가능 (경고) | await 필수 |
searchParams | 동기 접근 가능 (경고) | await 필수 |
draftMode() | 동기 접근 가능 (경고) | await 필수 |
sitemap id | 동기 | Promise |
opengraph-image params | 동기 | Promise |
프로젝트에서 `cookies()`, `headers()`, `params`, `searchParams`를 `await` 없이 쓰는 곳을 찾으려면 TypeScript strict 모드를 켜고 빌드하면 된다. 타입 에러로 모든 위치가 잡히는 편이다.
비동기 전환 시 주의할 패턴
기존에 params를 구조 분해 할당으로 바로 받던 패턴이 동작하지 않게 된다는 점이 함정이다:
// ❌ Next.js 16에서 동작하지 않음
export default function Page({ params: { slug } }) {
return <h1>{slug}</h1>
}
// ✅ Next.js 16 방식
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const { slug } = await props.params
return <h1>{slug}</h1>
}
컴포넌트 함수에 async를 붙여야 하므로, 클라이언트 컴포넌트에서는 이 패턴을 직접 쓸 수 없다. 서버 컴포넌트에서 await로 값을 꺼낸 뒤 클라이언트 컴포넌트에 props로 전달하는 구조가 필요하게 되는 것이다.
Turbopack 기본 번들러 전환과 Next.js 16 빌드 성능

Turbopack이 안정화되어 Next.js 16의 기본 번들러가 되었다. next dev와 next build 모두 Turbopack을 기본 사용하며, 최대 10배 빠른 Fast Refresh와 2~5배 빠른 프로덕션 빌드를 제공한다는 것이 공식 발표 내용이다.
이것도 breaking change에 해당하는 이유가 있다. experimental.turbopack 설정이 최상위 turbopack으로 이동했기 때문이다.
// next.config.ts (Next.js 16)
const nextConfig: NextConfig = {
turbopack: {
// options
},
};
export default nextConfig;
기존에 experimental.turbopack 아래에 옵션을 넣고 있었다면 설정 경로를 바꿔야 하는 구조다. webpack을 계속 쓰고 싶은 경우 --webpack 플래그로 opt-out이 가능하다.
| 항목 | webpack (opt-out) | Turbopack (기본) |
|---|---|---|
| Fast Refresh | 기준선 | 최대 10배 빠름 |
| 프로덕션 빌드 | 기준선 | 2~5배 빠름 |
| 설정 위치 | 별도 플래그 필요 없음 | turbopack: {} (최상위) |
| 활성화 | --webpack 플래그 | 기본값 |
`next.config.ts`에서 `experimental.turbopack`을 사용 중이었다면 최상위 `turbopack`으로 옮겨야 한다. 설정 경로가 그대로면 무시되므로 빌드 옵션이 적용되지 않는 문제가 생길 수 있다.
Turbopack은 webpack의 모든 플러그인을 지원하지 않는다. 커스텀 webpack 설정이 많은 프로젝트에서는 다음 항목을 점검해야 하는 편이다:
webpack콜백 함수로 직접 로더를 추가한 경우 → Turbopack 호환 로더 확인 필요next-bundle-analyzer같은 webpack 전용 플러그인 → Turbopack 대안이 있는지 확인- 커스텀
module.rules→ Turbopack의rules설정으로 매핑 필요
규모가 큰 프로젝트에서는 한 번에 전환하기보다, 개발 환경(next dev)에서 먼저 Turbopack을 테스트하고, 프로덕션 빌드는 --webpack으로 유지하다가 점진적으로 이동하는 전략이 안전하다.
Next.js 16 Cache Components: “use cache” 디렉티브
Next.js 16은 Cache Components를 도입하면서 캐싱 모델을 근본적으로 바꿨다. "use cache" 디렉티브를 사용해 페이지·컴포넌트·함수를 명시적으로 캐싱하며, 컴파일러가 자동으로 캐시 키를 생성하는 방식이다.
기존에 쓰던 experimental.ppr과 experimental.dynamicIO 플래그는 제거되었고, cacheComponents 설정으로 대체된 것이 핵심 변화다.
const nextConfig = {
cacheComponents: true,
};
export default nextConfig;
cacheComponents: true를 설정하면 "use cache" 디렉티브를 인식하게 된다. 이 디렉티브는 "use client"나 "use server"와 같은 위치에 선언하면 되는 구조다.
Cache Components의 동작 원리를 정리하면 이렇게 되는 셈이다:
- 컴포넌트나 함수 상단에
"use cache"선언 - 컴파일러가 해당 유닛의 입력(props, 인자)을 분석해 캐시 키를 자동 생성
- 같은 입력이 들어오면 캐시된 결과를 반환
- 캐시 무효화는
updateTag()또는revalidateTag()로 처리
`experimental.ppr`과 `experimental.dynamicIO`를 next.config에서 쓰고 있었다면 제거하고 `cacheComponents: true`로 교체해야 한다. 기존 플래그가 남아있으면 설정 유효성 검사에서 경고가 발생할 수 있다.
Cache Components 활성화 체크리스트
Cache Components 적용 전 확인 항목:
next.config.ts에cacheComponents: true추가 여부experimental.ppr,experimental.dynamicIO제거 여부"use cache"디렉티브를 쓸 컴포넌트가 순수 함수(같은 입력 → 같은 출력)인지 확인- 캐시 무효화 전략 수립 (
updateTagvsrevalidateTag선택)
Next.js 16 updateTag와 revalidateTag 캐시 갱신 비교
Next.js 16에서 캐시 갱신 API도 큰 변화가 있다. updateTag()는 Server Actions 전용 신규 API로, read-your-writes 시맨틱을 제공하는 것이 가장 큰 특징이다. 캐시를 만료시키고 같은 요청 내에서 즉시 새 데이터를 읽을 수 있다.
'use server';
import { updateTag } from 'next/cache';
export async function updateUserProfile(userId: string, profile: Profile) {
await db.users.update(userId, profile);
updateTag(`user-${userId}`);
}
revalidateTag()도 변경되었다. 두 번째 인자로 cacheLife 프로필(예: 'max')이 필수가 된 것이다. 기존처럼 태그만 넘기면 타입 에러가 발생하게 된다.
refresh()는 캐시되지 않은 데이터만 새로고침하는 Server Actions 전용 API로 별도 추가되었다.
| API | 용도 | 캐시 데이터 | 비캐시 데이터 | 같은 요청 내 반영 |
|---|---|---|---|---|
updateTag() | 캐시 즉시 갱신 | ✅ 만료+즉시 반영 | ❌ | ✅ (read-your-writes) |
revalidateTag() | 캐시 재검증 예약 | ✅ 다음 요청에 반영 | ❌ | ❌ |
refresh() | 비캐시 새로고침 | ❌ | ✅ | ✅ |
updateTag()를 쓸지 revalidateTag()를 쓸지는 사용자 경험 요구사항에 따라 갈리는 편이다. 프로필 수정 후 즉시 변경된 값을 보여줘야 하는 경우라면 updateTag()가 적합하고, 백그라운드에서 캐시가 갱신되어도 괜찮은 경우라면 revalidateTag()로 충분하다.
사용자가 폼을 제출하고 바로 결과 페이지를 볼 때 이전 캐시가 보이는 문제가 있었다면, updateTag()로 해결 가능하다. Server Action 내에서 DB 업데이트 직후 호출하면 같은 응답에 새 데이터가 반영되기 때문이다.
Next.js 16 breaking change 3가지와 신규 API 2가지의 마이그레이션 순서를 정리했다. breaking change부터 처리하고, 신규 API는 이후에 점진 적용하는 전략이 안전하다.
1단계: Breaking Change 처리 (필수)
[마이그레이션 체크리스트]
├── middleware.ts → proxy.ts
│ ├── 파일명 변경
│ ├── export function middleware → export default function proxy
│ └── skipMiddlewareUrlNormalize → skipProxyUrlNormalize
├── 비동기 API 전환
│ ├── cookies() → await cookies()
│ ├── headers() → await headers()
│ ├── params → await props.params
│ ├── searchParams → await props.searchParams
│ └── draftMode() → await draftMode()
└── Turbopack 설정
├── experimental.turbopack → turbopack (최상위)
└── webpack 의존성 호환 테스트
2단계: 신규 API 적용 (선택)
cacheComponents: true활성화 후"use cache"디렉티브 적용revalidateTag()호출부에 두 번째 인자cacheLife프로필 추가- 즉시 반영이 필요한 Server Action에
updateTag()도입 refresh()API로 비캐시 데이터 새로고침 로직 분리
한국어 공식 마이그레이션 가이드는 아직 제공되지 않는 상태다. 영문 업그레이드 가이드를 기준으로 작업해야 한다.
Next.js 16 업그레이드 이후 다음 단계
Next.js 16 새 기능 변경사항의 핵심은 세 가지 breaking change(proxy.ts 전환, 비동기 API 강제, Turbopack 기본화)와 두 가지 신규 API(Cache Components, updateTag)로 요약되는 셈이다.
마이그레이션 완료 후에는 세 영역을 추가로 점검해야 한다. Next.js 16 Cache Components를 활용한 세분화된 캐시 전략 설계가 첫 번째다. 페이지 단위가 아니라 컴포넌트·함수 단위로 캐시를 제어할 수 있게 되었으므로, 데이터 갱신 빈도에 따라 캐시 경계를 어디에 둘지 설계하는 작업이 중요해진다.
Next.js Turbopack 기본 번들러 환경에서의 빌드 파이프라인 최적화도 검토 대상이다. webpack 플러그인 생태계에 의존하던 부분을 Turbopack 네이티브 방식으로 전환하면 빌드 속도 이점을 최대로 끌어낼 수 있게 된다. 그리고 React 19.2와 Next.js 통합, 특히 View Transitions 같은 기능은 실전 예제가 아직 부족한 상태이므로 공식 문서 업데이트를 지켜볼 필요가 있다.
Next.js 16 비동기 params searchParams 패턴에 익숙해지면, 서버 컴포넌트와 클라이언트 컴포넌트 간 데이터 흐름 설계도 자연스럽게 정리될 것이다. Next.js 16 proxy.ts middleware 변경까지 적용하고 나면 Next.js 16 업그레이드의 핵심 작업은 마무리된다.
관련 글
- Next.js 16 시작하기 — proxy.ts 전환·Turbopack 설정 7단계 완전 가이드 – Next.js 16은 middleware.ts를 proxy.ts로 대체하고 Turbopack을 기본 번들러로 전환했다. 프로젝트 생성부터 …
- 검색 엔진 최적화(SEO) 기능이란 무엇인가? – [ez-toc] 검색 엔진 최적화(SEO) 기능이란 무엇인가? 검색 엔진 최적화 (SEO)는 우리가 흔히 사용하는 인터넷이 우리 삶의 필수…