[ 살펴보기 ] NextJS - v15 Stable

[ 살펴보기 ] NextJS - v15 Stable

·

6 min read

2024년 5월 NextJS 15 RC ( Release Candidate )가 release되고 그 뒤 5개월이 지난 뒤 stable version이 release되었다. React 19 stable version이 release되고 나서 Next15 stable version이 release될 줄 알았지만 포스트를 작성하는 기준 React는 아직 stable 19 version이 release되지 않은 상태이다. 이제 새로운 NextJS version에는 어떤 기능이 추가되었는지 살펴보자.

Next version 관리를 위한 CLI

@next/codemod cli를 통해 project에 사용되는 nextjs의 최신버전 upgrade 과정을 보다 쉽게 할 수 있도록 도와준다. Breaking change를 수반하는 latest version으로 upgrade한다면 다음 command를 통해 보다 수월하게 upgrade에 필요한 과정을 진행할 수 있다.

npx @next/codemod@canary update latest

위의 예제와 같이 @canary tag를 설정하면 가장 최근 version의 codemod가 사용되므로 codemod를 통해 upgrade과정을 진행할 때 @canary tag를 설정하는 것이 권장된다.

Async Request APIs

synchronous function이였던 headers, cookies, params, searchParams와 같은 function들이 15 version 부터는 asynchronous function으로 전환된다.

import { cookies } from 'next/headers'

// Before Next15
const cookieStore = cookies()
const token = cookieStore.get('token')

// After
const cookieStore = await cookies()
const token = cookieStore.get('token')

Migration으로 발생하는 issue를 최소화 하기 위해 당분간은 위의 function을 기존과 같이 synchronous function으로 사용할 수 있지만 관련 warning이 발생한다.

import { cookies, type UnsafeUnwrappedCookies } from 'next/headers'

// Before Next15
const cookieStore = cookies()
const token = cookieStore.get('token')

// After
const cookieStore = cookies() as unknown as UnsafeUnwrappedCookies
const token = cookieStore.get('token')

관련 migration 작업 역시 codemod cli를 통해 보다 수월하게 진행할 수 있다.

npx @next/codemod@canary next-async-request-api .

예를 들어 code가 아래와 같은 상태에서 위의 codemod cli를 실행하면 다음과 같이 변경된다.

// codemod cli 실행 전
export default function Home() {
  const cookieStore = cookies();
  const token = cookieStore.get("test");
  return (
   ...
 )
}
// codemod cli 실행 후
export default async function Home() {
  const cookieStore = await cookies();
  const token = cookieStore.get("test");
  return (
    ...
  )
}

Caching

해당 부분은 개인적으로도 굉장히 환영하는 변화이다. App route의 등장과 함께 많은 개발자들의 불만을 샀던 cache behavior가 Next15부터는 변경된다.

Next 14 version에선 GET method route handler에 default로 cache가 적용되었지만 Next 15 부터는 default로 cache가 적용되지 않는다. 만약 기존처럼 Get method route handler에 cache를 적용하고 싶다면 export dynamic = 'force-static’ config option을 사용해준다.

또한 Next 15부터 Client Route cache가 default로 적용되지 않는다. 기존에는 ( v14.2.0 이전 ) 일정 시간 동안 client route cache를 framework에서 강제 했었기에 이 역시 환영받을 만한 변화라고 생각한다. Client route에 적용되는 cache behavior는 변경 되지만 다음 사항은 변경되지 않고 그대로 유지된다.

  • client side navigation이 발생하더라도 layout은 server에서 새로 fetch되진 않는다.

  • browser의 back/forward button을 통한 페이지 이동시에는 route cache가 적용된다. ( 해당 페이지를 server에서 새로 fetch해서 render하지 않는다 )

  • loading.js는 default로 5분간 cache된다. ( next 설정 파일인 next.config.ts에서 staleTimes.static 값을 변경하면 해당 값에 따른다 )

만약 기존의 client route cache를 그대로 적용하고자 한다면 next.config.ts 설정 파일에 다음과 같이 설정해준다.

const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 30,
    },
  },
};

export default nextConfig;

React 19 RC

Next 15 version부터 의존하는 React version이 19 RC version으로 상향된다. React 19가 아직 RC에 머물러 있음에도 여러 테스트를 통해 도입해도 안전하다고 Next team은 판단한 것으로 보인다. 다만 Page Router를 사용한다면 호환성을 위해 react 18 version을 계속해서 사용할 수 있다고 한다.

하지만 Next15를 통해 새로운 변화가 도입되는 만큼 하나의 project에 app router와 page router를 함께 사용하는 것은 권장되지 않는다.

React Compiler

React 19에 도입될 compiler는 react 기반 code를 분석해 필요한 optimization 작업을 추가해준다. 현재는 experimental 단계이고 babel plugin을 통해서만 지원되므로 실제 개발 단계에서 활발하게 사용할 수 있기 까지는 조금 더 시간이 소요될 것으로 보인다.

Hydration Error improvements

Next 15에선 보다 나은 Hydration 관련 error가 발생했을 때 다음과 같이 error의 source code와 suggestion을 포함한 보다 나은 error 정보를 제공한다.

Hydration error message improved in Next.js 15

Turbopack

Next project는 내부적으로 turbopack을 통해 dev server를 실행하고 project를 bundle한다. 그리고 Next 15에선 turbopack이 stable 단계에 접어든다. Next team에 따르면 vercel.com에서 제공하는 web application 기준 stable turbopack을 적용해 다음과 같은 benchmark 결과를 얻었다고 말한다.

  • 76.7% 향샹된 local server setart up

  • 96.3% 향상된 fast refresh ( code update 발생 시 )

  • 45.8% 향상된 초기 route compile 시간 ( without cache )

Static Route Indicator

Next 15 version부터 개발 환경에서 특정 route가 static route인지 dynamic route인지 구분할 수 있는 indicator가 추가된다. 현재 page가 static page라면 페이지 하단 다음과 같은 icon을 확인할 수 있다.

unstable_after

NextJS Server가 client request를 처리할 때 client request에 대한 response외에 logging이나 analytics과 같은 추가 작업을 해야할 수도 있다. 하지만 만약 logging과 analytics 작업으로 인해 client request에 대한 response를 전달하는 시간이 늘어난다면 client request에 직접적으로 관련이 없는 작업은 response를 우선 전달하고 별도로 하는 것이 좋을 수 있다.

하지만 serverless function을 통해 client request를 처리한다면 response가 전달되면서 serverless function 역시 종료되기 때문에 response를 전달한 다음 기타 작업을 하는 것에 제한이 있다. Next 15는 이러한 상황에 사용할 수 있는 after function을 제공한다.

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}</>;
}

after function은 아직 experimental 단계 이므로 아래와 같이 next config 파일을 설정 해주어야 한다.

next.config.ts

const nextConfig = {
  experimental: {
    after: true,
  },
};

export default nextConfig;

instrumentation

NextJS 15 version에서 instrumentation 기능이 stable 단계로 들어서게 된다. Project root 경로에 instrumentation.ts 파일을 생성하고 register function을 export하면 해당 register function은 NextJS server가 시작될 때 한 번만 실행된다.

instrumentation.ts

import { registerOTel } from '@vercel/otel'

export function register() {
  registerOTel('next-app')
}

추가로 instrumentation 파일에서 onRequestError function을 export하면 NextJS에서 server error ( next server error )가 발생 할 때 해당 function도 실행되며 발생한 error 정보와 기타 context 정보를 parameter로 전달 받는다.

이를 통해 server error가 발생했을 때 특정 작업을 하거나 error log를 관리하는 service를 이용 중이라면 해당 error를 log 관리 service로 전달하는 작업을 수행할 수 있다.

import { registerOTel } from '@vercel/otel'
import { type Instrumentation } from 'next'

export const onRequestError: Instrumentation.onRequestError = async (
  err,
  request,
  context
) => {
  await fetch("https://.../report-error", {
    method: "POST",
    body: JSON.stringify({
      message: (err as Error).message,
      request,
      context,
    }),
    headers: {
      "Content-Type": "application/json",
    },
  });
};

export function register() {
  registerOTel('next-app')
}

Form component

NextJS 15에선 Next가 제공하는 Form component에 prefetching과 client-side navigation기능이 default로 추가된다. 예를 들어 다음 예제에서 form이 submit되면 /search route로 client-side navigation을 통해 이동한다.

import Form from 'next/form';

export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  );
}

ESLint 9 support

ESLint 9 version에서 ESLint configuration format이 변경되었는데 NextJS 15부터 변경된 ESLint 9 version의 configuration format을 지원한다. 현재는 호환성을 유지하기 위해 ESLint 8 version의 config format과 ESLint 9 version의 config format 모두 사용할 수 있지만 추후 새로운 version으로 upgrade해도 문제가 없도록 ESLint 9 version으로 migration하는 것을 권장한다.

( Reference - ESLint migration guide )

Other Changes

위에서 소개한 중요 변경 사항 외에도 변경된 사항이 존재하며 변경된 사항들의 모든 list는 documentation blog를 통해 확인할 수 있다. ( Reference - Next.js 15 )