왜 지금 Next.js 16으로 올라가야 하는가
버전 번호가 하나 올라간 게 아니다
Next.js는 13에서 App Router를 내놓은 이후로 14, 15로 넘어가는 동안 비교적 온건했습니다. 새 기능은 늘었지만 기존 코드가 하루아침에 깨지는 일은 드물었죠. next-codemod를 한 번 돌리고 워닝 몇 개를 지우면 끝나는 수준이었습니다.
16은 다릅니다. 이번 메이저는 팀이 몇 년간 "일단은 됩니다" 하고 넘어갔던 결정들을 정면으로 되돌리는 릴리스입니다. 동기 params 접근, Edge 기본 미들웨어, 암묵적 캐싱 — 전부 "명시적이지 않으면 깨진다"는 방향으로 바뀌었습니다. 그리고 이 세 가지는 서로 독립적이지 않습니다. 미들웨어가 proxy로 바뀐 이유와 params가 Promise가 된 이유는 사실 같은 문제의식(요청 처리 경계를 명확히 하겠다는)에서 나왔습니다. 이 책이 챕터를 나눠서 다루긴 하지만, 읽다 보면 하나의 설계 철학이 반복해서 등장하는 걸 느끼실 겁니다.
왜 이번엔 codemod만으로 안 끝나는가
과거 메이저 업그레이드는 대체로 기계적이었습니다. next/router를 next/navigation으로 바꾸고, getServerSideProps를 서버 컴포넌트로 옮기는 식의 치환 작업이었죠. 16의 변화 중 상당수는 치환이 아니라 재설계를 요구합니다.
예를 들어 middleware.ts에서 인증 체크를 하던 프로젝트는 단순히 파일명을 proxy.ts로 바꾸는 걸로 끝나지 않습니다. proxy는 더 이상 Edge 런타임에서 돌지 않고, 문서에서도 인증 로직은 레이아웃이나 서버 액션으로 옮기라고 못을 박습니다. 즉 "이름만 바뀐 것"과 "역할이 재정의된 것"을 구분해야 하는데, 공식 마이그레이션 가이드는 이 구분을 명확히 설명하지 않습니다. 실제로 커뮤니티에는 파일명만 바꾸고 인증이 새는 걸 뒤늦게 발견한 사례가 여럿 올라와 있습니다.
# 공식 codemod로 기계적인 변환은 자동화된다
npx @next/codemod@canary upgrade latest
# 하지만 아래는 codemod가 못 잡는다 — 사람이 읽고 판단해야 한다
# - middleware의 인증 로직이 proxy로 그대로 옮겨졌는가
# - use cache가 없어서 원래 캐시되던 페이지가 매번 렌더되고 있진 않은가이 책이 다루는 범위
이 책은 16의 모든 변경 사항을 나열하지 않습니다. 공식 릴리스 노트가 이미 그 일을 합니다. 대신 "코드가 조용히 잘못 동작하게 되는" 지점에 집중합니다. 에러를 던지는 변경은 배포 전에 걸러집니다. 진짜 위험한 건 컴파일도 되고 화면도 뜨는데 의미가 달라진 경우입니다 — 캐시가 빠졌는데 페이지는 여전히 렌더되고, 인증 체크가 빠졌는데 앱은 여전히 실행되는 식으로요.
App Router를 이미 쓰고 있고 15에서 16으로 실제로 옮겨야 하는 팀을 염두에 두고 썼습니다. Pages Router를 그대로 쓰고 있다면 이번 변경의 상당수(proxy, async params, use cache)는 App Router 전용이라 해당 사항이 적을 수 있습니다.