목차
- Next.js 16 핵심 Breaking Change 요약
- Next.js 16 프로젝트 생성과 초기 설정
- proxy.ts 전환 — middleware.ts를 대체하는 방법
- Turbopack 기본 전환과 Next.js 16 빌드 설정
- Cache Components와 “use cache” 지시어 활용
- React 19.2와 React Compiler 설정
- Next.js 16 TypeScript 설정 최적화 포인트
- 다음 단계 — 배포 설정과 고급 기능 도입
Next.js 16 시작하기 가이드를 찾고 있다면 가장 먼저 알아야 할 것이 두 가지 있다. middleware.ts가 proxy.ts로 대체되었고, Turbopack이 기본 번들러가 되었다는 점이다. 이 두 가지 breaking change를 모르고 기존 Next.js 15 코드를 그대로 올리면 빌드가 실패하거나 미들웨어가 동작하지 않는 상황이 생긴다. 더욱이 프로젝트 생성부터 각 변경점 대응까지 TypeScript 기반으로 단계별로 다룬다.
Next.js 16 핵심 Breaking Change 요약
Next.js 16으로 넘어오면서 하위 호환이 깨지는 변경이 두 건 발생했다. 기존 프로젝트를 업그레이드하든 신규로 시작하든, 이 두 가지를 먼저 파악해야 한다.
첫 번째는 middleware.ts → proxy.ts 전환이다. 내보내는 함수명도 middleware에서 proxy로 바뀌었다. proxy.ts는 Node.js 런타임에서 실행된다. 기존 middleware.ts는 Edge 런타임 전용으로 deprecated 상태가 된 것이다. 기존에 middleware.ts에서 인증 체크나 리다이렉트를 처리하던 코드는 파일명과 함수명을 모두 변경해야 동작하는 셈이다.
두 번째는 Turbopack 기본 번들러 전환이다. 이전까지는 next dev --turbopack 플래그로 opt-in 했지만, Next.js 16부터는 next dev만 실행해도 Turbopack이 작동한다. 문제는 커스텀 webpack 설정이 있는 프로젝트에서 빌드가 실패한다는 점이다. 한편 webpack을 계속 쓰려면 --webpack 플래그를 명시해야 한다. 그리고 Turbopack 설정 위치도 experimental.turbopack에서 최상위 turbopack으로 이동했다.
| 항목 | Next.js 15 | Next.js 16 |
|---|---|---|
| 미들웨어 파일 | middleware.ts | proxy.ts |
| 미들웨어 함수명 | middleware | proxy |
| 미들웨어 런타임 | Edge | Node.js |
| 기본 번들러 | webpack | Turbopack |
| webpack 사용법 | 기본 | --webpack 플래그 필요 |
| Turbopack 설정 위치 | experimental.turbopack | 최상위 turbopack |
| 캐싱 기본 동작 | 암묵적 캐싱 | 동적 코드 기본 실행 (opt-in 캐싱) |
대규모 팀에서 운영하던 프로젝트일수록 webpack 커스텀 설정이 많아 Turbopack 전환의 충격이 크다. 반면 소규모 프로젝트는 webpack 의존이 적어 전환이 비교적 수월한 편이다. 예를 들어 Next.js 16 업그레이드 가이드에서 마이그레이션 체크리스트를 확인할 수 있다.
middleware.ts는 deprecated 상태다. 당장 동작할 수 있지만 향후 제거 예정이므로, 신규 프로젝트는 물론 기존 프로젝트도 proxy.ts로 전환을 권장한다. 파일명과 함수명을 동시에 변경해야 한다.
Next.js 16 프로젝트 생성은 create-next-app으로 수행한다. 시스템 요구사항은 Node.js 20.9 이상이며, 브라우저는 Chrome 111+, Edge 111+, Firefox 111+, Safari 16.4+를 지원한다. 또한 --yes 플래그를 붙이면 TypeScript, Tailwind CSS, ESLint, App Router, Turbopack이 기본 포함된 프로젝트를 즉시 생성할 수 있다.
npx create-next-app@latest my-app --yes
cd my-app
npm run dev
이 명령 하나로 Turbopack 기반 개발 서버가 즉시 실행된다. 별도의 --turbopack 플래그가 불필요해진 것이다. 생성된 프로젝트의 package.json scripts 섹션은 다음과 같은 구조를 갖는다.
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
이전 버전에서는 "dev": "next dev --turbopack" 형태로 명시했지만, Next.js 16에서는 이 플래그 자체가 불필요하다. 반대로 webpack이 필요한 경우에만 "dev": "next dev --webpack"으로 지정하는 방식이다.
Node.js 20.9 미만에서는 create-next-app 실행 자체가 실패한다. `node -v`로 버전을 확인한 뒤 진행하는 것이 안전하다. nvm 사용자라면 `nvm use 20`으로 전환하면 된다.
--yes 플래그로 생성한 프로젝트는 기본적으로 TypeScript가 활성화되어 있다. tsconfig.json이 자동 생성되고, app/ 디렉터리 아래 .tsx 파일이 배치되는 구조다.
my-app/
├── app/
│ ├── layout.tsx ← 루트 레이아웃
│ ├── page.tsx ← 메인 페이지
│ └── globals.css
├── public/
├── next.config.ts ← TypeScript 설정 파일
├── tsconfig.json
└── package.json
주목할 점은 next.config.ts다. Next.js 15에서 도입된 TypeScript 기반 설정 파일이 Next.js 16에서도 기본값으로 유지된다. next.config.js 대신 .ts 확장자를 사용하면 설정 객체에 대한 타입 추론이 자동으로 작동해서, 잘못된 키를 입력했을 때 편집기에서 바로 에러를 확인할 수 있다.
proxy.ts 전환 — middleware.ts를 대체하는 방법

Next.js 16에서 가장 체감이 큰 breaking change가 이 부분이다. middleware.ts가 proxy.ts로 대체되었고, 내보내는 함수명도 middleware에서 proxy로 변경해야 한다. proxy.ts는 Node.js 런타임에서 실행되므로, Edge 런타임에서만 사용 가능했던 제약이 사라진 것이기도 하다.
기존 middleware.ts에서 리다이렉트를 처리하던 코드를 proxy.ts로 전환하면 다음과 같은 형태가 된다.
export default function proxy(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url));
}
함수명이 proxy로 바뀐 것 외에 NextRequest와 NextResponse API 자체는 동일하게 사용 가능하다. 다만 런타임이 Node.js로 변경되었기 때문에, 이전에 Edge 런타임 제약으로 사용하지 못했던 Node.js 네이티브 모듈(fs, crypto 등)을 proxy.ts 안에서 직접 사용할 수 있게 된다.
TypeScript 인터페이스로 proxy 응답 타입 정의하기
proxy.ts에서 복잡한 분기 로직을 다룰 때, 응답 타입을 인터페이스로 명시하면 유지보수가 수월해진다. 대규모 프로젝트에서는 리다이렉트 대상이 수십 개로 늘어나는 경우가 있고, 이때 타입 없이 작업하면 실수가 누적되기 쉽다.
interface ProxyResult {
readonly destination: string;
readonly statusCode: 301 | 302 | 307 | 308;
}
interface RouteConfig {
readonly pattern: RegExp;
readonly result: ProxyResult;
}
const routes: readonly RouteConfig[] = [
{
pattern: /^\/old-path/,
result: { destination: '/new-path', statusCode: 301 },
},
{
pattern: /^\/legacy/,
result: { destination: '/modern', statusCode: 308 },
},
] as const;
readonly와 as const를 활용하면 routes 배열이 런타임에 변경되는 실수를 컴파일 타임에 차단할 수 있다. 소규모 프로젝트에서는 과잉 설계처럼 보일 수 있지만, 리다이렉트 규칙이 20개를 넘어가면 이런 타입 정의가 디버깅 시간을 줄여주는 편이다.
proxy.ts에서 Edge 런타임을 사용하는 구체적인 대체 방안에 대해서는 공식 문서에도 명시되어 있지 않다. 현재 기준으로는 Node.js 런타임에서 proxy.ts를 사용하는 것이 기본 경로다.

Next.js 16에서 Turbopack이 next dev와 next build의 기본 번들러가 되었다. 기존에 --turbopack 플래그로 opt-in 하던 방식이 사라지고, 아무 플래그 없이 실행하면 Turbopack으로 빌드된다.
가장 주의할 점은 커스텀 webpack 설정이 있으면 빌드가 실패한다는 것이다. next.config.ts에 webpack 함수를 정의해 둔 프로젝트는 Next.js 16으로 올리는 순간 에러가 발생하게 된다. 이 경우 두 가지 선택지가 있다.
선택 1: webpack 유지 — next dev --webpack, next build --webpack 플래그를 명시한다. 기존 설정을 그대로 쓸 수 있지만, Turbopack의 빌드 속도 이점을 포기하는 셈이다.
선택 2: Turbopack 전환 — webpack 커스텀 설정을 Turbopack 호환으로 마이그레이션한다. 다만 Turbopack 커스텀 webpack 설정 마이그레이션에 대한 구체적 가이드는 공식 문서에도 아직 충분하지 않다.
Turbopack 설정 위치 변경
설정 위치도 변경되었다. experimental.turbopack에서 **최상위 turbopack**으로 이동한 것이다. 기존 Next.js 15에서 Turbopack 설정을 사용하던 프로젝트는 이 경로를 반드시 업데이트해야 한다.
// Next.js 15 (이전)
const oldConfig = {
experimental: {
turbopack: {
// 설정값
},
},
};
// Next.js 16 (현재)
const nextConfig = {
turbopack: {
// 설정값을 최상위로 이동
},
};
export default nextConfig;
대규모 프로젝트에서는 CI/CD 파이프라인의 빌드 스크립트도 함께 확인해야 한다. next build 명령이 Turbopack으로 실행되면서 빌드 캐시 구조가 달라질 수 있기 때문이다. 소규모 프로젝트는 별도 설정 없이 기본값으로 충분한 경우가 많다.
| 시나리오 | 권장 번들러 | 이유 |
|---|---|---|
| 신규 프로젝트 | Turbopack (기본) | 추가 설정 불필요, 빌드 속도 이점 |
| webpack 플러그인 의존 프로젝트 | webpack (--webpack) | 플러그인 호환성 우선 |
| CI/CD 캐시 의존 프로젝트 | 단계적 전환 | 캐시 구조 변경 영향 확인 후 전환 |
Cache Components와 “use cache” 지시어 활용
Next.js 16은 Cache Components를 도입했다. "use cache" 지시어를 사용해 페이지, 컴포넌트, 함수 단위로 캐싱을 opt-in 방식으로 적용하는 구조다. 이전 App Router의 암묵적 캐싱과 근본적으로 다른 접근이다 — 모든 동적 코드는 기본적으로 요청 시점에 실행되고, 캐싱이 필요한 부분만 명시적으로 선언하는 방식이 된 것이다.
활성화는 next.config.ts에서 cacheComponents: true로 설정한다.
const nextConfig = {
cacheComponents: true,
};
export default nextConfig;
이 설정을 켠 후 개별 컴포넌트나 함수에 "use cache" 지시어를 추가하면 해당 단위가 캐싱 대상이 된다. 기존 Next.js 15에서는 fetch 요청이 암묵적으로 캐싱되어 “왜 데이터가 갱신되지 않는가”라는 혼란이 빈번했는데, Next.js 16에서는 이 문제가 구조적으로 해소된 셈이다.
제네릭을 활용한 캐시 유틸리티 타입 설계
Cache Components를 TypeScript 프로젝트에 도입할 때, 캐시 대상 함수의 반환 타입을 제네릭으로 래핑하는 패턴이 유용하다.
type CacheableResponse = {
readonly data: T;
readonly cachedAt: string;
readonly ttl: number;
};
type ApiEndpoint = (
params: TParams
) => Promise>;
interface UserProfile {
id: string;
name: string;
email: string;
}
interface UserQueryParams {
userId: string;
}
이런 타입 정의를 두면 캐시 대상 함수가 늘어나도 일관된 응답 구조를 강제할 수 있다. CacheableResponse의 ttl 필드는 캐시 수명을 명시적으로 드러내서, "use cache" 지시어와 함께 사용할 때 캐시 전략을 코드 레벨에서 추적 가능하게 만든다.
Cache Components(`”use cache”`)의 실전 마이그레이션 패턴에 대한 상세 예제는 공식 문서에도 아직 충분하지 않다. 현재 기준으로는 단순한 데이터 페칭 함수부터 적용하고, 복잡한 컴포넌트 트리 캐싱은 안정화 이후 도입하는 것이 안전하다.
React 19.2와 React Compiler 설정
Next.js 16은 React 19.2를 포함한다. View Transitions, useEffectEvent, Activity 컴포넌트가 새로 지원되며, 가장 실용적인 변화는 React Compiler가 stable로 승격된 것이다.
활성화는 next.config.ts에서 reactCompiler: true 한 줄이면 된다.
const nextConfig = {
reactCompiler: true,
};
export default nextConfig;
React Compiler가 하는 일은 자동 메모이제이션이다. 기존에 React.memo, useMemo, useCallback을 수동으로 감싸던 코드를 컴파일러가 자동으로 최적화해주는 것이다. 이 기능이 stable이 되면서 프로덕션에서도 안심하고 사용할 수 있게 된 셈이다.
TypeScript에서 React Compiler 효과가 큰 패턴
React Compiler의 자동 메모이제이션이 특히 효과적인 패턴이 있다. 제네릭 컴포넌트에서 props 변경 여부를 수동으로 비교하던 코드가 대표적이다.
interface DataTableProps {
readonly items: readonly T[];
readonly renderRow: (item: T) => React.ReactNode;
readonly keyExtractor: (item: T) => string;
}
function DataTable({
items,
renderRow,
keyExtractor,
}: DataTableProps) {
return (
{items.map((item) => (
))}
{renderRow(item)}
);
}
이전에는 이런 제네릭 컴포넌트를 React.memo로 감쌀 때 커스텀 비교 함수까지 작성해야 하는 경우가 있었다. React Compiler가 활성화되면 이 보일러플레이트가 불필요해진다. readonly 수식어와 함께 사용하면 불변성까지 타입 레벨에서 보장되어, 컴파일러 최적화와 타입 안전성을 동시에 확보하게 되는 구조다.
React 19.2에서 지원하는 View Transitions는 페이지 전환 애니메이션을, Activity 컴포넌트는 비활성 UI 상태 관리를 담당한다. 두 기능 모두 Next.js 16에서 사용 가능하지만, App Router와의 통합 패턴은 아직 확립 단계에 있다.
Next.js 16 프로젝트에서 TypeScript를 활용하려면 몇 가지 설정을 점검할 필요가 있다. next.config.ts가 TypeScript 파일인 만큼, 설정 객체 자체에 타입 추론이 적용되는 것이 출발점이다.
strict 모드와 Next.js 타입 통합
tsconfig.json에서 strict: true는 기본 활성화 상태다. 여기에 Next.js 16 특화 설정을 추가하면 타입 안전성이 한 단계 올라간다.
// next.config.ts에서 타입 임포트 활용
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
cacheComponents: true,
reactCompiler: true,
};
export default nextConfig;
NextConfig 타입을 명시적으로 임포트하면 cacheComponents, reactCompiler 같은 Next.js 16 신규 옵션에 대한 자동완성이 동작한다. 존재하지 않는 키를 입력하면 컴파일 에러가 발생하므로, 오타로 인한 설정 누락을 방지할 수 있다.
대규모 모노레포 환경에서는 NextConfig 타입을 확장해서 팀 공통 설정 인터페이스를 만드는 경우도 있다. 그러나 소규모 프로젝트에서는 기본 NextConfig 타입만으로도 충분한 경우가 많은 편이다.
interface TeamNextConfig extends NextConfig {
readonly customAnalytics?: {
readonly provider: 'ga4' | 'amplitude';
readonly trackingId: string;
};
}
이런 확장은 next.config.ts가 팀 컨벤션을 강제하는 역할까지 겸하게 되어, 설정 리뷰 시 타입 체커가 1차 검증을 대신해준다.
proxy.ts의 타입 안전한 라우트 매칭
proxy.ts에서 경로 매칭 로직이 복잡해지면 유니온 타입과 제네릭을 조합한 라우트 정의가 도움이 된다.
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
interface ProxyRule {
readonly path: T;
readonly methods: readonly HttpMethod[];
readonly rewrite: (url: URL) => URL;
}
function createProxyRules(
rules: readonly ProxyRule[]
): ReadonlyMap> {
return new Map(rules.map((r) => [r.path, r]));
}
이 패턴에서 T extends string 제네릭은 경로 문자열을 리터럴 타입으로 좁혀준다. createProxyRules 함수에 전달한 경로가 ReadonlyMap의 키 타입으로 그대로 전파되어, 존재하지 않는 경로에 접근하면 타입 에러가 발생하는 구조다.
다음 단계 — 배포 설정과 고급 기능 도입
Next.js 16 시작하기 가이드의 기본 설정이 끝났다면, 다음으로 살펴볼 영역은 배포 환경 구성이다. Vercel 배포 시 Turbopack 빌드가 기본으로 작동하는지 확인하는 것이 첫 번째이고, 셀프 호스팅 환경에서는 next start의 Node.js 런타임 버전이 20.9 이상인지 점검해야 한다.
Next.js 16 설치 가이드에서 프로덕션 배포에 필요한 시스템 요구사항 전체를 확인할 수 있다. 게다가 Cache Components의 "use cache" 지시어를 프로덕션에 적용하려면, CDN 캐시 레이어와의 상호작용을 먼저 테스트하는 것이 안전하다.
그 다음 단계로는 Next.js 16 Turbopack 환경에서의 커스텀 빌드 파이프라인 구성이 있다. webpack 플러그인에 의존하던 번들 분석, 환경변수 주입, CSS 모듈 처리 등을 Turbopack 호환으로 전환해야 한다. 그리고 Next.js 16 proxy.ts에서 인증·권한 체크를 Node.js 런타임 이점을 살려 구현하는 패턴도 실무에서 바로 필요해지는 영역이다. Next.js 16 Cache Components와 React 19.2의 View Transitions를 조합한 페이지 전환 최적화까지 다루면, Next.js 16 시작하기 가이드의 범위를 넘어 실전 운영 수준으로 확장할 수 있다.
관련 글
- TypeScript Zod transform pipe 완벽 가이드 — API 응답 변환 7가지 실전 패턴 – Zod의 .transform()과 .pipe()를 조합하면 API 응답 데이터를 스키마 파싱 단계에서 원하는 형태로 변환할 수 있다. v4…