Next.js 15에서 업데이트 되는 내용들
2024-11-01
911
17분
soaple
안녕하세요, 소플입니다.
지난 6월 매거진에서 Next.js 15 RC (Release Candidate) 에 대한 소식을 전해드렸었는데요,
약 2주 전인 지난 10월 21일에 드디어 Next.js 15가 정식으로 릴리즈 되었습니다 🎉
이번 매거진에서는 Next.js 15에서 업데이트 된 내용에 대해 한 번 알아보도록 하겠습니다.
영어로 된 공식 블로그 문서는 아래 링크에서 보실 수 있습니다.
Next.js 15
https://nextjs.org/blog/next-15
@next/codemod
CLIheaders
, cookies
, params
, 그리고 searchParams
등 요청에 따른 특정 데이터에 의존하는 API를 비동기 방식으로 전환fetch
요청, GET
Route Handlers 및 Client Router Cache는 기본적으로 캐싱되지 않음unstable_after
API (Experimental)instrumentation.js
API (Stable)<Form>
컴포넌트<form>
요소를 확장하여 prefetching, 클라이언트 측 네비게이션 및 점진적 개선 기능을 제공next.config
next.config.ts
파일 형식을 지원Cache-Control
헤더를 통해 더 많은 제어 가능아래 명령어를 사용해서 Next.js 15를 지금 바로 사용해볼 수 있습니다.
# Codemode CLI를 활용한 자동 업그레이드
npx @next/codemod@canary upgrade latest
# 수동 업그레이드
npm install next@latest react@rc react-dom@rc
@next/codemod
CLI를 활용한 원활한 업그레이드Next.js 같은 경우 새로운 버전이 굉장히 빠르게 릴리즈 되기 때문에,
기존 버전을 사용하는 개발자들이 새로운 버전으로 업그레이드 하는데 어려움이 많았습니다.
그런 어려움을 해결하기 위해 Next.js팀에서는 Codemod CLI(@next/codemod
)를 개발하게 되었고,
이를 통해 새로운 버전으로 업그레이드하는 과정을 원활하게 만들었다고 합니다.
Codemod CLI를 사용하려면 아래와 같이 실행하면 됩니다.
npx @next/codemod@canary upgrade latest
여기서 마지막 파라미터(@rc
, @canary
등)를 통해 업그레이드할 버전을 결정하게 됩니다.
Codemod CLI에 대한 자세한 내용은 Next.js codemods를 참고하시기 바랍니다.
전통적인 서버 사이드 렌더링(SSR)에서는 서버가 요청을 받은 후에만 콘텐츠를 렌더링합니다.
그러나 모든 컴포넌트가 요청에 따른 특정 데이터를 필요로 하는 것은 아니기 때문에,
요청을 기다릴 필요 없이 일부 컴포넌트를 미리 렌더링할 수 있습니다.
이상적으로는, 서버가 가능한 한 많은 작업을 요청이 도착하기 전에 준비할 수 있어야 합니다.
이를 가능하게 하고 향후 최적화를 위한 기반을 마련하려면, 언제 요청을 기다려야 하는지 알아야 합니다.
그래서 headers
, cookies
, params
, 그리고 searchParams
등 요청에 따른 특정 데이터에 의존하는 API를 비동기 방식으로 전환한다고 합니다.
import { cookies } from 'next/headers';
export async function AdminPanel() {
const cookieStore = await cookies();
const token = cookieStore.get('token');
// ...
}
이 변경 사항은 아래 API에 영향을 미칩니다.
cookies
headers
draftMode
layout.js
, page.js
, route.js
, default.js
, generateMetadata
, generateViewport
에서의 params
page.js
에서의 searchParams
쉽게 마이그레이션할 수 있도록 이 API는 일시적으로 동기적으로 접근 가능하지만,
개발 및 프로덕션 환경에서 경고 메시지를 표시하며 다음 major 버전까지 이 경고가 계속 표시됩니다.
그리고 아래와 같이 codemod를 사용하여 마이그레이션을 자동화할 수 있습니다.
npx @next/codemod@canary next-async-request-api .
codemod를 통해 마이그레이션이 완전하게 수행되지 못할 경우에는 업그레이드 가이드를 참고하시기 바랍니다.
그리고 Next.js 애플리케이션을 새로운 API로 마이그레이션하는 예시도 제공하고 있습니다.
Next.js App Router는 기본적으로 성능에 최적화된 캐싱 설정을 적용하여 출시되었으며, 필요한 경우 비활성화할 수 있는 옵션을 제공했습니다.
하지만 기본 캐싱 설정으로 인해 의도치 않은 결과가 나오는 일이 많아 개발자들의 불만이 있었고,
그래서 Partial Prerendering(PPR)과 fetch
를 사용하는 타사 라이브러리와의 상호작용 방식을 포함하여 캐싱 휴리스틱을 재평가했다고 합니다.
Next.js 15부터는 GET
Route Handlers 및 Client Router Cache의 기본 설정이 캐싱됨에서 캐싱되지 않음으로 변경됩니다.
이전과 동일하게 캐싱을 사용하고 싶으면 옵션을 통해 적용 가능합니다.
GET
Route Handlers는 기본적으로 캐싱되지 않음Next 14에서는 dynamic function이나 dynamic config option을 사용하지 않는 경우에,
GET
HTTP 메서드를 사용한 Route Handler가 기본적으로 캐싱되었습니다.
하지만 Next.js 15에서는 GET
함수가 기본적으로 캐싱되지 않습니다.
만약 이전과 같이 캐싱을 적용하고 싶다면 static route config 옵션에서 export dynamic = 'force-static'
같은 형태로 적용할 수는 있습니다.
그리고 아래와 같은 특별한 Route Handlers는 여전히 static이 기본 값입니다.
(dynamic function이나 dynamic config option을 사용하지 않는 경우에)
Next.js 14.2에서는 Router Cache의 커스텀 설정을 위해 실험적으로 staleTime flag를 소개했습니다.
Next.js 15에서는 이 flag에 계속 액세스할 수는 있지만,
기본 동작이 Page 세그먼트에 대해 staleTime
이 0
이 되도록 변경됩니다.
즉, 앱을 탐색할 때 클라이언트는 탐색의 일부로 활성화되는 Page 컴포넌트의 최신 데이터를 항상 반영합니다.
하지만 아래와 같은 중요한 동작들은 여전히 변경되지 않고 유지됩니다.
staleTimes.static
설정 값).그리고 아래와 같이 설정하면 이전 Client Router Cache의 동작을 사용할 수 있습니다.
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 30,
},
},
};
export default nextConfig;
Next.js에서 캐싱을 지속적으로 개선할 예정이며, 곧 자세한 내용을 공유할 예정이라고 합니다.
Next.js 15의 가장 큰 변화는 아무래도 React 19를 지원하는 것이 아닐까 싶습니다.
참고로 아직 React 19는 정식으로 릴리즈 되지 않았고 RC(Release Candidate) 버전만 제공하고 있습니다.
Next.js 15의 App Router는 React 19 RC를 사용하며, Pages Router에 대한 React 18의 하위 호환성도 도입했다고 합니다.
Pages Router를 사용하는 경우, 준비가 되면 React 19로 업그레이드할 수 있습니다.
React 19는 아직 RC 단계이지만, 실제 애플리케이션에 대한 광범위한 테스트와 React 팀과의 긴밀한 협력을 통해 안정성에 대한 확신을 얻었다고 합니다.
(하지만 GitHub를 보면 아직 문제가 좀 있는 것 같기도...🥲)
주요 호환성 깨짐 변경 사항은 잘 테스트되어 기존 App Router 사용자는 영향을 받지 않습니다.
따라서 Next.js 15를 Stable 버전으로 출시하여, 프로젝트가 React 19 정식 릴리즈에 완벽히 대비할 수 있도록 하였습니다.
버전업이 원활하게 이루어지도록 하기 위해 마이그레이션 과정을 쉽게 도와줄 Codemod 및 자동화 도구를 제공합니다.
React 19에 대해서는 이전 매거진인 2024 리액트 컨퍼런스를 참고하면 파악하는데 도움이 되실 겁니다.
그리고 Next.js 15에서는 리액트 19에서 새롭게 추가된 기능들을 사용할 수 있습니다.
🔗 관련 링크: 2024 리액트 컨퍼런스
https://www.frontoverflow.com/magazine/8/React%20Conf%202024%20(Day%201)
Next.js 15는 React 18과 함께 Pages Router에 대한 하위 호환성을 유지하여,
Next.js 15의 개선 사항을 활용하면서도 React 18을 계속 사용할 수 있도록 지원합니다.
RC1 이후, 커뮤니티 피드백을 반영하여 React 18 지원을 포함하도록 초점을 전환다고 합니다.
이 유연성을 통해 React 18을 사용하는 Pages Router와 함께 Next.js 15를 채택할 수 있어, 업그레이드 경로에 대한 더 큰 제어권을 제공합니다.
NOTE. 주의사항
동일한 애플리케이션에서 Pages Router는 React 18, App Router는 React 19로 실행할 수 있지만, 이 설정은 권장되지 않습니다.
이렇게 구성할 경우 두 버전 간의 기본 API 및 렌더링 로직이 완전히 일치하지 않아 예측할 수 없는 동작과 타입 불일치가 발생할 수 있습니다.
올해 리액트 컨퍼런스에서 발표에 가장 큰 분량을 차지했던 것이 바로 React Compiler입니다.
자동으로 Memoization을 해줌으로써 개발자가 수동으로 최적화 해야하는 부분을 줄여주는 것이 핵심 기능이었죠.
수년간 Meta에서 React Compiler를 개발해왔고 이제 드디어 정식 릴리즈를 앞두고 있는데요,
Next.js 15에서는 아래와 같이 React Compiler를 사용해볼 수 있습니다.
먼저 babel-plugin-react-compiler
을 설치합니다.
npm install babel-plugin-react-compiler
다음으로 next.config.ts
파일을 아래와 같이 수정하면 됩니다.
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
module.exports = nextConfig;
참고로 현재 Next.js에서는 React Compiler를 Babel 플러그인 형태로만 사용할 수 있으며,
이로 인해 빌드 시간이 늘어날 수 있다는 점에 유의하시기 바랍니다.
🔗 관련 링크: React Compiler 매거진
https://www.frontoverflow.com/magazine/5/%EB%A6%AC%EC%95%A1%ED%8A%B8%20%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC%20(React%20Compiler)
이제 Hydration error 해결 방법에 대한 제안과 함께 에러가 발생한 부분의 소스 코드가 함께 표시된다고 합니다.
먼저 아래 화면은 Next.js 14.1의 Hydration error 메시지입니다.
그리고 아래 화면은 Next.js 15 RC에서 업데이트 된 모습입니다.
조금 더 자세한 정보를 표현해주기 때문에 에러를 수정하기에 용이해보입니다.
Next.js 15의 정식 릴리즈와 함께 로컬 개발 환경을 위한 Turbopack이 안정화 버전으로 출시됩니다.
Turbopack을 사용하는 것이 필수는 아니고 개발자가 선택할 수 있는데,
아래와 같은 명령어를 통해 사용해볼 수 있습니다.
next dev --turbo
Turbopack팀은 지난 세 달 동안 cold compilation 성능을 최적화하는 데 중점을 두고 개발을 진행하였고,
이전 릴리즈에 비해 다음과 같은 성능 향상을 확인했다고 합니다.
향후 릴리즈에서도 이러한 영역을 계속해서 최적화할 예정이며,
지속적 캐싱, 메모리 사용량 감소, next build
측면에서 상당한 진전을 이루고 있다고 합니다.
아래 그림은 next build
테스트 통과율(현재 96%)을 나타낸 그림입니다.
Next.js는 이제 개발 모드에서 정적 경로 표시기(Static Route Indicator)를 통해,
각 라우트가 정적(static)인지 동적(dynamic)인지 시각적으로 확인할 수 있는 기능을 제공합니다.
아래와 같은 Static Route Indicator는 페이지가 어떻게 렌더링되는지 명확하게 보여주므로 성능을 최적화하는 데 유용합니다.
Next.js에서는 이제 next build
명령어의 출력에서 모든 라우트의 렌더링 전략을 확인할 수 있습니다.
이 업데이트는 Next.js의 관찰 가능성을 개선하려는 지속적인 노력의 일환으로,
개발자가 애플리케이션을 모니터링하고 디버그하며 최적화하기 쉽게 만들기 위한 것이라고 합니다.
이를 통해 각 페이지가 정적으로 렌더링되는지, 동적으로 렌더링되는지 등을 한눈에 파악할 수 있습니다.
또한, Next.js는 앞으로 전용 개발자 도구를 제공하기 위해 작업 중이며, 이에 대한 자세한 내용은 추후 발표될 예정입니다.
정적 경로 표시기에 대해 더 알아보고, 필요에 따라 이 기능을 비활성화할 수도 있습니다.
unstable_after
를 사용하여 응답 이후에 코드 실행 (Experimental)사용자 요청을 처리할 때 서버는 일반적으로 응답(response)과 직접 관련된 작업을 수행합니다.
그러나 로깅, 분석 및 기타 외부 시스템 동기화와 같은 작업을 수행해야 할 수도 있습니다.
이러한 작업은 응답과 직접 관련이 없으므로 사용자가 완료될 때까지 기다릴 필요가 없습니다.
하지만 서버리스 함수는 응답이 종료되는 즉시 중단되기 때문에, 사용자에게 먼저 응답한 이후에 다른 작업들을 수행하도록 하는 것이 어려웠습니다.
새롭게 추가된 after()
는 응답 스트리밍이 완료된 이후에 처리할 작업을 예약하여 기본 응답을 차단하지 않고 보조 작업을 실행할 수 있도록 해주는 실험적인 API입니다.
이것을 사용하려면 먼저 아래와 같이 next.config.ts
에 experimental.after
를 추가해야 합니다.
const nextConfig = {
experimental: {
after: true,
},
};
export default nextConfig;
그리고 이후에 Server Components, Server Actions, Route Handlers, 또는 Middleware에서 아래와 같이 import
해서 사용하면 됩니다.
import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
export default function Layout({ children }) {
// Secondary task
after(() => {
log();
});
// Primary task
return <>{children}</>;
}
instrumentation.js
(Stable)instrumentation
파일과 register()
API를 사용하면,
사용자가 Next.js 서버 라이프사이클에 접근하여 성능을 모니터링하고, 오류의 원인을 추적하며, OpenTelemetry와 같은 관찰 도구와 통합할 수 있습니다.
이 기능은 이제 안정화되었으며, 실험적 옵션인 experimental.instrumentationHook
설정을 제거할 수 있습니다.
또한, Sentry와 협력하여 새로운 onRequestError 훅을 설계하였으며, 이를 통해 다음 작업을 수행할 수 있습니다.
export async function onRequestError(err, request, context) {
await fetch('https://...', {
method: 'POST',
body: JSON.stringify({ message: err.message, request, context }),
headers: { 'Content-Type': 'application/json' },
});
}
export async function register() {
// observability provider SDK 초기화
}
<Form>
컴포넌트새로운 <Form>
컴포넌트는 HTML <form>
요소를 확장하여 prefetching, 클라이언트 측 네비게이션 및 점진적 개선 기능을 제공합니다.
import Form from 'next/form';
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
);
}
<Form>
컴포넌트는 다음과 같은 기능을 제공합니다.
이전에는 이러한 기능을 구현하기 위해 아래와 같이 많은 보일러플레이트 코드가 필요했습니다.
// Note: 이 코드는 설명하기 위한 목적의 코드이며, production 코드에서 사용하는 것을 권장하지 않습니다.
'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
export default function Form(props) {
const { action } = props;
const router = useRouter();
useEffect(() => {
// form 타겟이 URL이면 prefetch 함
if (typeof action === 'string') {
router.prefetch(action);
}
}, [action, router]);
function onSubmit(event) {
event.preventDefault();
// 모든 form fields에 대해 URL encoded 된 데이터와 함께 `router.push` 함수 호출
const formData = new FormData(event.currentTarget);
const data = new URLSearchParams();
for (const [name, value] of formData) {
data.append(name, value as string);
}
router.push(`${action}?${data.toString()}`);
}
if (typeof action === 'string') {
return (
<form
onSubmit={onSubmit}
{...props}
/>
);
}
return <form {...props} />;
}
하지만 새로운 <Form>
컴포넌트를 사용하면 위와 같이 많은 보일러플레이트 코드를 작성할 필요가 없습니다.
next.config.ts
지원Next.js는 이제 TypeScript로 작성된 next.config.ts
파일 형식을 지원하며,
자동완성 및 타입 안전한 옵션을 제공하는 NextConfig
타입을 사용할 수 있습니다.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
/* 여기에 config 옵션 작성 */
};
export default nextConfig;
🔗 관련 링크: Next.js의 TypeScript 지원
https://nextjs.org/docs/canary/app/building-your-application/configuring/typescript#type-checking-nextconfigts
셀프 호스팅 애플리케이션을 사용하는 경우, Cache-Control
지시어에 대한 더 많은 제어가 필요할 수 있습니다.
일반적인 사례 중 하나는 ISR(Incremental Static Regeneration) 페이지에 대해 stale-while-revalidate
기간을 제어하는 것입니다.
이를 위해 두 가지 개선 사항을 도입했다고 합니다.
next.config
에서 expireTime
값을 구성할 수 있음experimental.swrDelta
옵션이 사용)stale-while-revalidate
의미를 제대로 적용할 수 있음또한, 사용자 정의 Cache-Control
값을 기본값으로 덮어쓰지 않으므로, 완전한 제어가 가능하며 CDN 설정과의 호환성을 보장한다고 합니다.
그리고 이미지 최적화 기능도 개선되었다고 합니다.
이전에는 이미지 최적화를 위해 Next.js 서버에 sharp
을 설치하도록 권장했지만 설치를 놓치는 경우가 있었습니다.
하지만 Next.js 15에서는 sharp
을 수동으로 설치할 필요 없이, next start
나 독립 실행 모드에서 자동으로 사용됩니다.
Server Actions는 클라이언트에서 호출할 수 있는 서버 사이드 함수입니다.
파일 상단에 'use server'
지시어를 추가하고 비동기 함수를 export
함으로써 정의할 수 있습니다.
서버 액션이나 유틸리티 함수가 코드의 다른 부분에서 import
되지 않더라도, 여전히 공개적으로 접근 가능한 HTTP 엔드포인트가 됩니다.
이 동작은 기술적으로는 올바르지만, 이러한 함수가 의도치 않게 노출될 수 있습니다.
그래서 보안을 강화하기 위해 다음과 같은 기능을 도입했다고 합니다.
// app/actions.js
'use server';
// 이 액션은 애플리케이션에서 사용되고 있으므로,
// Next.js는 클라이언트가 이 서버 액션을 참조하고 호출할 수 있도록 보안이 강화된 ID를 생성합니다.
export async function updateUserAction(formData) {}
// 이 액션은 애플리케이션에서 사용되지 않으므로,
// Next.js는 `next build` 중에 이 코드를 자동으로 제거하고 공개 엔드포인트를 생성하지 않습니다.
export async function deleteUserAction(formData) {}
서버 액션은 HTTP 엔드포인트로 간주되므로, 이를 공개적으로 사용할 수 있음을 유의해야 합니다.
자세한 내용은 Server Actions 보안을 참고하시기 바랍니다.
외부 패키지를 번들링하면 애플리케이션의 cold start 성능이 향상될 수 있습니다.
App Router에서는 외부 패키지들이 기본적으로 번들링 됩니다.
그리고 serverExternalPackages 옵션을 사용하면 특정 패키지만 제외할 수도 있습니다.
반면에 Pages Router에서는 외부 패키지들이 기본적으로 번들링되지 않습니다.
대신, transpilePackages 옵션을 사용해서 번들링 할 패키지 목록을 제공하면 됩니다.
그리고 이러한 App Router와 Pages Router의 옵션을 통합하기 위해, 아래와 같이 새로운 옵션이 추가되었습니다.
bundlePagesRouterDependencies는 App Router의 기본 자동 번들링과 통합시키고,
serverExternalPackages는 특정 패키지를 번들링에서 제외시킵니다.
const nextConfig = {
// Pages Router에서 외부 패키지들을 자동으로 번들링 함
bundlePagesRouterDependencies: true,
// App Router와 Pages Router에서 특정 패키지들을 모두 번들링하지 않음
serverExternalPackages: ['package-name'],
};
export default nextConfig;
Next.js 15는 ESLint 9를 지원하며, 2024년 10월 5일부로 ESLint 8이 지원 종료됩니다.
원활한 전환을 위해 Next.js는 ESLint 8과 9 모두를 계속 지원합니다.
ESLint 9로 업그레이드하고 새로운 구성 형식을 아직 채택하지 않은 경우,
Next.js는 자동으로 ESLINT_USE_FLAT_CONFIG=false
옵션을 적용하여 마이그레이션을 용이하게 합니다.
또한, —ext
및 —ignore-path
와 같은 오래된 옵션은 더 이상 next lint
에서 지원되지 않으며,
ESLint 10에서는 이러한 구성이 최종적으로 금지될 예정이므로, 빠른 마이그레이션을 권장합니다.
변경 사항에 대한 자세한 내용은 마이그레이션 가이드를 확인하세요.
이 업데이트의 일환으로 eslint-plugin-react-hooks
를 v5.0.0
으로 업그레이드했습니다.
여기에는 React Hooks 사용에 대한 새로운 규칙이 도입되었습니다.
eslint-plugin-react-hooks@5.0.0
의 변경 로그에서 모든 변경 사항을 확인할 수 있습니다.
개발 중 서버 컴포넌트는 파일을 저장할 때마다 다시 실행됩니다.
이는 API 엔드포인트 또는 타사 서비스로의 fetch
요청이 매번 다시 호출된다는 의미입니다.
로컬 개발 성능을 개선하고 과금되는 API 호출 비용을 줄이기 위해,
HMR이 이전 렌더링에서의 fetch
응답을 재사용할 수 있도록 개선했다고 합니다.
자세한 내용은 Server Components HMR Cache에서 확인하시기 바랍니다.
느린 네트워크 요청이 있는 페이지의 빌드 시간을 개선하기 위해 정적 생성(static generation)을 최적화했다고 합니다.
이전에는 클라이언트 측 네비게이션을 위한 데이터를 생성하고 초기 페이지 방문을 위해 HTML을 렌더링하기 위해 두 번의 렌더링 과정을 거쳤습니다.
이제 첫 번째 렌더링 결과를 재사용하여 두 번째 과정을 생략, 작업량과 빌드 시간을 줄였습니다.
또한, 정적 생성 워커(static generation workers)가 페이지 간에 fetch
캐시를 공유하도록 최적화했습니다.
fetch
호출이 캐싱을 거부하지 않으면, 동일한 워커에서 처리되는 다른 페이지에서도 그 결과를 재사용할 수 있습니다.
이는 동일한 데이터를 위한 요청 수를 줄여줍니다.
고급 제어가 필요한 경우를 위해 정적 생성 프로세스에 대한 실험적 지원을 추가했다고 합니다.
이러한 옵션은 리소스 사용량 증가 및 동시성 문제로 인한 메모리 부족 오류를 초래할 수 있으므로,
특별한 요구 사항이 없다면 기본 설정을 사용하는 것이 좋습니다.
const nextConfig = {
experimental: {
// 빌드가 실패하기 전 페이지 생성 시도 횟수
staticGenerationRetryCount: 1,
// 워커당 처리할 페이지 수
staticGenerationMaxConcurrency: 8,
// 새 익스포트 워커를 시작하기 전에 처리할 최소 페이지 수
staticGenerationMinPagesPerWorker: 25,
},
}
export default nextConfig;
이번 매거진에서는 Next.js 15에서 업데이트 된 내용에 대해 알아보았습니다.
곧바로 최신 버전으로 업데이트 할 필요는 없지만,
변경된 부분에 대해서 잘 파악하고 마이그레이션을 준비하면 좋지 않을까 생각합니다.
그럼 저는 다음에 또 유익한 글로 찾아뵙겠습니다!
지금까지 소플이었습니다. 감사합니다 😀
지금 가입하고 프론트엔드 개발 관련 매거진을 이메일로 받아보세요!