๐ **Next.js 15 (opens in a new tab)
๐๏ธ ๋ฒ์ญ ๋ ์ง: 2024.11.07
๐ง ๋ฒ์ญํ ํฌ๋ฃจ: ๋ฌ๊ธฐ(๋ฐ์ ์ฐ)
Next.js 15
Next.js 15๊ฐ ๊ณต์์ ์ผ๋ก ์์ ํ๋์ด ํ๋ก๋์ ํ๊ฒฝ์์ ์ฌ์ฉํ ์ค๋น๊ฐ ๋์์ต๋๋ค. ์ด๋ฒ ๋ฆด๋ฆฌ์ค๋ RC1 (opens in a new tab)๊ณผ RC2 (opens in a new tab)์ ์ ๋ฐ์ดํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉฐ, ์์ ์ฑ์ ์ค์ ์ ๋๋ฉด์๋ ์ฌ๋ฌ๋ถ์ด ์ข์ํ์ค ๋งํ ํฅ๋ฏธ๋ก์ด ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐํ์ต๋๋ค. ์ค๋ ๋ฐ๋ก Next.js 15๋ฅผ ์๋ํด ๋ณด์ธ์.
# Use the new automated upgrade CLI
npx @next/codemod@canary upgrade latest
# ...or upgrade manually
npm install next@latest react@rc react-dom@rc
๋ค๊ฐ์ค๋ ๋ชฉ์์ผ, 10์ 24์ผ์ ์ด๋ฆฌ๋ Next.js Conf์์ ๊ณง ๋ค๊ฐ์ฌ ์ ๋ฐ์ดํธ์ ๋ํด ๋ ๋ง์ ๋ด์ฉ์ ๊ณต์ ํ ์์ ์ ๋๋ค.
Next.js 15์ ์๋ก์ด ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- @next/codemod CLI: ์ต์ Next.js ๋ฐ React ๋ฒ์ ์ผ๋ก ์์ฝ๊ฒ ์ ๊ทธ๋ ์ด๋ํ ์ ์๋ CLI ๋๊ตฌ.
- ๋น๋๊ธฐ ์์ฒญ API (Breaking): ๋ ๋๋ง ๋ฐ ์บ์ฑ ๋ชจ๋ธ์ ๋จ์ํํ๊ธฐ ์ํ ๋จ๊ณ์ ์ ๊ทผ.
- ์บ์ฑ ๋ฐฉ์ (Breaking): fetch ์์ฒญ, GET ๋ผ์ฐํธ ํธ๋ค๋ฌ, ํด๋ผ์ด์ธํธ ํ์์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์บ์๋์ง ์์.
- React 19 ์ง์: React 19, React Compiler (์คํ์ ), ๋ฐ hydration ์ค๋ฅ ๊ฐ์ ์ง์.
- Turbopack Dev (Stable): ์ฑ๋ฅ ๋ฐ ์์ ์ฑ ํฅ์.
- ์ ์ ํ์๊ธฐ: ๊ฐ๋ฐ ์ค ์ ์ ๋ผ์ฐํธ๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ํ๋ ์ ์ธ๋์ผ์ดํฐ.
- unstable_after API (Experimental): ์๋ต์ด ์คํธ๋ฆฌ๋ฐ ์๋ฃ๋ ํ ์ฝ๋๋ฅผ ์คํํ๋ ์คํ์ API.
- instrumentation.js API (Stable): ์๋ฒ ์๋ช ์ฃผ๊ธฐ ๊ฐ์์ฑ์ ์ํ ์๋ก์ด API.
- ํฅ์๋ ํผ (next/form): HTML ํผ์ ํด๋ผ์ด์ธํธ ์ธก ํ์ ๊ธฐ๋ฅ ์ถ๊ฐ.
- next.config: next.config.ts์์ TypeScript ์ง์.
- ์ ํ ํธ์คํ ๊ฐ์ : Cache-Control ํค๋์ ๋ํ ๋ ๋ง์ ์ ์ด ์ต์ ์ ๊ณต.
- ์๋ฒ ์ก์ ๋ณด์: ์ถ์ธก ๋ถ๊ฐ๋ฅํ ์๋ํฌ์ธํธ์ ์ฌ์ฉํ์ง ์๋ ์ก์ ์ ๊ฑฐ.
- ์ธ๋ถ ํจํค์ง ๋ฒ๋ค๋ง (Stable): ์ฑ ๋ฐ ํ์ด์ง ๋ผ์ฐํฐ์ฉ ์ ๊ตฌ์ฑ ์ต์ .
- ESLint 9 ์ง์: ESLint 9 ์ง์ ์ถ๊ฐ.
- ๊ฐ๋ฐ ๋ฐ ๋น๋ ์ฑ๋ฅ: ๋ ๋น ๋ฅธ ๋น๋ ์๊ฐ๊ณผ ํฅ์๋ Fast Refresh.
์ํํ ์ ๊ทธ๋ ์ด๋๋ฅผ ์ํ @next/codemod CLI
๋ชจ๋ ์ฃผ์ Next.js ๋ฆด๋ฆฌ์ค์๋ codemod๋ผ๋ ์๋ ์ฝ๋ ๋ณํ ๋๊ตฌ๊ฐ ํฌํจ๋์ด ์์ด, ์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ์ ์์ฝ๊ฒ ์ ๊ทธ๋ ์ด๋ํ ์ ์๋๋ก ์ง์ํฉ๋๋ค.
์ด๋ฒ์๋ ์ ๊ทธ๋ ์ด๋ ๊ณผ์ ์ ๋์ฑ ๋งค๋๋ฝ๊ฒ ๋๊ธฐ ์ํด, ํฅ์๋ codemod CLI๋ฅผ ์ถ์ํ์ต๋๋ค.
npx @next/codemod@canary upgrade latest
์ด ๋๊ตฌ๋ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ต์ ์์ ๋ฒ์ ๋๋ ์ฌ์ ๋ฆด๋ฆฌ์ค ๋ฒ์ ์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ๋ ๋ฐ ๋์์ ์ค๋๋ค. CLI๋ ์ข ์์ฑ์ ์ ๋ฐ์ดํธํ๊ณ , ์ฌ์ฉ ๊ฐ๋ฅํ codemod๋ฅผ ํ์ํ๋ฉฐ, ์ด๋ฅผ ์ ์ฉํ๋ ๊ณผ์ ์ ์๋ดํฉ๋๋ค.
canary ํ๊ทธ๋ codemod์ ์ต์ ๋ฒ์ ์ ์ฌ์ฉํ๋ฉฐ, latest๋ Next.js ๋ฒ์ ์ ์ง์ ํฉ๋๋ค. ์ต์ Next.js ๋ฒ์ ์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ๋๋ผ๋, ์ฌ์ฉ์ ํผ๋๋ฐฑ์ ๊ธฐ๋ฐ์ผ๋ก ๋๊ตฌ๋ฅผ ์ง์์ ์ผ๋ก ๊ฐ์ ํ ๊ณํ์ด๋ฏ๋ก canary ๋ฒ์ ์ codemod๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
Next.js codemod CLI์ ๋ํ ์์ธํ ๋ด์ฉ์ Next.js ๊ณต์ ๋ฌธ์ (opens in a new tab)๋ฅผ ์ฐธ์กฐํ์ธ์.
๋น๋๊ธฐ Request API (์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ)
๊ธฐ์กด ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง(SSR)์์๋ ์๋ฒ๊ฐ ์์ฒญ์ ๊ธฐ๋ค๋ ธ๋ค๊ฐ ์ฝํ ์ธ ๋ฅผ ๋ ๋๋งํฉ๋๋ค. ํ์ง๋ง ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ์์ฒญ์ ๋ฐ๋ฅธ ํน์ ๋ฐ์ดํฐ์ ์์กดํ์ง ์์ผ๋ฏ๋ก, ์์ฒญ์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ ๋๋งํ ์ ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ค๋ฆด ํ์๊ฐ ์์ต๋๋ค. ์ด์์ ์ผ๋ก๋ ์๋ฒ๊ฐ ์์ฒญ์ด ๋์ฐฉํ๊ธฐ ์ ์ ๊ฐ๋ฅํ ํ ๋ง์ ์ค๋น๋ฅผ ๋ง์ณ์ผ ํฉ๋๋ค. ์ด๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๊ณ ๋ฏธ๋์ ์ต์ ํ๋ฅผ ์ํด, ์ธ์ ์์ฒญ์ ๊ธฐ๋ค๋ ค์ผ ํ ์ง๋ฅผ ์๋ฒ๊ฐ ์ ํ์๊ฐ ์์ต๋๋ค.
๋ฐ๋ผ์, ํค๋, ์ฟ ํค, 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
- params (layout.js, page.js, route.js, default.js, generateMetadata, generateViewport ํ์ผ๋ค์์)
- searchParams (page.js์์)
๋ ์ฝ๊ฒ ๋ง์ด๊ทธ๋ ์ด์ ํ ์ ์๋๋ก, ์ด๋ฌํ API๋ ์ผ์์ ์ผ๋ก ๋๊ธฐ ๋ฐฉ์์ผ๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ์ง๋ง, ๋ค์ ์ฃผ์ ๋ฒ์ ๊น์ง ๊ฐ๋ฐ ๋ฐ ํ๋ก๋์ ํ๊ฒฝ์์ ๊ฒฝ๊ณ ๋ฉ์์ง๊ฐ ํ์๋ฉ๋๋ค. ๋ง์ด๊ทธ๋ ์ด์ ์ ์๋ํํ๊ธฐ ์ํ codemod (opens in a new tab)๋ ์ ๊ณต๋ฉ๋๋ค.
npx @next/codemod@canary next-async-request-api .
codemod๊ฐ ์ฝ๋์ ์์ ํ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ํํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ, ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋ (opens in a new tab)๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์. ์๋ก์ด API๋ก Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ์์ (opens in a new tab)๋ ํจ๊ป ์ ๊ณตํ๊ณ ์์ต๋๋ค.
์บ์ฑ ๋ฐฉ์
Next.js App Router๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ๋ฅ์ ์ต์ ํํ ์บ์ฑ ๋ฐฉ์์ ์ ๊ณตํ์ฌ ํ์ ์ ์บ์ฑ์ ์ ํ์ ์ผ๋ก ํด์ ํ ์ ์๋๋ก ์ค๊ณ๋์์ต๋๋ค.
์ฌ์ฉ์ ํผ๋๋ฐฑ์ ๋ฐํ์ผ๋ก, Partial Prerendering(PPR)๊ณผ fetch๋ฅผ ์ฌ์ฉํ๋ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ํธ ์์ฉ ๋ฐฉ์์ ๊ณ ๋ คํ์ฌ ์บ์ฑ ๊ท์น (opens in a new tab)์ ์ฌ๊ฒํ ํ์ต๋๋ค.
Next.js 15์์๋ GET ๋ผ์ฐํธ ํธ๋ค๋ฌ์ ํด๋ผ์ด์ธํธ ๋ผ์ฐํฐ ์บ์์ ๊ธฐ๋ณธ ์ค์ ์ ์บ์ฑ๋ ์ํ์์ ๋น์บ์ฑ ์ํ๋ก ๋ณ๊ฒฝํ์ต๋๋ค. ์ด์ ์ ์บ์ฑ ๋์์ ์ ์งํ๋ ค๋ฉด, ์ง์ ์บ์ฑ์ ์ ํํ ์ ์์ต๋๋ค.
์์ผ๋ก ๋ช ๋ฌ ๊ฐ Next.js์ ์บ์ฑ ๊ธฐ๋ฅ์ ์ง์์ ์ผ๋ก ๊ฐ์ ํ ์์ ์ด๋ฉฐ, ๋ ๋ง์ ์ ๋ณด๋ฅผ ๊ณง ๊ณต์ ํ๊ฒ ์ต๋๋ค.
GET ๋ผ์ฐํธ ํธ๋ค๋ฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์บ์ฑ๋์ง ์์
Next.js 14์์๋ GET HTTP ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋ผ์ฐํธ ํธ๋ค๋ฌ๊ฐ ๋์ ํจ์๋ ๋์ ๊ตฌ์ฑ ์ต์ ์ ์ฌ์ฉํ์ง ์๋ ํ ๊ธฐ๋ณธ์ ์ผ๋ก ์บ์ฑ๋์์ต๋๋ค. ํ์ง๋ง Next.js 15๋ถํฐ๋ GET ํจ์๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์บ์ฑ๋์ง ์์ต๋๋ค.
์ฌ์ ํ export const dynamic = 'force-static'
๊ณผ ๊ฐ์ ์ ์ ๋ผ์ฐํธ ๊ตฌ์ฑ ์ต์
์ ์ฌ์ฉํ์ฌ ์บ์ฑ์ ํ์ฑํํ ์ ์์ต๋๋ค.
sitemap.ts, opengraph-image.tsx, icon.tsx์ ๊ฐ์ ํน๋ณํ ๋ผ์ฐํธ ํธ๋ค๋ฌ์ ๊ธฐํ ๋ฉํ๋ฐ์ดํฐ ํ์ผ์ ๋์ ํจ์๋ ๋์ ๊ตฌ์ฑ ์ต์ ์ ์ฌ์ฉํ์ง ์๋ ํ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ ์ผ๋ก ์ ์ง๋ฉ๋๋ค.
ํด๋ผ์ด์ธํธ ๋ผ์ฐํฐ ์บ์๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ด์ง ์ปดํฌ๋ํธ๋ฅผ ์บ์ฑํ์ง ์์
Next.js 14.2.0์์๋ ๋ผ์ฐํฐ ์บ์์ ์ฌ์ฉ์ ์ง์ ๊ตฌ์ฑ์ ์ํด ์คํ์ ์ธ staleTimes ํ๋๊ทธ๋ฅผ ๋์ ํ์ต๋๋ค.
Next.js 15์์๋ ์ด ํ๋๊ทธ๊ฐ ์ฌ์ ํ ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง, ํ์ด์ง ์ธ๊ทธ๋จผํธ์ ๊ธฐ๋ณธ staleTime ๊ฐ์ 0์ผ๋ก ๋ณ๊ฒฝํ์ต๋๋ค. ์ฆ, ์ฑ์ ํ์ํ ๋๋ง๋ค ํ์ฑํ๋ ํ์ด์ง ์ปดํฌ๋ํธ์ ์ต์ ๋ฐ์ดํฐ๊ฐ ํญ์ ๋ฐ์๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ค์๊ณผ ๊ฐ์ ์ค์ํ ๋์์ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค.
- ๊ณต์ ๋ ์ด์์ ๋ฐ์ดํฐ: ๋ถ๋ถ ๋ ๋๋ง์ ์ง์ํ๊ธฐ ์ํด ์๋ฒ์์ ๋ค์ ๊ฐ์ ธ์ค์ง ์์ต๋๋ค.
- ๋ค๋ก/์์ผ๋ก ํ์: ์คํฌ๋กค ์์น ๋ณต์์ ์ํด ์บ์์์ ๋ณต์๋ฉ๋๋ค.
- loading.js: 5๋ถ๊ฐ(๋๋ staleTimes.static ์ค์ ๊ฐ) ์บ์ฑ๋ ์ํ๋ฅผ ์ ์งํฉ๋๋ค.
์ด์ ํด๋ผ์ด์ธํธ ๋ผ์ฐํฐ ์บ์ ๋์์ ์ฌ์ฉํ๋ ค๋ฉด ๋ค์ ์ค์ ์ ํตํด ์ ํํ ์ ์์ต๋๋ค.
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 30,
},
},
};
export default nextConfig;
React 19
Next.js 15 ๋ฆด๋ฆฌ์ค์ ์ผํ์ผ๋ก, ๊ณง ์ถ์๋ React 19์ ํธํ๋๋๋ก ์ ๋ฐ์ดํธ๋ฅผ ์งํํ์ต๋๋ค.
๋ฒ์ 15์์ App Router๋ React 19 RC๋ฅผ ์ฌ์ฉํ๋ฉฐ, ์ปค๋ฎค๋ํฐ ํผ๋๋ฐฑ์ ๋ฐ์ํด Pages Router์ React 18์ ๋ํ ํ์ ํธํ์ฑ๋ ์ถ๊ฐํ์ต๋๋ค. Pages Router๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ค๋น๊ฐ ๋๋ฉด React 19๋ก ์ ๊ทธ๋ ์ด๋ํ ์ ์์ต๋๋ค.
React 19๋ ์์ง RC ๋จ๊ณ์ ์์ง๋ง, ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์์์ ๊ด๋ฒ์ํ ํ ์คํธ์ React ํ๊ณผ์ ๊ธด๋ฐํ ํ๋ ฅ์ ํตํด ์์ ์ฑ์ ํ์ ํ๊ฒ ๋์์ต๋๋ค. ์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ์ ์ถฉ๋ถํ ๊ฒ์ฆ๋์ด ๊ธฐ์กด App Router ์ฌ์ฉ์์๊ฒ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. ์ด์ ๋ฐ๋ผ Next.js 15๋ฅผ ์์ ๋ฒ์ ์ผ๋ก ์ถ์ํ์ฌ, ํ๋ก์ ํธ๊ฐ React 19 GA์ ๋๋นํ ์ ์๋๋ก ์ค๋นํ์ต๋๋ค.
์ํํ ์ ํ์ ์ํด, ๋ง์ด๊ทธ๋ ์ด์ ์ ๋๋ codemod์ ์๋ํ ๋๊ตฌ๋ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
์์ธํ ๋ด์ฉ์ Next.js 15 ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋ (opens in a new tab), React 19 ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋ (opens in a new tab), ๊ทธ๋ฆฌ๊ณ React Conf Keynote (opens in a new tab)๋ฅผ ํตํด ํ์ธํด ๋ณด์ธ์.
Pages Router์ React 18
Next.js 15๋ Pages Router์ ๋ํด React 18์ ํ์ ํธํ์ฑ์ ์ ์งํ์ฌ, ์ฌ์ฉ์๊ฐ Next.js 15์ ๊ฐ์ ์ฌํญ์ ํ์ฉํ๋ฉด์๋ React 18์ ๊ณ์ ์ฌ์ฉํ ์ ์๋๋ก ํฉ๋๋ค.
RC1 ์ดํ ์ปค๋ฎค๋ํฐ ํผ๋๋ฐฑ์ ๋ฐ์ํ์ฌ React 18 ์ง์์ ํฌํจํ๋๋ก ์ ๋ฐ์ดํธํ์ผ๋ฉฐ, ์ด๋ฅผ ํตํด Pages Router์ ํจ๊ป React 18์ ์ฌ์ฉํ๋ฉด์ Next.js 15๋ก ์ ๊ทธ๋ ์ด๋ํ ์ ์๋ ์ ์ฐ์ฑ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ก์จ ์ ๊ทธ๋ ์ด๋ ๊ฒฝ๋ก์ ๋ํ ๋ ํฐ ์ ํ๊ถ์ ๊ฐ๊ฒ ๋ฉ๋๋ค.
์ฐธ๊ณ : ํ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์์ Pages Router๋ฅผ React 18๋ก, App Router๋ฅผ React 19๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ๋ฅํ์ง๋ง, ์ด๋ฌํ ์ค์ ์ ๊ถ์ฅ๋์ง ์์ต๋๋ค. ์ด๋ฌํ ํผํฉ ์ฌ์ฉ์ ์๊ธฐ์น ์์ ๋์์ด๋ ํ์ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ๋ ๋ฒ์ ๊ฐ์ API ๋ฐ ๋ ๋๋ง ๋ก์ง์ด ์์ ํ ์ผ์นํ์ง ์์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
React Compiler๋ Meta์ React ํ์ด ๊ฐ๋ฐํ ์๋ก์ด ์คํ์ ์ปดํ์ผ๋ฌ๋ก, JavaScript์ ์๋ฏธ์ React์ ๊ท์น์ ๊น์ด ์ดํดํ์ฌ ์ฝ๋๋ฅผ ์๋์ผ๋ก ์ต์ ํํฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๊ฐ useMemo๋ useCallback๊ณผ ๊ฐ์ API๋ฅผ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ๋ฉ๋ชจ์ด์ ์ด์ ํด์ผ ํ๋ ์์ ์ ์ค์ฌ, ์ฝ๋๋ฅผ ๋ ๊ฐ๋จํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
Next.js 15์์๋ React Compiler์ ๋ํ ์ง์์ด ์ถ๊ฐ๋์์ต๋๋ค. ์์ธํ ๋ด์ฉ๊ณผ Next.js์ ๊ตฌ์ฑ ์ต์ ์ Next.js ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
์ฃผ์: ํ์ฌ React Compiler๋ Babel ํ๋ฌ๊ทธ์ธ์ผ๋ก๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ, ์ด๋ก ์ธํด ๊ฐ๋ฐ ๋ฐ ๋น๋ ์๊ฐ์ด ๋๋ ค์ง ์ ์์ต๋๋ค.
React Compiler (์คํ์ )
React Compiler (opens in a new tab)๋ Meta์ React ํ์ด ๊ฐ๋ฐํ ์๋ก์ด ์คํ์ ์ปดํ์ผ๋ฌ๋ก, JavaScript์ ์๋ฏธ์ React ๊ท์น (opens in a new tab)์ ๊น์ด ์ดํดํ์ฌ ์ฝ๋๋ฅผ ์๋์ผ๋ก ์ต์ ํํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ useMemo์ useCallback ๊ฐ์ API๋ฅผ ์ด์ฉํด ์๋์ผ๋ก ๋ฉ๋ชจ์ด์ ์ด์ ์ ์ค์ ํด์ผ ํ๋ ์์ ์ ์ค์ผ ์ ์์ผ๋ฉฐ, ์ฝ๋๊ฐ ๋ ๊ฐ๋จํ๊ณ ์ ์ง ๊ด๋ฆฌํ๊ธฐ ์ฌ์์ ธ ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ์ฑ๋ ๋ฎ์์ง๋๋ค.
Next.js 15์์๋ React Compiler์ ๋ํ ์ง์์ด ์ถ๊ฐ๋์์ต๋๋ค. React Compiler์ Next.js์์ ์ ๊ณตํ๋ ๊ตฌ์ฑ ์ต์ ์ ๋ํด ์์ธํ ์์๋ณด์ธ์ (opens in a new tab).
์ฐธ๊ณ : React Compiler๋ ํ์ฌ Babel ํ๋ฌ๊ทธ์ธ์ผ๋ก๋ง ์ ๊ณต๋๋ฏ๋ก, ๊ฐ๋ฐ ๋ฐ ๋น๋ ์๋๊ฐ ๋๋ ค์ง ์ ์์ต๋๋ค.
ํ์ด๋๋ ์ด์ ์ค๋ฅ ๊ฐ์
Next.js 14.1์์๋ ์ค๋ฅ ๋ฉ์์ง์ ํ์ด๋๋ ์ด์ ์ค๋ฅ๋ฅผ ๊ฐ์ ํ์ผ๋ฉฐ, Next.js 15์์๋ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ ํฅ์๋ ํ์ด๋๋ ์ด์ ์ค๋ฅ ํ๋ฉด์ ์ถ๊ฐํ์ต๋๋ค. ์ด์ ํ์ด๋๋ ์ด์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด, ์ค๋ฅ๊ฐ ๋ฐ์ํ ์์ค ์ฝ๋์ ํจ๊ป ๋ฌธ์ ํด๊ฒฐ์ ์ํ ๊ถ์ฅ ์ฌํญ์ด ํ์๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด, Next.js 14.1์์์ ํ์ด๋๋ ์ด์ ์ค๋ฅ ๋ฉ์์ง๋ ๋ค์๊ณผ ๊ฐ์์ต๋๋ค.
Next.js 15์์๋ ์ด๋ฅผ ๊ฐ์ ํ์ฌ ๋ค์๊ณผ ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ ๊ณตํฉ๋๋ค.
Turbopack Dev
next dev --turbo ๋ช ๋ น์ด๊ฐ ์ด์ ์์ ํ๋์ด ๊ฐ๋ฐ ๊ฒฝํ์ ํ์ธต ๋น ๋ฅด๊ฒ ๊ฐ์ ํ ์ ์๊ฒ ๋์์ต๋๋ค. ์ ํฌ๋ ์ด ๊ธฐ๋ฅ์ vercel.com, nextjs.org, v0 (opens in a new tab)๋ฅผ ๋น๋กฏํ ์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ ์ฉํ๋ฉฐ ์ข์ ์ฑ๋ฅ ํฅ์์ ํ์ธํ์ต๋๋ค.
์๋ฅผ ๋ค์ด, ๋๊ท๋ชจ Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ธ vercel.com์์๋ ๋ค์๊ณผ ๊ฐ์ ์ฑ๋ฅ ๊ฐ์ ์ด ์์์ต๋๋ค:
- ๋ก์ปฌ ์๋ฒ ์์ ์๋ ์ต๋ 76.7% ํฅ์
- Fast Refresh๋ฅผ ํตํ ์ฝ๋ ์ ๋ฐ์ดํธ ์๋ ์ต๋ 96.3% ํฅ์
- ์บ์ฑ ์์ด ์ด๊ธฐ ๋ผ์ฐํธ ์ปดํ์ผ ์๋ ์ต๋ 45.8% ํฅ์ (ํ์ฌ Turbopack์ ๋์คํฌ ์บ์ฑ์ ์ง์ํ์ง ์์)
Turbopack Dev์ ๋ํ ์์ธํ ๋ด์ฉ์ ์๋ก์ด ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ์์ ํ์ธํ ์ ์์ต๋๋ค. ๋ธ๋ก๊ทธ ๊ธ (opens in a new tab)
์ ์ ๋ผ์ฐํธ ํ์๊ธฐ
Next.js๋ ์ด์ ๊ฐ๋ฐ ์ค์ ์ ์ ๋ผ์ฐํธ ํ์๊ธฐ๋ฅผ ์ ๊ณตํ์ฌ, ์ด๋ค ๋ผ์ฐํธ๊ฐ ์ ์ ์ธ์ง ๋๋ ๋์ ์ธ์ง ์ฝ๊ฒ ์๋ณํ ์ ์์ต๋๋ค. ์ด๋ฌํ ์๊ฐ์ ์๋ด๋ฅผ ํตํด ํ์ด์ง๊ฐ ์ด๋ป๊ฒ ๋ ๋๋ง๋๋์ง ์ดํดํ์ฌ ์ฑ๋ฅ ์ต์ ํ์ ๋์์ด ๋ฉ๋๋ค.
๋น๋ ๊ฒฐ๊ณผ๋ฌผ์์ ๋ชจ๋ ๋ผ์ฐํธ์ ๋ ๋๋ง ์ ๋ต์ next build (opens in a new tab)๋ฅผ ํตํด ํ์ธํ ์๋ ์์ต๋๋ค.
์ด๋ฒ ์ ๋ฐ์ดํธ๋ Next.js์ ๊ฐ์์ฑ์ ๋์ด๊ธฐ ์ํ ์ง์์ ์ธ ๋ ธ๋ ฅ์ ์ผํ์ผ๋ก, ๊ฐ๋ฐ์๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ํฐ๋งํ๊ณ ๋๋ฒ๊น ํ๋ฉฐ ์ต์ ํํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋๋ค. ์ ์ฉ ๊ฐ๋ฐ ๋๊ตฌ๋ ์ค๋น ์ค์ด๋ฉฐ, ์์ธํ ๋ด์ฉ์ ๊ณง ๊ณต๊ฐ๋ ์์ ์ ๋๋ค.
์ ์ ๋ผ์ฐํธ ํ์๊ธฐ (opens in a new tab)์ ๋ํด ๋ ์์๋ณด์ธ์. ํ์์ ๋นํ์ฑํํ ์๋ ์์ต๋๋ค.
unstable_after๋ฅผ ์ฌ์ฉํ ์๋ต ํ ์ฝ๋ ์คํ (์คํ์ )
์ฌ์ฉ์ ์์ฒญ์ ์ฒ๋ฆฌํ ๋ ์๋ฒ๋ ์ผ๋ฐ์ ์ผ๋ก ์๋ต์ ๊ณ์ฐํ๋ ๋ฐ ์ง์ ๊ด๋ จ๋ ์์ ์ ์ํํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ก๊ทธ ๊ธฐ๋ก, ๋ถ์, ์ธ๋ถ ์์คํ ๋๊ธฐํ์ ๊ฐ์ ์์ ์ ์ฒ๋ฆฌํด์ผ ํ ์๋ ์์ต๋๋ค.
์ด๋ฌํ ์์ ์ ์๋ต๊ณผ ์ง์ ์ ์ผ๋ก ๊ด๋ จ์ด ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์๊ฐ ์๋ฃ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆด ํ์๊ฐ ์์ต๋๋ค. ์๋ต ์ดํ์ ์์ ์ ์ง์ฐํ๋ ค๊ณ ํด๋ ์๋ฒ๋ฆฌ์ค ํจ์๋ ์๋ต์ด ๋ซํ ํ ์ฆ์ ์ฒ๋ฆฌ๋ฅผ ์ค์งํ๋ฏ๋ก ์ด๋ ค์์ด ๋ฐ์ํฉ๋๋ค.
after()๋ ์๋ต์ด ์คํธ๋ฆฌ๋ฐ์ ๋ง์น ํ ์์ ์ ์์ฝํ ์ ์๋๋ก ํด์ฃผ๋ ์๋ก์ด ์คํ์ API๋ก, ๋ณด์กฐ ์์ ์ด ์ฃผ์ ์๋ต์ ๋ฐฉํดํ์ง ์๊ณ ์คํ๋ ์ ์๋๋ก ํฉ๋๋ค.
์ฌ์ฉํ๋ ค๋ฉด next.config.js์ experimental.after๋ฅผ ์ถ๊ฐํ์ธ์.
// next.config.ts
const nextConfig = {
experimental: {
after: true,
},
};
export default nextConfig;
๊ทธ๋ฐ ๋ค์, ์๋ฒ ์ปดํฌ๋ํธ, ์๋ฒ ์ก์ , ๋ผ์ฐํธ ํธ๋ค๋ฌ ๋๋ ๋ฏธ๋ค์จ์ด์์ ์ด ํจ์๋ฅผ importํฉ๋๋ค.
import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
export default function Layout({ children }) {
// ๋ณด์กฐ ์์
after(() => {
log();
});
// ์ฃผ์ ์์
return <>{children}</>;
}
unstable_after์ ๋ํ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ (opens in a new tab)๋ฅผ ์ฐธ์กฐํ์ธ์.
instrumentation.js (์์ ๋ฒ์ )
instrumentation.js ํ์ผ๊ณผ register() API๋ฅผ ํตํด ์ฌ์ฉ์๋ค์ Next.js ์๋ฒ์ ๋ผ์ดํ์ฌ์ดํด์ ์ ๊ทผํ์ฌ ์ฑ๋ฅ์ ๋ชจ๋ํฐ๋งํ๊ณ , ์ค๋ฅ์ ๊ทผ์์ ์ถ์ ํ๋ฉฐ OpenTelemetry์ ๊ฐ์ ๊ฐ์์ฑ(Observability) ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊น์ด ํตํฉํ ์ ์์ต๋๋ค.
์ด ๊ธฐ๋ฅ์ ์ด์ ์์ ํ๋์์ผ๋ฉฐ, experimental.instrumentationHook ๊ตฌ์ฑ ์ต์ ์ ์ ๊ฑฐํด๋ ๋ฉ๋๋ค.
๋ํ Sentry์ ํ๋ ฅํ์ฌ ์๋ก์ด onRequestError ํ ์ ์ค๊ณํ์ผ๋ฉฐ, ์ด๋ฅผ ํตํด ๋ค์์ ์ํํ ์ ์์ต๋๋ค.
- ์๋ฒ์์ ๋ฐ์ํ๋ ๋ชจ๋ ์ค๋ฅ์ ๋ํ ์ค์ํ ์ปจํ
์คํธ๋ฅผ ์์งํฉ๋๋ค. ์์ง ํญ๋ชฉ์๋ ๋ค์์ด ํฌํจ๋ฉ๋๋ค.
- ๋ผ์ฐํฐ: Pages Router ๋๋ App Router
- ์๋ฒ ์ปจํ ์คํธ: ์๋ฒ ์ปดํฌ๋ํธ, ์๋ฒ ์ก์ , ๋ผ์ฐํธ ํธ๋ค๋ฌ ๋๋ ๋ฏธ๋ค์จ์ด
- ์์งํ ์ค๋ฅ ์ ๋ณด๋ฅผ ์ ํธํ๋ ๊ฐ์์ฑ ์ ๊ณต์์ ๋ณด๊ณ ํ ์ ์์ต๋๋ค.
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() {
// ์ ํธํ๋ ๊ฐ์์ฑ ์ ๊ณต์ SDK ์ค์
}
onRequestError ํจ์์ ๋ํ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ (opens in a new tab)์์ ํ์ธํ์ธ์.
<Form>
์ปดํฌ๋ํธ
์๋ก์ด <Form>
์ปดํฌ๋ํธ๋ HTML <form>
์์๋ฅผ ํ์ฅํ์ฌ ํ๋ฆฌํ์น, ํด๋ผ์ด์ธํธ ์ธก ํ์, ์ ์ง์ ํฅ์์ ์ ๊ณตํฉ๋๋ค.
์ด ์ปดํฌ๋ํธ๋ ๊ฒฐ๊ณผ ํ์ด์ง๋ก ์ด๋ํ๋ ๊ฒ์ ํผ๊ณผ ๊ฐ์ด ์๋ก์ด ํ์ด์ง๋ก ์ด๋ํ๋ ํผ์ ์ ์ฉํฉ๋๋ค.
// app/page.jsx
import Form from 'next/form';
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
);
}
<Form>
์ปดํฌ๋ํธ์ ์ฃผ์ ๊ธฐ๋ฅ.
- ํ๋ฆฌํ์น: ํผ์ด ๋ณด์ด๋ฉด ๋ ์ด์์ (opens in a new tab)๊ณผ ๋ก๋ฉ (opens in a new tab) UI๊ฐ ๋ฏธ๋ฆฌ ๋ก๋๋์ด ๋น ๋ฅธ ํ์์ ์ง์ํฉ๋๋ค.
- ํด๋ผ์ด์ธํธ ์ธก ํ์: ํผ ์ ์ถ ์, ๊ณต์ ๋ ์ด์์๊ณผ ํด๋ผ์ด์ธํธ ์ํ๊ฐ ์ ์ง๋ฉ๋๋ค.
- ์ ์ง์ ํฅ์: JavaScript๊ฐ ๋ก๋๋์ง ์์ ๊ฒฝ์ฐ์๋ ํผ์ ์ ์ฒด ํ์ด์ง ํ์์ ํตํด ์ ์ ์๋ํฉ๋๋ค.
์ด์ ์๋ ์ด๋ฌํ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํด ๋ง์ ์์์
์ด ํ์ํ์ง๋ง, ์ด์
<Form>
์ปดํฌ๋ํธ๋ก ๊ฐํธํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
'use client'
import { useEffect } from 'react' import { useRouter } from 'next/navigation'
export default function Form(props) { const action = props.action const router = useRouter()
useEffect(() => { // if form target is a URL, prefetch it if (typeof action === 'string') { router.prefetch(action) } }, [action, router])
function onSubmit(event) { event.preventDefault()
// grab all of the form fields and trigger a `router.push` with the data URL encoded
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>
์ปดํฌ๋ํธ (opens in a new tab)์ ๋ํด์ ๋ ์์๋ณด์ธ์.
next.config.ts ์ง์
Next.js๋ ์ด์ TypeScript ํ์ผ ํ์์ธ next.config.ts๋ฅผ ์ง์ํ๋ฉฐ, ์๋ ์์ฑ๊ณผ ํ์ ์์ ์ฑ์ ์ํ NextConfig ํ์ ์ ์ ๊ณตํฉ๋๋ค.
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* ์ค์ ์ต์
์
๋ ฅ */
};
export default nextConfig;
TypeScript ์ง์์ ๋ํ ์์ธํ ๋ด์ฉ์ Next.js ๊ณต์ ๋ฌธ์ (opens in a new tab)์์ ํ์ธํ์ธ์.
์์ฒด ํธ์คํ ๊ฐ์ ์ฌํญ
์ ํ๋ฆฌ์ผ์ด์
์ ์์ฒด ํธ์คํ
ํ ๋, Cache-Control
์ง์์ ๋ํ ๋ ๋ง์ ์ ์ด๊ฐ ํ์ํ ์ ์์ต๋๋ค.
๋ํ์ ์ธ ๊ฒฝ์ฐ๋ก ISR ํ์ด์ง์ ๋ํด stale-while-revalidate
๊ธฐ๊ฐ์ ์ ์ดํ๋ ์ํฉ์ด ์์ต๋๋ค. ์ด๋ฅผ ์ํด ๋ ๊ฐ์ง ๊ฐ์ ์ฌํญ์ ๊ตฌํํ์ต๋๋ค:
next.config
์์ expireTime
(opens in a new tab) ๊ฐ์ ์ค์ ํ ์ ์์ต๋๋ค. ์ด๋ ์ด์ ์ experimental.swrDelta
์ต์
์ ๋์ฒดํฉ๋๋ค.
๊ธฐ๋ณธ๊ฐ์ด 1๋
์ผ๋ก ์
๋ฐ์ดํธ๋์ด ๋๋ถ๋ถ์ CDN์ด stale-while-revalidate๋ฅผ ์ํ๋ ๋๋ก ์ ์ฉํ ์ ์์ต๋๋ค.
๋ํ, ๊ธฐ๋ณธ Cache-Control
๊ฐ์ ๋ ์ด์ ์ฌ์ฉ์ ์ง์ ๊ฐ์ ๋ฎ์ด์ฐ์ง ์์, CDN ์ค์ ๊ณผ์ ํธํ์ฑ์ ๋ณด์ฅํ๋ฉด์ ์์ ํ ์ ์ด๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก ์ด๋ฏธ์ง ์ต์ ํ ๊ธฐ๋ฅ์ ๊ฐ์ ํ์ต๋๋ค. ์ด์ ์๋ Next.js ์๋ฒ์์ ์ด๋ฏธ์ง๋ฅผ ์ต์ ํํ๊ธฐ ์ํด sharp
์ค์น๋ฅผ ๊ถ์ฅํ์ผ๋, ์ข
์ข
๋๋ฝ๋๋ ๊ฒฝ์ฐ๊ฐ ์์์ต๋๋ค. Next.js 15์์๋ next start
๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ๋
๋ฆฝ ์คํ ๋ชจ๋ (opens in a new tab)์์ ์คํํ ๋ sharp
์ ์๋์ผ๋ก ์ฌ์ฉํ๋ฏ๋ก ์๋ ์ค์น๊ฐ ํ์ํ์ง ์์ต๋๋ค.
์์ธํ ๋ด์ฉ์ Next.js ์์ฒด ํธ์คํ ์ ๋ํ ์๋ก์ด ๋ฐ๋ชจ์ ํํ ๋ฆฌ์ผ ๋น๋์ค (opens in a new tab)์์ ํ์ธํ์ธ์.
์๋ฒ ์ก์ ์ ๋ณด์ ๊ฐํ
์๋ฒ ์ก์ (Server Actions)์ ํด๋ผ์ด์ธํธ์์ ํธ์ถํ ์ ์๋ ์๋ฒ ์ธก ํจ์์ ๋๋ค. ํ์ผ ์๋จ์ 'use server' ์ง์์ด๋ฅผ ์ถ๊ฐํ๊ณ ๋น๋๊ธฐ ํจ์๋ฅผ exportํ์ฌ ์ ์ํ ์ ์์ต๋๋ค.
์๋ฒ ์ก์ ์ด๋ ์ ํธ๋ฆฌํฐ ํจ์๊ฐ ์ฝ๋์์ ๋ค๋ฅธ ๊ณณ์ import๋์ง ์๋๋ผ๋, ์ด๋ ์ฌ์ ํ ๊ณต๊ฐ์ ์ผ๋ก ์ ๊ทผ ๊ฐ๋ฅํ HTTP ์๋ํฌ์ธํธ๊ฐ ๋ ์ ์์ต๋๋ค. ์ด ๋์์ ๊ธฐ์ ์ ์ผ๋ก๋ ์ฌ๋ฐ๋ฅด์ง๋ง, ์๋์น ์๊ฒ ์ด๋ฌํ ํจ์๊ฐ ๋ ธ์ถ๋ ์ํ์ด ์์ต๋๋ค.
๋ณด์์ ๊ฐํํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ ์ฌํญ์ ๋์ ํ์ต๋๋ค.
- ๋ถํ์ํ ์ฝ๋ ์ ๊ฑฐ: ์ฌ์ฉ๋์ง ์๋ ์๋ฒ ์ก์ ์ ํด๋ผ์ด์ธํธ ์ธก ๋ฒ๋ค์์ ID๊ฐ ๋ ธ์ถ๋์ง ์์ผ๋ฉฐ, ์ด๋ก ์ธํด ๋ฒ๋ค ํฌ๊ธฐ๊ฐ ์ค์ด๋ค๊ณ ์ฑ๋ฅ์ด ํฅ์๋ฉ๋๋ค.
- ๋ณด์์ฑ์ด ๊ฐํ๋ ์ก์ ID: Next.js๋ ์ถ์ธก์ด ๋ถ๊ฐ๋ฅํ๊ณ ๋น๊ฒฐ์ ์ ์ธ ID๋ฅผ ์์ฑํ์ฌ, ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ ์ก์ ์ ์ฐธ์กฐํ๊ณ ํธ์ถํ ์ ์๋๋ก ํฉ๋๋ค. ์ด๋ฌํ ID๋ ๋น๋ ๊ฐ์ ์ฃผ๊ธฐ์ ์ผ๋ก ์ฌ๊ณ์ฐ๋์ด ๋ณด์์ ๊ฐํํฉ๋๋ค.
์์ ์ฝ๋.
// app/actions.js
"use server";
// ์ด ์ก์
์ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉ๋๋ฏ๋ก Next.js๊ฐ
// ํด๋ผ์ด์ธํธ์์ ์ฐธ์กฐ ๋ฐ ํธ์ถํ ์ ์๋๋ก ๋ณด์ ID๋ฅผ ์์ฑํฉ๋๋ค.
export async function updateUserAction(formData) {}
// ์ด ์ก์
์ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฉ๋์ง ์์ผ๋ฏ๋ก
// Next.js๊ฐ `next build` ์ค์ ์๋์ผ๋ก ์ด ์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ
// ๊ณต๊ฐ ์๋ํฌ์ธํธ๋ฅผ ์์ฑํ์ง ์์ต๋๋ค.
export async function deleteUserAction(formData) {}
์๋ฒ ์ก์ ์ ์ฌ์ ํ ๊ณต๊ฐ HTTP ์๋ํฌ์ธํธ๋ก ์ทจ๊ธํด์ผ ํฉ๋๋ค. ์๋ฒ ์ก์ ๋ณด์์ ๋ํ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ (opens in a new tab)๋ฅผ ์ฐธ๊ณ ํ์ธ์.
์ธ๋ถ ํจํค์ง ๋ฒ๋ค๋ง ์ต์ ํ (์์ ๋ฒ์ )
์ธ๋ถ ํจํค์ง๋ฅผ ๋ฒ๋ค๋งํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ ์ด๊ธฐ ์์ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค. App Router์์๋ ์ธ๋ถ ํจํค์ง๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฒ๋ค๋ง๋๋ฉฐ, ์๋ก์ด serverExternalPackages
์ค์ ์ต์
์ ์ฌ์ฉํ์ฌ ํน์ ํจํค์ง๋ฅผ ๋ฒ๋ค๋ง์์ ์ ์ธํ ์ ์์ต๋๋ค.
Pages Router์์๋ ์ธ๋ถ ํจํค์ง๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฒ๋ค๋ง๋์ง ์์ง๋ง, ๊ธฐ์กด transpilePackages
์ต์
์ ์ฌ์ฉํ์ฌ ๋ฒ๋ค๋งํ ํจํค์ง ๋ชฉ๋ก์ ์ง์ ํ ์ ์์ต๋๋ค. ์ด ์ต์
์์๋ ๊ฐ ํจํค์ง๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ์ง์ ํด์ผ ํฉ๋๋ค.
App Router์ Pages Router์ ์ค์ ์ ํตํฉํ๊ธฐ ์ํด, Pages Router์์ ์ธ๋ถ ํจํค์ง๋ฅผ ์๋์ผ๋ก ๋ฒ๋ค๋งํ๋ bundlePagesRouterDependencies
์ต์
์ ์ถ๊ฐํ์ต๋๋ค. ์ด๋ฅผ ํตํด App Router์ ๊ธฐ๋ณธ ๋ฒ๋ค๋ง ๋์๊ณผ ์ผ์นํ๊ฒ ์ค์ ํ ์ ์์ผ๋ฉฐ, ํ์ ์ serverExternalPackages
๋ฅผ ์ฌ์ฉํด ํน์ ํจํค์ง๋ฅผ ๋ฒ๋ค๋ง์์ ์ ์ธํ ์ ์์ต๋๋ค.
// next.config.ts
const nextConfig = {
// Pages Router์์ ์ธ๋ถ ํจํค์ง๋ฅผ ์๋์ผ๋ก ๋ฒ๋ค๋ง:
bundlePagesRouterDependencies: true,
// App ๋ฐ Pages Router์์ ํน์ ํจํค์ง๋ฅผ ๋ฒ๋ค๋ง์์ ์ ์ธ:
serverExternalPackages: ['package-name'],
};
export default nextConfig;
์ธ๋ถ ํจํค์ง ์ต์ ํ์ ๋ํ ์์ธํ ๋ด์ฉ์ Next.js ๊ณต์ ๋ฌธ์ (opens in a new tab)์์ ํ์ธํ์ธ์.
Next.js 15์์๋ ESLint 9์ ๋ํ ์ง์์ ๋์ ํ์ฌ, 2024๋ 10์ 5์ผ์ ์ข ๋ฃ๋ ESLint 8์ ์ง์์ ์ด์ด๋ฐ์์ต๋๋ค. ์ด๋ก ์ธํด ๊ฐ๋ฐ์๋ ESLint 8๊ณผ 9 ์ค ์ํ๋ ๋ฒ์ ์ ์ ํํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ESLint 9 ์ง์
ESLint 9 (opens in a new tab)๋ก ์
๊ทธ๋ ์ด๋ํ ๊ฒฝ์ฐ, ์๋ก์ด ๊ตฌ์ฑ ํ์ (opens in a new tab)์ ์์ง ์ฑํํ์ง ์์ ํ๋ก์ ํธ์์๋ Next.js๊ฐ ์๋์ผ๋ก ESLINT_USE_FLAT_CONFIG=false
์ค์ ์ ์ ์ฉํ์ฌ ๋ง์ด๊ทธ๋ ์ด์
์ ์ํํ๊ฒ ์ง์ํฉ๋๋ค.
๋ํ, --ext
๋ฐ --ignore-path
์ ๊ฐ์ ์ฌ์ฉ ์ค๋จ๋ ์ต์
์ next lint ์คํ ์ ์ ๊ฑฐ๋ฉ๋๋ค. ESLint 10์์๋ ์ด๋ฌํ ์ด์ ๊ตฌ์ฑ ์ต์
์ด ์์ ํ ์ง์๋์ง ์์ ์์ ์ด๋ฏ๋ก, ์กฐ์ํ ๋ง์ด๊ทธ๋ ์ด์
์ ๊ถ์ฅํฉ๋๋ค. ๋ง์ด๊ทธ๋ ์ด์
๊ฐ์ด๋ (opens in a new tab)
์ด ์ ๋ฐ์ดํธ์ ์ผํ์ผ๋ก, React Hooks ์ฌ์ฉ์ ๋ํ ์๋ก์ด ๊ท์น์ ๋์ ํ eslint-plugin-react-hooks๋ฅผ v5.0.0์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ์ต๋๋ค. ์์ธํ ๋ณ๊ฒฝ ์ฌํญ์ eslint-plugin-react-hooks@5.0.0์ ๋ณ๊ฒฝ ๋ก๊ทธ์์ ํ์ธ (opens in a new tab)ํ์ค ์ ์์ต๋๋ค.
๊ฐ๋ฐ ๋ฐ ๋น๋ ์ฑ๋ฅ ๊ฐ์
์๋ฒ ์ปดํฌ๋ํธ HMR ๊ฐ๋ฐ ์ค์ ์๋ฒ ์ปดํฌ๋ํธ๋ ์ ์ฅ๋ ๋๋ง๋ค ๋ค์ ์คํ๋ฉ๋๋ค. ์ด๋ API ์๋ํฌ์ธํธ๋ ์๋ํํฐ ์๋น์ค์ ๋ํ fetch ์์ฒญ์ด ๋ฐ๋ณต์ ์ผ๋ก ํธ์ถ๋ ์ ์๋ค๋ ๋ป์ ๋๋ค.
๋ก์ปฌ ๊ฐ๋ฐ ์ฑ๋ฅ์ ๊ฐ์ ํ๊ณ API ์์ฒญ์ ๋ํ ๋น์ฉ์ ์ค์ด๊ธฐ ์ํด, ์ด์ ํซ ๋ชจ๋ ๊ต์ฒด(HMR)๊ฐ ์ด์ ๋ ๋๋ง์์์ fetch ์๋ต์ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํฉ๋๋ค.
์์ธํ ๋ด์ฉ์ ์๋ฒ ์ปดํฌ๋ํธ HMR ์บ์ (opens in a new tab)๋ฅผ ์ฐธ๊ณ ํ์ธ์.
App Router๋ฅผ ์ํ ๋ ๋น ๋ฅธ ์ ์ ์์ฑ
์ ์ ์์ฑ ๊ณผ์ ์ ์ต์ ํํ์ฌ ํนํ ๋คํธ์ํฌ ์์ฒญ์ด ๋๋ฆฐ ํ์ด์ง์ ๋น๋ ์๊ฐ์ ๊ฐ์ ํ์ต๋๋ค.
์ด์ ์๋ ํด๋ผ์ด์ธํธ ์ธก ํ์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ธฐ ์ํด ํ ๋ฒ, ์ด๊ธฐ ํ์ด์ง ๋ฐฉ๋ฌธ์ ์ํ HTML ๋ ๋๋ง์ ์ํด ๋ค์ ํ ๋ฒ ํ์ด์ง๋ฅผ ๋ ๋๋งํ๋ ๋ฐฉ์์ด์์ต๋๋ค. ์ด์ ์ฒซ ๋ฒ์งธ ๋ ๋๋ง์ ์ฌ์ฌ์ฉํ์ฌ ๋ ๋ฒ์งธ ๋ ๋๋ง์ ์๋ตํจ์ผ๋ก์จ ์์ ๋๊ณผ ๋น๋ ์๊ฐ์ ์ค์์ต๋๋ค.
์ถ๊ฐ๋ก, ์ ์ ์์ฑ ์์ ์๋ค์ด ์ฌ๋ฌ ํ์ด์ง์์ fetch ์บ์๋ฅผ ๊ณต์ ํฉ๋๋ค. ์บ์์์ ์ ์ธ๋์ง ์์ fetch ํธ์ถ์ ๊ฒฐ๊ณผ๋ ๋์ผํ ์์ ์๊ฐ ์ฒ๋ฆฌํ๋ ๋ค๋ฅธ ํ์ด์ง์์๋ ์ฌ์ฌ์ฉ๋์ด, ๋์ผํ ๋ฐ์ดํฐ ์์ฒญ ํ์๊ฐ ์ค์ด๋ญ๋๋ค.
๊ณ ๊ธ ์ ์ ์์ฑ ์ ์ด (์คํ์ )
๊ณ ๊ธ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ํด ์ ์ ์์ฑ ํ๋ก์ธ์ค๋ฅผ ๋ ์ธ๋ฐํ๊ฒ ์ ์ดํ ์ ์๋ ์คํ์ ์ง์ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์์ต๋๋ค. ํ์ง๋ง ํน๋ณํ ์๊ตฌ ์ฌํญ์ด ์๋ค๋ฉด ํ์ฌ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ด๋ฌํ ์ต์ ์ ๋ฆฌ์์ค ์ฌ์ฉ๋ ์ฆ๊ฐ์ ๋์์ฑ ์ฆ๊ฐ๋ก ์ธํด ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
// next.config.ts
const nextConfig = {
experimental: {
// ํ์ด์ง ์์ฑ ์คํจ ์, ๋น๋๊ฐ ์คํจํ๊ธฐ ์ ์ฌ์๋ ํ์
staticGenerationRetryCount: 1,
// ์์
์๋น ์ฒ๋ฆฌํ ํ์ด์ง ์
staticGenerationMaxConcurrency: 8,
// ์๋ก์ด ์์
์ ์ค๋ ๋๋ฅผ ์์ํ๊ธฐ ์ํ ์ต์ ํ์ด์ง ์
staticGenerationMinPagesPerWorker: 25,
},
};
export default nextConfig;
์ ์ ์์ฑ ์ต์ ์ ๋ํ ์์ธํ ๋ด์ฉ์ Next.js ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.