2024
August
Next.js 15 Rc

πŸ”— Next.js 15 RC (opens in a new tab)

πŸ—“οΈ λ²ˆμ—­ λ‚ μ§œ: 2024.07.30

🧚 λ²ˆμ—­ν•œ 크루: 러기(λ°•μ •μš°)


Next.js 15 RC

Next.js 15 Release Candidate (RC)κ°€ 이제 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이 초기 버전을 톡해 λ‹€κ°€μ˜€λŠ” μ•ˆμ •ν™” 릴리슀 전에 μ΅œμ‹  κΈ°λŠ₯을 ν…ŒμŠ€νŠΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • React: React 19 RC 지원, React 컴파일러(μ‹€ν—˜μ ), ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜ κ°œμ„ 
  • 캐싱: fetch μš”μ²­, GET 라우트 ν•Έλ“€λŸ¬, ν΄λΌμ΄μ–ΈνŠΈ 탐색이 기본적으둜 μΊμ‹œλ˜μ§€ μ•ŠμŒ
  • λΆ€λΆ„ ν”„λ¦¬λ Œλ”λ§(μ‹€ν—˜): 점진적 채택을 μœ„ν•œ μƒˆλ‘œμš΄ λ ˆμ΄μ•„μ›ƒ 및 νŽ˜μ΄μ§€ ꡬ성 μ˜΅μ…˜
  • next/after(μ‹€ν—˜): 응닡이 슀트리밍된 ν›„ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” μƒˆλ‘œμš΄ API
  • create-next-app: 둜컬 κ°œλ°œμ—μ„œ Turbopack을 ν™œμ„±ν™”ν•˜λŠ” μƒˆ ν”Œλž˜κ·Έμ™€ μ—…λ°μ΄νŠΈλœ λ””μžμΈ
  • μ™ΈλΆ€ νŒ¨ν‚€μ§€ λ²ˆλ“€λ§(μ•ˆμ •): App 및 Pages λΌμš°ν„°λ₯Ό μœ„ν•œ μƒˆλ‘œμš΄ ꡬ성 μ˜΅μ…˜

였늘 Next.js 15 RCλ₯Ό μ‹œλ„ν•΄λ³΄μ„Έμš”:

 
npm install next@rc react@rc react-dom@rc

μ°Έκ³ : Next.js 15 RC λ¬Έμ„œλŠ” Next.js 15 GAκ°€ λ°œν‘œλ  λ•ŒκΉŒμ§€ rc.nextjs.org/docsμ—μ„œ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

React 19 RC

Next.js μ•± λΌμš°ν„°λŠ” ν”„λ ˆμž„μ›Œν¬μš© React canary channel을 기반으둜 κ΅¬μΆ•λ˜μ–΄, κ°œλ°œμžλ“€μ΄ React 19 μΆœμ‹œ 전에 μ΄λŸ¬ν•œ μƒˆλ‘œμš΄ React APIλ₯Ό μ‚¬μš©ν•˜κ³  ν”Όλ“œλ°±μ„ μ œκ³΅ν•  수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€.

Next.js 15 RCλŠ” 이제 React 19 RCλ₯Ό μ§€μ›ν•˜λ©°, μ΄λŠ” Actions와 같은 ν΄λΌμ΄μ–ΈνŠΈ 및 μ„œλ²„μ˜ μƒˆλ‘œμš΄ κΈ°λŠ₯을 ν¬ν•¨ν•©λ‹ˆλ‹€.

μžμ„Έν•œ λ‚΄μš©μ„ μ•Œμ•„λ³΄λ €λ©΄ Next.js 15 μ—…κ·Έλ ˆμ΄λ“œ κ°€μ΄λ“œ, React 19 μ—…κ·Έλ ˆμ΄λ“œ κ°€μ΄λ“œ, 그리고 React Conf Keynoteλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.

μ°Έκ³ : 일뢀 타사 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” 아직 React 19와 ν˜Έν™˜λ˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

React 컴파일러(μ‹€ν—˜)

React μ»΄νŒŒμΌλŸ¬λŠ” Meta의 React νŒ€μ΄ λ§Œλ“  μƒˆλ‘œμš΄ μ‹€ν—˜μ  μ»΄νŒŒμΌλŸ¬μž…λ‹ˆλ‹€. 이 μ»΄νŒŒμΌλŸ¬λŠ” JavaScript의 μ˜λ―Έμ™€ React의 κ·œμΉ™μ„ 깊이 μ΄ν•΄ν•˜μ—¬ μ½”λ“œλ₯Ό μžλ™μœΌλ‘œ μ΅œμ ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이 μ»΄νŒŒμΌλŸ¬λŠ” useMemo와 useCallbackκ³Ό 같은 APIλ₯Ό 톡해 κ°œλ°œμžκ°€ ν•΄μ•Ό ν•  μˆ˜λ™ λ©”λͺ¨ν™”λ₯Ό 쀄여 μ½”λ“œκ°€ 더 κ°„λ‹¨ν•˜κ³  μœ μ§€ 관리가 μ‰¬μ›Œμ§€λ©° 였λ₯˜κ°€ μ μ–΄μ§€κ²Œ ν•©λ‹ˆλ‹€.

Next.js 15μ—μ„œλŠ” React μ»΄νŒŒμΌλŸ¬μ— λŒ€ν•œ 지원을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

babel-plugin-react-compilerλ₯Ό μ„€μΉ˜ν•˜μ„Έμš”:

터미널

npm install babel-plugin-react-compiler

그런 λ‹€μŒ next.config.js에 experimental.reactCompiler μ˜΅μ…˜μ„ μΆ”κ°€ν•˜μ„Έμš”:

next.config.ts

const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};
 
module.exports = nextConfig;

μ„ νƒμ μœΌλ‘œ, 컴파일러λ₯Ό "opt-in" λͺ¨λ“œλ‘œ μ‹€ν–‰ν•˜λ„λ‘ ꡬ성할 수 μžˆμŠ΅λ‹ˆλ‹€:

next.config.ts

typescriptμ½”λ“œ 볡사
const nextConfig = {
  experimental: {
    reactCompiler: {
      compilationMode: 'annotation',
    },
  },
};
 
module.exports = nextConfig;
 

μ°Έκ³ : React μ»΄νŒŒμΌλŸ¬λŠ” ν˜„μž¬ Babel ν”ŒλŸ¬κ·ΈμΈμ„ 톡해 Next.jsμ—μ„œλ§Œ μ‚¬μš©ν•  수 있으며, μ΄λŠ” λΉŒλ“œ μ‹œκ°„μ„ 느리게 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

React μ»΄νŒŒμΌλŸ¬μ™€ μ‚¬μš© κ°€λŠ₯ν•œ Next.js ꡬ성 μ˜΅μ…˜μ— λŒ€ν•΄ μžμ„Ένžˆ μ•Œμ•„λ³΄μ„Έμš”.

Hydration 였λ₯˜ κ°œμ„ 

Next.js 14.1μ—μ„œλŠ” 였λ₯˜ λ©”μ‹œμ§€μ™€ ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜κ°€ κ°œμ„ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. Next.js 15λŠ” 여기에 더해 ν–₯μƒλœ ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜ λ·°λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. 이제 ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜λŠ” 였λ₯˜μ˜ μ†ŒμŠ€ μ½”λ“œμ™€ 문제 해결에 λŒ€ν•œ μ œμ•ˆμ„ ν‘œμ‹œν•©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄, λ‹€μŒμ€ Next.js 14.1의 이전 ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜ λ©”μ‹œμ§€μž…λ‹ˆλ‹€:

ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜ λ©”μ‹œμ§€ in Next.js 14.1

이미지 1 Next.js 15 RCμ—μ„œλŠ” 이λ₯Ό λ‹€μŒκ³Ό 같이 κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€:

이미지 2

ν•˜μ΄λ“œλ ˆμ΄μ…˜ 였λ₯˜ λ©”μ‹œμ§€ improved in Next.js 15 RC

캐싱 μ—…λ°μ΄νŠΈ

Next.js μ•± λΌμš°ν„°λŠ” 의견이 반영된 캐싱 κΈ°λ³Έκ°’μœΌλ‘œ μΆœμ‹œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 기본적으둜 κ°€μž₯ μ„±λŠ₯이 쒋은 μ˜΅μ…˜μ„ μ œκ³΅ν•˜λ©° ν•„μš”ν•œ 경우 μ˜΅νŠΈμ•„μ›ƒν•  수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.

κ·€ν•˜μ˜ ν”Όλ“œλ°±μ„ 기반으둜 μš°λ¦¬λŠ” 캐싱 νœ΄λ¦¬μŠ€ν‹±κ³Ό 그것이 λΆ€λΆ„ ν”„λ¦¬λ Œλ”λ§(PPR) 및 fetchλ₯Ό μ‚¬μš©ν•˜λŠ” 타사 λΌμ΄λΈŒλŸ¬λ¦¬μ™€ μ–΄λ–»κ²Œ μƒν˜Έ μž‘μš©ν• μ§€λ₯Ό μž¬ν‰κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

Next.js 15μ—μ„œλŠ” fetch μš”μ²­, GET 라우트 ν•Έλ“€λŸ¬ 및 ν΄λΌμ΄μ–ΈνŠΈ λΌμš°ν„° μΊμ‹œμ— λŒ€ν•œ 캐싱 기본값을 μΊμ‹œμ—μ„œ 기본적으둜 μ œμ™Έν•˜λ„λ‘ λ³€κ²½ν•©λ‹ˆλ‹€. 이전 λ™μž‘μ„ μœ μ§€ν•˜λ €λ©΄ κ³„μ†ν•΄μ„œ 캐싱을 μ˜΅νŠΈμΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μš°λ¦¬λŠ” μ•žμœΌλ‘œ λͺ‡ 달 λ™μ•ˆ Next.jsμ—μ„œ 캐싱을 계속 κ°œμ„ ν•  것이며 Next.js 15 GA λ°œν‘œμ—μ„œ 더 λ§Žμ€ μ„ΈλΆ€ 정보λ₯Ό κ³΅μœ ν•  κ²ƒμž…λ‹ˆλ‹€.

fetch μš”μ²­μ€ 더 이상 기본적으둜 μΊμ‹œλ˜μ§€ μ•ŠμŒ

Next.jsλŠ” Web fetch API μΊμ‹œ μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μ„œλ²„ μ‚¬μ΄λ“œ fetch μš”μ²­μ΄ ν”„λ ˆμž„μ›Œν¬μ˜ 지속적인 HTTP μΊμ‹œμ™€ μ–΄λ–»κ²Œ μƒν˜Έ μž‘μš©ν• μ§€λ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€:

fetch("https://...", { cache: "force-cache" | "no-store" });
  • no-store: 맀 μš”μ²­λ§ˆλ‹€ 원격 μ„œλ²„μ—μ„œ λ¦¬μ†ŒμŠ€λ₯Ό κ°€μ Έμ˜€λ©° μΊμ‹œλ₯Ό μ—…λ°μ΄νŠΈν•˜μ§€ μ•ŠμŒ
  • force-cache: μΊμ‹œμ— λ¦¬μ†ŒμŠ€κ°€ 있으면 μΊμ‹œμ—μ„œ, 그렇지 μ•ŠμœΌλ©΄ 원격 μ„œλ²„μ—μ„œ λ¦¬μ†ŒμŠ€λ₯Ό κ°€μ Έμ˜€κ³  μΊμ‹œλ₯Ό μ—…λ°μ΄νŠΈ

Next.js 14μ—μ„œλŠ” dynamic ν•¨μˆ˜ λ˜λŠ” dynamic ꡬ성 μ˜΅μ…˜μ΄ μ‚¬μš©λ˜μ§€ μ•ŠλŠ” ν•œ 기본적으둜 force-cacheκ°€ μ‚¬μš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Next.js 15μ—μ„œλŠ” μΊμ‹œ μ˜΅μ…˜μ΄ μ œκ³΅λ˜μ§€ μ•ŠμœΌλ©΄ 기본적으둜 no-storeκ°€ μ‚¬μš©λ©λ‹ˆλ‹€. μ΄λŠ” fetch μš”μ²­μ΄ 기본적으둜 μΊμ‹œλ˜μ§€ μ•ŠμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.

μ—¬μ „νžˆ fetch μš”μ²­μ— λŒ€ν•΄ 캐싱을 μ˜΅νŠΈμΈν•  수 μžˆμŠ΅λ‹ˆλ‹€:

  • 단일 fetch ν˜ΈμΆœμ—μ„œ μΊμ‹œ μ˜΅μ…˜μ„ force-cache둜 μ„€μ •
  • 단일 λΌμš°νŠΈμ— λŒ€ν•΄ dynamic 라우트 ꡬ성 μ˜΅μ…˜μ„ 'force-static'으둜 μ„€μ •
  • λ ˆμ΄μ•„μ›ƒ λ˜λŠ” νŽ˜μ΄μ§€μ˜ λͺ¨λ“  fetch μš”μ²­μ„ 기본적으둜 force-cacheλ₯Ό μ‚¬μš©ν•˜λ„λ‘ fetchCache 라우트 ꡬ성 μ˜΅μ…˜μ„ 'default-cache'둜 μ„€μ •ν•˜μ—¬ μΊμ‹œ μ˜΅μ…˜μ„ λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•˜μ§€ μ•Šμ€ 경우 λͺ¨λ‘ λ¬΄μ‹œ
  • GET 라우트 ν•Έλ“€λŸ¬λŠ” 더 이상 기본적으둜 μΊμ‹œλ˜μ§€ μ•ŠμŒ

Next 14μ—μ„œλŠ” dynamic ν•¨μˆ˜ λ˜λŠ” dynamic ꡬ성 μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν•œ GET HTTP λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 라우트 ν•Έλ“€λŸ¬κ°€ 기본적으둜 μΊμ‹œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. Next.js 15μ—μ„œλŠ” GET ν•¨μˆ˜κ°€ 기본적으둜 μΊμ‹œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ—¬μ „νžˆ static 라우트 ꡬ성 μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ 캐싱을 μ˜΅νŠΈμΈν•  수 μžˆμŠ΅λ‹ˆλ‹€(ex. export dynamic = 'force-static').

sitemap.ts, opengraph-image.tsx, icon.tsx와 같은 특수 라우트 ν•Έλ“€λŸ¬ 및 기타 메타데이터 νŒŒμΌμ€ dynamic ν•¨μˆ˜ λ˜λŠ” dynamic ꡬ성 μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν•œ 기본적으둜 정적 μƒνƒœλ‘œ μœ μ§€λ©λ‹ˆλ‹€.

ν΄λΌμ΄μ–ΈνŠΈ λΌμš°ν„° μΊμ‹œλŠ” 기본적으둜 νŽ˜μ΄μ§€ ꡬ성 μš”μ†Œλ₯Ό μΊμ‹œν•˜μ§€ μ•ŠμŒ

Next.js 14.2.0μ—μ„œλŠ” λΌμš°ν„° μΊμ‹œμ˜ μ‚¬μš©μž μ •μ˜ ꡬ성을 ν—ˆμš©ν•˜κΈ° μœ„ν•΄ μ‹€ν—˜μ  staleTimes ν”Œλž˜κ·Έλ₯Ό λ„μž…ν–ˆμŠ΅λ‹ˆλ‹€.

Next.js 15μ—μ„œλŠ” 이 ν”Œλž˜κ·Έκ°€ μ—¬μ „νžˆ μ ‘κ·Ό κ°€λŠ₯ν•˜μ§€λ§Œ νŽ˜μ΄μ§€ μ„Έκ·Έλ¨ΌνŠΈμ— λŒ€ν•œ staleTime을 0으둜 μ„€μ •ν•˜μ—¬ κΈ°λ³Έ λ™μž‘μ„ λ³€κ²½ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 앱을 탐색할 λ•Œ ν΄λΌμ΄μ–ΈνŠΈκ°€ νƒμƒ‰μ˜ μΌλΆ€λ‘œ ν™œμ„±ν™”λ˜λŠ” νŽ˜μ΄μ§€ ꡬ성 μš”μ†Œμ—μ„œ 항상 μ΅œμ‹  데이터λ₯Ό λ°˜μ˜ν•¨μ„ μ˜λ―Έν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ€‘μš”ν•œ λ™μž‘μ€ μ—¬μ „νžˆ λ³€κ²½λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€:

곡유 λ ˆμ΄μ•„μ›ƒ λ°μ΄ν„°λŠ” λΆ€λΆ„ λ Œλ”λ§μ„ 계속 μ§€μ›ν•˜κΈ° μœ„ν•΄ μ„œλ²„μ—μ„œ λ‹€μ‹œ κ°€μ Έμ˜€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이전/λ‹€μŒ 탐색은 μ—¬μ „νžˆ μΊμ‹œμ—μ„œ λ³΅μ›λ˜μ–΄ λΈŒλΌμš°μ €κ°€ 슀크둀 μœ„μΉ˜λ₯Ό 볡원할 수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€. Loading.jsλŠ” μ—¬μ „νžˆ 5λΆ„ λ™μ•ˆ μΊμ‹œλ©λ‹ˆλ‹€(λ˜λŠ” staleTimes.static κ΅¬μ„±μ˜ κ°’). 이전 ν΄λΌμ΄μ–ΈνŠΈ λΌμš°ν„° μΊμ‹œ λ™μž‘μ„ μ˜΅νŠΈμΈν•˜λ €λ©΄ λ‹€μŒ ꡬ성을 μ„€μ •ν•˜μ„Έμš”:

// next.config.ts
const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 30,
    },
  },
};
 
module.exports = nextConfig;

λΆ€λΆ„ ν”„λ¦¬λ Œλ”λ§μ˜ 점진적 채택(μ‹€ν—˜μ )

Next.js 14μ—μ„œλŠ” λΆ€λΆ„ ν”„λ¦¬λ Œλ”λ§(PPR)을 λ„μž…ν–ˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 같은 νŽ˜μ΄μ§€μ—μ„œ 정적 λ Œλ”λ§κ³Ό 동적 λ Œλ”λ§μ„ κ²°ν•©ν•˜λŠ” μ΅œμ ν™”μž…λ‹ˆλ‹€.

Next.jsλŠ” ν˜„μž¬ cookies(),Β headers() 및 μΊμ‹œλ˜μ§€ μ•Šμ€ 데이터 μš”μ²­κ³Ό 같은 동적 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν•œ 정적 λ Œλ”λ§μ„ κΈ°λ³Έκ°’μœΌλ‘œ ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ APIλŠ” 전체 라우트λ₯Ό 동적 λ Œλ”λ§μœΌλ‘œ μ˜΅νŠΈμΈν•©λ‹ˆλ‹€. PPR을 μ‚¬μš©ν•˜λ©΄ 동적 UIλ₯Ό Suspense 경계에 λž˜ν•‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μƒˆλ‘œμš΄ μš”μ²­μ΄ λ“€μ–΄μ˜€λ©΄ Next.jsλŠ” 정적 HTML μ‰˜μ„ μ¦‰μ‹œ μ œκ³΅ν•œ λ‹€μŒ λ™μΌν•œ HTTP μš”μ²­μ—μ„œ 동적 뢀뢄을 λ Œλ”λ§ν•˜κ³  μŠ€νŠΈλ¦¬λ°ν•©λ‹ˆλ‹€.

점진적 채택을 ν—ˆμš©ν•˜κΈ° μœ„ν•΄ νŠΉμ • λ ˆμ΄μ•„μ›ƒ 및 νŽ˜μ΄μ§€λ₯Ό PPR에 μ˜΅νŠΈμΈν•  수 μžˆλŠ” experimental_ppr 라우트 ꡬ성 μ˜΅μ…˜μ„ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€:

import { Suspense } from "react"
import { StaticComponent, DynamicComponent } from "@/app/ui"
 
export const experimental_ppr = true
 
export default