목차
- Next.js 16 Breaking Changes 전체 범위 파악
- 비동기 API 전환: await 없이는 빌드 불가
- middleware.ts에서 proxy.ts로의 전환
- Turbopack 기본 번들러 전환과 webpack 호환 전략
- 캐시 API 변경: revalidateTag와 Next.js 16 신규 API
- 런타임 요구사항과 제거된 기능 정리
- Next.js 16 Breaking Changes 마이그레이션 단계별 체크리스트
- 마이그레이션 한계점과 아직 해결되지 않은 영역
title: "Next.js 15에서 16으로 마이그레이션: Breaking Changes 완전 가이드" slug: "nextjs-16-breaking-changes-migration" description: "Next.js 16 업그레이드 시 반드시 알아야 할 비동기 API 강제화, proxy.ts 전환, Turbopack 기본화, 캐시 API 변경 등 핵심 breaking changes를 코드 레벨에서 단계별로 정리한다." tags: ["Next.js", "마이그레이션", "TypeScript", "React", "Turbopack"] date: "2026-06-03"
Next.js 15에서 16으로 올리려는데, next build가 갑자기 실패하기 시작했다면 어디부터 손대야 할까? Next.js 16 breaking changes 마이그레이션 가이드를 찾고 있다면, 변경 범위가 예상보다 넓다는 점을 먼저 인지해야 한다. 비동기 API 강제화, 번들러 교체, 미들웨어 파일명 변경까지 — 단순 버전 범프가 아닌 아키텍처 수준의 전환이 필요한 항목이 여러 개 존재하기 때문이다.
이 글은 Next.js 16에서 하위 호환이 깨지는 핵심 변경사항 4가지를 분석하고, 각 항목에 대해 기존 코드가 왜 동작하지 않는지, 어떻게 전환해야 하는지를 코드 레벨에서 다룬다. 업그레이드 명령 한 줄로 시작해서 빌드 성공까지 도달하는 순서를 제시하는 것이 목표다.
Next.js 16 Breaking Changes 전체 범위 파악
마이그레이션의 첫 단계는 변경 범위를 정확히 파악하는 것이다. Next.js 16은 단일 기능 변경이 아니라 런타임, 번들러, 라우팅 레이어, 캐시 API까지 동시에 바뀐 메이저 릴리스에 해당한다.
| 영역 | 변경 내용 | 영향도 |
|---|---|---|
| 비동기 API | cookies(), headers(), params 등 동기 접근 제거 | 전체 페이지·레이아웃 |
| 미들웨어 | middleware.ts → proxy.ts 전환 | 인증·리다이렉트 로직 |
| 번들러 | Turbopack 기본화, webpack은 플래그 필요 | 빌드 파이프라인 |
| 캐시 | revalidateTag 시그니처 변경, updateTag 신규 | ISR·데이터 갱신 |
| 런타임 | Node.js 20.9+ 필수, AMP·next lint 제거 | CI/CD 환경 |
이 표의 모든 항목이 breaking change이거나 기존 코드에 직접 영향을 주는 변경이다. 한 가지 놓치면 빌드 실패 또는 런타임 에러로 이어지는 셈이다.
Next.js 16은 Node.js 18 지원을 중단했다. CI 환경과 로컬 개발 환경 모두 Node.js 20.9 이상인지 먼저 확인해야 한다. `node -v` 출력이 v18.x면 업그레이드 자체가 불가능하다.
# 업그레이드 명령
npx @next/codemod@canary upgrade latest
이 명령은 패키지 버전 업데이트와 함께 일부 코드 패턴을 자동 변환해 준다. 다만 모든 breaking change를 커버하지는 않으므로, 아래 섹션에서 수동 전환이 필요한 항목을 하나씩 짚어본다.
비동기 API 전환: await 없이는 빌드 불가

Next.js 16에서 가장 광범위한 영향을 미치는 변경은 cookies(), headers(), draftMode(), params, searchParams의 동기 접근 완전 제거다. Next.js 15에서는 임시 동기 호환 레이어가 있었기 때문에 const { slug } = params처럼 직접 접근해도 경고만 표시되었다. 16에서는 이 호환 레이어가 삭제되어 런타임 에러가 발생한다.
기존 코드가 실패하는 이유
15 이하에서 흔히 쓰던 패턴은 다음과 같은 형태다.
// Next.js 15 — 동기 접근 (16에서 에러)
export default function Page({ params }: { params: { slug: string } }) {
const { slug } = params // ← 16에서 TypeError
return <h1>{slug}</h1>
}
params 자체가 Promise 객체로 변경되었기 때문에, 구조 분해 할당을 동기로 하면 undefined이거나 타입 에러가 발생하게 된다. headers()와 cookies()도 마찬가지 구조다.
전환 패턴
모든 Dynamic API 접근에 await를 추가하고, 컴포넌트 함수를 async로 선언해야 한다. npx next typegen 명령으로 PageProps, LayoutProps, RouteContext 타입 헬퍼를 자동 생성할 수 있어서 타입 정의를 수동으로 작성할 필요가 줄어든다.
// Next.js 16 — async params 필수
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>
}
`npx next typegen`을 실행하면 라우트 구조 기반으로 PageProps, LayoutProps 등의 타입이 자동 생성된다. 수동 타입 정의보다 정확하고, 라우트 변경 시 타입도 자동 갱신되는 이점이 있다.
이 변경은 App Router를 사용하는 모든 페이지와 레이아웃에 영향을 준다. grep -r "params\." app/ 같은 명령으로 동기 접근 패턴을 먼저 검색하고, 파일 단위로 전환하는 것이 안전하다. 서버 컴포넌트뿐 아니라 generateMetadata, generateStaticParams 내부에서 params를 사용하는 경우에도 동일하게 await가 필요하다.
특히 cookies()와 headers()를 미들웨어가 아닌 서버 컴포넌트에서 직접 호출하는 패턴이 있다면, 해당 컴포넌트를 모두 async function으로 바꿔야 하는 셈이다. 프로젝트 규모가 클수록 이 작업의 분량이 커지므로, codemod가 커버하지 못한 부분을 수동으로 확인하는 과정이 반드시 필요하다.
middleware.ts에서 proxy.ts로의 전환
Next.js 16에서 두 번째로 큰 breaking change는 미들웨어 파일의 rename이다. middleware.ts가 proxy.ts로 대체되었고, export 함수명도 default function middleware에서 default function proxy로 변경되었다. 기존 middleware.ts는 deprecated 상태로, 향후 완전히 제거될 예정이다.
런타임 변경의 의미
단순 파일명 변경처럼 보이지만, 근본적인 차이는 런타임에 있다. proxy.ts는 Node.js 런타임에서 실행되며, 기존 middleware의 Edge 런타임은 지원하지 않는다. 이것은 Edge 런타임 특화 API(EdgeRuntime, crypto.subtle 등)를 사용하던 미들웨어가 있다면 단순 rename으로 끝나지 않는다는 뜻이기도 하다.
// proxy.ts
export default function proxy(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url));
}
전환 체크리스트
proxy.ts 전환 시 확인해야 할 항목을 정리하면 다음과 같다.
- 파일명:
middleware.ts→proxy.ts(프로젝트 루트) - 함수명:
export default function middleware→export default function proxy - config 키:
skipMiddlewareUrlNormalize→skipProxyUrlNormalize로 rename - Edge 전용 코드: Node.js 런타임에서 동작하는지 확인 필요
proxy.ts에서 Edge 런타임을 사용하는 방법은 아직 공식 문서에 명시되어 있지 않다. 향후 minor release에서 제공될 예정으로만 언급되어 있으므로, Edge 런타임 의존도가 높은 프로젝트는 16 전환 시점을 신중하게 판단해야 한다.
기존 middleware에서 export const config = { matcher: [...] } 형태로 경로를 제한하던 패턴은 proxy.ts에서도 동일하게 동작한다. 파일명과 함수명만 바꾸면 matcher 로직 자체는 변경 없이 이식 가능하다. 다만 skipMiddlewareUrlNormalize를 사용 중이었다면 skipProxyUrlNormalize로 키 이름을 변경해야 한다.
인증, A/B 테스트, 지역 기반 리다이렉트처럼 미들웨어가 핵심 인프라 역할을 하는 프로젝트에서는 proxy.ts 전환 후 통합 테스트를 반드시 돌려야 한다. Node.js 런타임 전환으로 인해 cold start 특성이 달라질 수 있기 때문이다.
Turbopack 기본 번들러 전환과 webpack 호환 전략

Next.js 16에서 Turbopack이 기본 번들러가 되었다. next dev와 next build 모두 Turbopack을 사용하며, 커스텀 webpack 설정이 있으면 빌드가 실패한다. 이것은 webpack 플러그인이나 로더에 의존하는 프로젝트에 즉각적인 영향을 미치는 변경이다.
빌드 실패 시나리오
next.config.js에 webpack 키가 있는 프로젝트는 16으로 업그레이드하는 순간 다음과 같은 에러를 만나게 된다.
Error: Custom webpack configuration detected.
Turbopack does not support custom webpack config.
Use `next build --webpack` to fall back to webpack.
webpack 유지 전략
당장 Turbopack으로 전환할 수 없는 경우, --webpack 플래그로 기존 번들러를 유지하는 방법이 있다.
{
"scripts": {
"dev": "next dev",
"build": "next build --webpack",
"start": "next start"
}
}
next dev는 Turbopack이 기본이지만, 개발 서버에서도 webpack을 쓰려면 next dev --webpack으로 명시해야 한다. 이 방식은 과도기 전략이며, 장기적으로는 Turbopack 호환으로 전환하는 것이 권장된다.
config 키 이동
experimental.turbopack 설정을 사용하던 프로젝트는 top-level turbopack 키로 이동해야 한다. 기존 위치에 남겨두면 경고가 출력되며, 향후 제거 대상에 해당한다.
// next.config.ts — 변경 전
const nextConfig = {
experimental: {
turbopack: { /* ... */ }
}
}
// next.config.ts — 변경 후
const nextConfig = {
turbopack: { /* ... */ }
}
커스텀 webpack 설정을 Turbopack으로 마이그레이션하는 구체적인 매핑표는 공식 문서에 아직 제공되지 않는다. webpack 로더별 대응 방법을 개별적으로 확인해야 하는 상황이므로, 복잡한 webpack 설정을 가진 프로젝트는 --webpack 플래그로 먼저 빌드를 통과시킨 뒤 점진적으로 전환하는 접근이 현실적이다.
Turbopack은 개발 서버 기동 시간과 HMR 속도에서 webpack 대비 유의미한 차이를 보인다. Next.js 16 릴리스 블로그에서도 Turbopack 기본화를 이번 릴리스의 핵심 변경으로 다루고 있다. 빌드 속도 개선이 필요한 프로젝트라면 webpack 의존을 줄이는 방향으로 로드맵을 잡는 것이 합리적인 셈이다.
캐시 API 변경: revalidateTag와 Next.js 16 신규 API
Next.js 16에서 캐시 관련 API도 시그니처 변경과 신규 도입이 동시에 이루어졌다. cacheLife와 cacheTag는 unstable_ 접두사 없이 안정화되었고, revalidateTag()의 호출 방식이 달라졌다.
revalidateTag 시그니처 변경
기존에 revalidateTag('posts') 형태로 태그만 전달하던 패턴이 deprecated되었다. Next.js 16에서는 두 번째 인자로 cacheLife 프로필을 반드시 지정해야 한다.
// Before
revalidateTag('posts')
// After — cacheLife 프로필 필수
revalidateTag('posts', 'max')
// 즉시 반영이 필요하면 Server Action에서 updateTag 사용
import { updateTag } from 'next/cache'
updateTag(`user-${userId}`)
updateTag와 refresh의 차이
새로 도입된 updateTag() API는 Server Actions 전용으로, read-your-writes 시맨틱을 제공한다. 사용자가 데이터를 수정한 직후 같은 세션에서 변경 결과를 즉시 확인해야 하는 시나리오에 적합하다. 반면 refresh() API는 캐시되지 않은 데이터만 갱신하는 용도에 해당한다.
| API | 용도 | 실행 컨텍스트 | 시맨틱 |
|---|---|---|---|
| revalidateTag | 태그 기반 캐시 무효화 | Server Component / Action | cacheLife 프로필 필수 |
| updateTag | 즉시 캐시 갱신 | Server Action 전용 | read-your-writes |
| refresh | 비캐시 데이터 갱신 | Client Component | 캐시 미사용 데이터만 |
`unstable_cacheLife`, `unstable_cacheTag`로 사용하던 API가 `cacheLife`, `cacheTag`로 정식 안정화되었다. 기존에 `unstable_` 접두사를 쓰던 코드는 접두사를 제거하면 된다.
Cache Components(use cache)를 활용한 실전 마이그레이션 패턴은 공식 문서에 별도 가이드 페이지가 있으나, 상세 예제가 부족한 상태다. 공식 문서에 명시되어 있지 않은 부분이므로, use cache 도입은 공식 예제가 보강된 이후에 검토하는 것이 안전하다.
런타임 요구사항과 제거된 기능 정리
Next.js 16은 최소 요구사항을 대폭 올렸다. 이 변경은 코드 수정이 아닌 인프라 환경 점검에 해당하므로, CI/CD 파이프라인과 배포 환경을 먼저 확인해야 한다.
최소 버전 요구사항
- Node.js: 20.9 이상 (18 지원 중단)
- TypeScript: 5.1 이상
- 브라우저: Chrome/Edge/Firefox 111+, Safari 16.4+
Node.js 18을 사용 중인 CI 환경이라면 러너 이미지를 먼저 업데이트해야 한다. GitHub Actions 기준 actions/setup-node@v4에서 node-version: '20'으로 지정하면 되는 셈이다.
제거된 기능 목록
다음 기능은 16에서 완전히 제거되어 대체재를 찾거나 코드에서 삭제해야 한다.
- AMP 지원:
amp관련 설정과 컴포넌트가 모두 제거되었다. AMP 페이지를 운영 중이었다면 별도 AMP 프레임워크로 분리해야 한다. - next lint 명령어:
next lint가 제거되었다. ESLint를 직접 설정하여eslint .형태로 실행해야 한다. - serverRuntimeConfig / publicRuntimeConfig:
next.config.js의 이 두 설정이 완전히 삭제되었다. 환경 변수(process.env)로 전환해야 한다. - experimental.ppr 플래그: Partial Prerendering 관련 실험 플래그가 제거되었다.
React Compiler 통합
React Compiler가 stable로 승격되어 reactCompiler: true로 활성화할 수 있게 되었다. 이 기능은 breaking change는 아니지만, 16 업그레이드 시점에 함께 도입을 검토할 만한 항목이다.
// next.config.ts — React Compiler 활성화
const nextConfig = {
reactCompiler: true,
}
export default nextConfig
React Compiler는 useMemo, useCallback, React.memo를 수동으로 작성하지 않아도 컴파일러가 자동으로 최적화를 수행한다. 기존에 메모이제이션 코드를 광범위하게 사용하던 프로젝트에서는 코드 단순화 효과를 기대할 수 있다. 다만 모든 컴포넌트 패턴에서 정확히 동작하는지는 프로젝트별로 검증이 필요하다.
프로젝트 전체에 한 번에 적용하기보다는, 특정 디렉토리나 컴포넌트 단위로 동작을 확인한 뒤 범위를 넓히는 접근이 안전하다. 컴파일러가 예상과 다른 최적화를 수행하는 케이스가 있을 수 있기 때문이다.
Next.js 16 Breaking Changes 마이그레이션 단계별 체크리스트
각 변경사항의 의존 관계를 고려한 실행 순서는 다음과 같다. 순서가 중요한 이유는 의존 관계 때문이다. Node.js 버전이 맞지 않으면 설치 자체가 실패하고, 비동기 API 전환 없이는 빌드가 통과하지 않는다.
1단계: 환경 점검
Node.js 20.9+, TypeScript 5.1+ 확인이 선행되어야 한다. CI/CD 파이프라인의 러너 이미지도 함께 점검한다.
node -v # v20.9.0 이상 확인
tsc -v # Version 5.1 이상 확인
2단계: 패키지 업그레이드 및 codemod 실행
npx @next/codemod@canary upgrade latest
npx next typegen
codemod가 자동 변환한 파일을 git diff로 확인하고, 변환되지 않은 패턴을 수동으로 찾는다.
3단계: 비동기 API 전환
params, searchParams, cookies(), headers(), draftMode()를 사용하는 모든 파일에서 await를 추가한다. generateMetadata와 generateStaticParams 내부도 포함되는 점을 놓치기 쉽다.
4단계: proxy.ts 전환
middleware.ts를 proxy.ts로 rename하고, export 함수명을 proxy로 변경한다. skipMiddlewareUrlNormalize 설정이 있다면 skipProxyUrlNormalize로 키를 변경한다.
5단계: 번들러 전환 판단
커스텀 webpack 설정이 없다면 Turbopack이 자동 적용된다. 있다면 next build --webpack으로 우선 빌드를 통과시킨 뒤, Turbopack 호환 전환을 별도 태스크로 관리한다. experimental.turbopack 설정은 top-level turbopack으로 이동한다.
6단계: 캐시 API 수정
revalidateTag 호출에 cacheLife 프로필 인자를 추가한다. unstable_cacheLife, unstable_cacheTag 접두사를 제거한다. Server Action에서 즉시 반영이 필요한 패턴은 updateTag로 전환을 검토한다.
7단계: 제거된 기능 대응
serverRuntimeConfig / publicRuntimeConfig를 process.env로 전환한다. next lint를 사용 중이었다면 ESLint 직접 실행으로 변경한다. AMP 페이지가 있었다면 분리 전략을 수립한다.
마이그레이션 순서 요약
──────────────────────
환경 점검 → codemod → async API → proxy.ts → 번들러 → 캐시 API → 제거 기능
① ② ③ ④ ⑤ ⑥ ⑦
마이그레이션 한계점과 아직 해결되지 않은 영역
Next.js 16 breaking changes 마이그레이션 가이드를 따라도 모든 문제가 해결되는 것은 아니다. 공식 문서 자체에 빈 영역이 존재하며, 프로젝트 특성에 따라 추가 작업이 필요한 부분이 있기 때문이다.
첫째, proxy.ts에서 Edge 런타임 대응 방안이 공식 문서에 명시되어 있지 않다. Edge에서 실행되던 미들웨어 로직을 Node.js 런타임으로 이식하면 cold start 특성과 레이턴시 프로파일이 달라진다. Vercel Edge Functions이나 Cloudflare Workers 기반으로 미들웨어를 운영하던 프로젝트는 별도 아키텍처 검토가 필요한 상황이다.
둘째, Turbopack으로 커스텀 webpack 설정을 마이그레이션하는 구체적 매핑표가 없다. svg-loader, babel-plugin-styled-components 같은 서드파티 webpack 플러그인의 Turbopack 대응 여부를 개별적으로 확인해야 하며, 일부는 아직 미지원 상태일 수 있다.
셋째, Cache Components(use cache) 실전 마이그레이션 패턴의 상세 예제가 부족하다. 기존 ISR/SSG 패턴에서 use cache로 전환하려면 캐시 경계 설계가 필요한데, 이 부분은 공식 가이드가 보강되기를 기다려야 하는 상태다.
넷째, 한국어 공식 문서가 제공되지 않는다. nextjs.org 한국어 번역이 미제공 상태이므로, 영문 문서를 직접 참조해야 하는 점도 팀 내 온보딩에서 고려할 부분이기도 하다.
모든 변경을 한 번에 적용하기보다는, 비동기 API 전환과 proxy.ts 전환을 먼저 완료하고 빌드를 통과시킨 뒤, 캐시 API와 Turbopack 전환을 별도 PR로 분리하는 접근이 리스크를 줄인다.
관련 글
- GPT-5 출시 새 기능 4가지 완전 정리 — API 파라미터·마이그레이션 실전 가이드 – GPT-5 시리즈(5.0~5.2)의 신규 API 파라미터, 모델 변형, 마이그레이션 주의사항을 Q&A 형식으로 정리한다. Verbosity…