October 12, 20256 minutes
Next.js 16이 드디어 공개되었는데요.
이건 단순한 마이너 업데이트가 아닙니다.
안정화된 ‘터보팩(TurboPack)‘부터, 그리고 최종적으로 내장된 ‘리액트(React) 컴파일러’, 훨씬 스마트해진 라우팅과 새로운 캐싱 API까지 정말 많은 변화가 있었거든요..
그래서 이번 글에서는 Next.js 16의 모든 주요 기능과 개선 사항, 그리고 꼭 알아야 할 변경점까지 예시와 함께 자세히 설명해 드리려고 합니다.
가장 먼저 눈에 띄는 업데이트는 바로 터보팩(TurboPack)이 개발 및 프로덕션 환경 모두에서 드디어 안정화되었다는 소식인데요.
이제 터보팩이 모든 애플리케이션의 ‘기본 번들러’가 되는 것입니다.
만약 웹팩(Webpack)을 계속 사용하고 싶다면, 특정 파라미터를 추가해서 옵트인(Opt-in)할 수 있습니다.
next dev --webpack
next build --webpack터보팩을 사용하면 별도의 설정 없이도 프로덕션 빌드 속도는 2~5배, 빠른 새로고침(Fast Refresh) 속도는 최대 10배까지 빨라지는 놀라운 경험을 할 수 있습니다.
다음은 ‘터보팩 파일 시스템 캐싱’ 기능인데요.
아직 베타 버전이긴 하지만, 개발 환경에서 파일 시스템 캐싱을 지원하게 된 것입니다.
빌드 결과물을 디스크에 그대로 유지해서, 재시작 시 거의 즉시 가까운 속도를 보여주거든요.
특히 거대해지기 쉬운 모노레포(Monorepo) 환경에서 정말 큰 힘을 발휘할 기능입니다.
이미 버셀(Vercel) 내부 앱에서는 이 기능을 활용해 상당한 개선 효과를 보고 있다고 하는데요.
실제로 X(구 트위터) 같은 소셜 미디어에서도 많은 개발자들이 업그레이드 후 성능 향상을 체감했다는 후기를 올리고 있습니다.
리액트 컴파일러가 안정화되면서 Next.js 16에서도 이를 정식으로 지원하게 되었는데요.
덕분에 더 이상 useMemo나 useCallback을 직접 사용할 필요 없이 컴파일러가 알아서 최적화를 처리해 주는 ‘자동 메모이제이션’이 가능해졌습니다.
아직 기본으로 활성화되어 있지는 않지만, 설정 파일에 플래그 하나만 추가하면 바로 사용할 수 있거든요.
꼭 한번 확인해 보시길 추천하는 기능입니다.
const nextConfig = {
  reactCompiler: true,
};
export default nextConfig;npm install babel-plugin-react-compiler@latestNext.js 16은 캐싱 동작을 명시적으로 제어할 수 있는 새로운 API들을 선보였는데요.
아래와 같이 함께 살펴보겠습니다.
Revalidate Tag는 캐시에 특정 태그를 지정하고, 필요할 때 해당 태그가 붙은 캐시만 선택적으로 삭제할 수 있게 해주는 기능인데요.
페이지 전체가 아닌 특정 섹션의 캐시만 무효화하고 싶을 때 정말 유용한 기능입니다.
이제는 여기에 추가적으로 ‘캐시 프로필’을 전달해야 하거든요.
태그를 얼마나 오래 캐시할지, 언제 재검증할지 등 더 상세한 정보를 넘겨줘야 하는 것입니다.
이 기능은 블로그 포스트나 분석 대시보드처럼 즉각적인 업데이트는 필요 없지만, 결국에는 재검증이 필요한 경우에 특히 유용합니다.
revalidateTag 예시: ‘캐시 프로필’로 더 똑똑해진 업데이트가장 큰 변화는 revalidateTag 함수를 사용할 때 이제 두 번째 인자로 ‘캐시 프로필(cacheLife profile)‘을 넘겨줘야 한다는 점인데요.
이게 바로 ‘SWR(stale-while-revalidate)’ 동작을 가능하게 해주는 핵심적인 변경점입니다.
공식 문서에서 추천하는 가장 일반적인 사용 예시는 다음과 같습니다.
import { revalidateTag } from 'next/cache';
// ✅ 내장된 캐시 프로필 사용 (대부분의 경우 'max'를 추천)
revalidateTag('blog-posts', 'max');
// 또는 다른 내장 프로필 사용
revalidateTag('news-feed', 'hours');
revalidateTag('analytics', 'days');
// 또는 커스텀 재검증 시간을 가진 인라인 객체 사용
revalidateTag('products', { revalidate: 3600 });
// ⚠️ 더 이상 사용되지 않음 - 인자가 하나인 이전 방식
revalidateTag('blog-posts');여기서 'max' 프로필을 사용하면, 사용자가 태그된 콘텐츠를 요청했을 때 일단 캐시된 데이터를 즉시 보여주고, 백그라운드에서 조용히 최신 데이터로 업데이트를 진행하거든요.
즉각적인 일관성이 필요 없는 정적 콘텐츠에 아주 이상적인 방식입니다.
참고로, 이제 인자를 하나만 사용하는 이전 방식은 지원되지 않으니 꼭 두 번째 인자를 추가해 주셔야 합니다.
이건 ‘서버 액션(Server Actions)‘에서만 사용할 수 있는 새로운 API인데요.
같은 요청 내에서 캐시 데이터를 만료시키고 ‘즉시’ 새로고침하는 기능을 제공합니다.
덕분에 사용자가 변경 사항을 바로 확인할 수 있게 되는 것이죠.
예를 들어 사용자 설정이나 프로필처럼, 사용자가 데이터를 변경한 후 즉각적인 피드백을 기대하는 모든 곳에 활용할 수 있습니다.
‘Revalidate Tag’와 달리 Update Tag는 즉시 캐시를 만료시키고 새로고침하기 때문에, 사용자가 자신의 업데이트를 바로 확인해야 하는 기능에 완벽합니다.
updateTag 예시: 사용자가 변경 사항을 즉시 확인해야 할 때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}`);
}이 코드는 데이터베이스 업데이트가 끝난 직후 updateTag를 호출해서 해당 사용자 ID와 관련된 캐시를 즉시 만료시키고 새로고침 하는데요.
덕분에 사용자는 별도의 페이지 새로고침 없이도 방금 자신이 수정한 프로필 정보를 바로 확인할 수 있게 되는 것입니다.
이 API 역시 서버 액션 전용으로, 캐시되지 않은 데이터만 새로고침하는 역할을 하는데요.
캐시는 전혀 건드리지 않는다는 점이 핵심입니다.
알림 배지, 장바구니 개수, 실시간 지표처럼 캐시되지는 않지만 캐시된 콘텐츠와 함께 표시되는 데이터를 새로고침할 때 유용하거든요.
예를 들어 데이터베이스의 알림 상태를 업데이트하고, 캐시되지 않은 헤더의 알림 개수를 새로고침하고 싶을 때 바로 이 refresh 함수를 사용할 수 있습니다.
정적인 셸(shell)은 빠르게 유지하면서 동적인 부분만 업데이트할 수 있게 되는 것이죠.
‘게임 체인저’가 될 API가 있는데요, 그게 바로 이 캐싱 API, 그중에서도 updateTag입니다.
예를 들어 유저가 만약 ‘완료’ 버튼을 누르는 순간이 있다면,
개발자인 저는 이 버튼을 누르면 체크 표시가 바로 업데이트되어 사용자가 완료 사실을 즉시 인지하고 다음으로 넘어갈 수 있기를 정말 바랐습니다.
바로 이 기능이 그걸 가능하게 해주는 것이거든요.
이제 사용자는 즉시 다음 경로로 이동하고, 나머지 백그라운드 작업은 Next.js의 after 함수를 통해 처리할 수 있게 된 것입니다.
refresh 예시: 캐시는 그대로, 실시간 데이터만 새로고침마지막으로 refresh API는 캐시는 전혀 건드리지 않고, 캐시되지 않은 데이터만 새로고침하고 싶을 때 사용하는 기능이거든요.
클라이언트 사이드의 router.refresh()와 상호 보완적인 역할을 하는 서버 액션 전용 API입니다.
알림을 ‘읽음’으로 처리하고 헤더에 있는 알림 개수를 업데이트하는 예시가 가장 직관적입니다.
'use server';
import { refresh } from 'next/cache';
export async function markNotificationAsRead(notificationId: string) {
  // 데이터베이스에서 알림을 업데이트
  await db.notifications.markAsRead(notificationId);
  // 헤더에 표시된 알림 개수를 새로고침
  // (이 데이터는 별도로 가져오며 캐시되지 않음)
  refresh();
}페이지의 다른 정적 콘텐츠들은 모두 캐시된 상태로 빠르게 유지하면서, 데이터베이스에 변경이 일어난 후 캐시되지 않은 알림 개수 같은 동적인 데이터만 새로 가져오고 싶을 때가 있는데요.
바로 이럴 때 refresh() 함수가 완벽한 해결책이 되어주는 것입니다.
만약 자체 인프라에 Next.js 앱을 배포하거나, 빌드 단계를 커스텀하고 싶을 때 유용한 기능인데요.
클라우드플레어 워커(Cloudflare Workers)를 실행하거나 S3에 에셋을 업로드하는 등의 작업을 할 수 있게 해주는 것이 바로 ‘빌드 어댑터 API’입니다.
이 API를 통해 빌드 프로세스에 연결되는 커스텀 어댑터를 만들 수 있거든요.
덕분에 특정 제공업체에 얽매이지 않고 원하는 곳 어디에나 애플리케이션을 자유롭게 배포할 수 있게 됩니다.
Next.js 16은 라우팅 및 내비게이션 시스템을 전면 개편하여 페이지 전환을 더욱 선형적이고 빠르게 만들었는데요.
이건 커뮤니티에서 많은 개발자들이 지적했던 문제점이었습니다.
가장 큰 변화 중 하나는 ‘레이아웃 중복 제거’인데요.
이전에는 공유 레이아웃을 사용하는 여러 URL을 미리 가져올 때 각 링크마다 레이아웃을 별도로 다운로드했습니다.
하지만 이제는 50개의 제품 링크가 있는 페이지라도 공유 레이아웃은 단 한 번만 다운로드하게 되어 네트워크 전송 크기를 획기적으로 줄일 수 있습니다.
또한, ‘점진적 프리페칭(Incremental Prefetching)’ 기능도 추가되었는데요.
페이지 전체가 아닌, 캐시에 아직 없는 부분만 미리 가져오는 더 스마트한 방식입니다.
이로 인해 프리페치 요청 수는 더 많아질 수 있지만, 전체 전송 데이터 크기는 오히려 줄어드는 효과를 볼 수 있습니다.
Next.js 16에서는 실험 기능이었던 ‘부분적 사전 렌더링(PPR, Partial Prerendering)’ 플래그가 제거되었는데요.
이제 PPR 기능은 ‘캐시 컴포넌트(Cache Components)‘에 통합되었습니다.
정적 콘텐츠를 먼저 로드하고 동적 콘텐츠는 스트리밍하는 PPR 기능이 캐시 컴포넌트와 결합되면서, 무엇을 캐시하고 무엇을 캐시하지 않을지 더 스마트하게 판단할 수 있게 된 것이죠.
이 변화는 정말 많은 가능성을 열어줄 것으로 기대됩니다.
마지막으로 몇 가지 주요 변경 사항이 있는데요.
가장 먼저, 최소 지원 ‘노드(Node.js)’ 버전이 변경되었고, ‘타입스크립트(TypeScript)’ 5가 최소 버전이 되었습니다.
그리고 가장 주의해야 할 점은 기존의 middleware 파일의 이름이 proxy로 변경되었다는 점인데요.
따라서 업그레이드 시 기존 미들웨어 파일의 이름을 꼭 바꿔주셔야 합니다.
여기까지 Next.js 16 베타 버전의 주요 변경 사항들을 모두 살펴봤는데요.
2주 앞으로 다가온 ‘Next.js Conf’에서는 더 많은 기능들이 공개될 것이라 생각합니다.