โท Next.js 16 ์ ๋ฐ์ดํธ ์ ๋ฆฌ
ย
Next 16๋ถํฐ Turbopack์ด ๊ธฐ๋ณธ ๋ฒ๋ค๋ฌ๋ก ์ค์ ๋์ด ์์ผ๋ฉฐ,
๊ฐ๋ฐ(dev)๊ณผ ๋น๋(build) ํ๊ฒฝ ๋ชจ๋์์ Turbopack์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉ๋๋ค.
ย
Next 15์์๋ Turbopack์ด ๊ธฐ๋ณธ๊ฐ์ด ์๋์์ผ๋ฉฐ, ์ฌ์ฉ์๊ฐ ์ ํ์ ์ผ๋ก ํ์ฑํํด์ผ ํ๋ค.
๋ง์ฝ Next 16์์ Webpack์ ๊ณ์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด --webpack ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค. (๊ณต์ ์ตํธ์์ ์ต์
)
next build --webpack
ย
ย
๐ React Compiler ์ง์
ย
useMemo๋ useCallback์ ์ง์ ์ฌ์ฉํ์ง ์์๋ ์ปดํ์ผ๋ฌ๊ฐ ์์์ ์ต์ ํ ํด์ค๋ค.ย
# ์ค์น pnpm add babel-plugin-react-compiler
/* next.config.mjs */ const nextConfig = { reactCompiler: true, }; export default nextConfig;
ย
ย
๐ Renaming Middleware to Proxy
ย
ย
Next.js๋
middleware.ts๋ฅผ proxy.ts๋ก ๋์ฒดํ๋ ๋ฐฉํฅ์ผ๋ก ์
๋ฐ์ดํธ๋๊ณ ์์ผ๋ฉฐ,์๋ codemod ๋ช
๋ น์ด๋ก ํ์ผ๋ช
๊ณผ ํจ์๋ช
์ ํ ๋ฒ์ ๋ณ๊ฒฝํ ์ ์๋ค.
ย
npx @next/codemod@canary middleware-to-proxy .
ย
์คํ ํ์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ค:
// middleware.ts โ proxy.ts - export function middleware() { + export function proxy() { ... }
ย
ย
๐ revalidateTag
ย
ย
Next 16์์๋
revalidateTag๊ฐ SWR ๋ฐฉ์์ผ๋ก ๋์ํ๋๋ก ๋ณ๊ฒฝ๋์๋ค.ย
SWR ๋ฐฉ์์ stale-while-revalidate ์ ๋ต์ผ๋ก, ์บ์๊ฐ ๋ง๋ฃ๋๋๋ผ๋ ์ฌ์ฉ์์๊ฒ๋ ๊ธฐ์กด(stale) ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ๋ณด์ฌ์ฃผ๊ณ , ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ (fetch)ํ๋ ๋ฐฉ์์ด๋ค. ์ฆ, ์บ์๊ฐ ๋ง๋ฃ๋์ด๋ ์ฌ์ฉ์๋ ๋ก๋ฉ ํ๋ฉด ์์ด ์ต์ ๋ฐ์ดํฐ๋ฅผ ํ์ธํ ์ ์๋ค.
ย
profile ์ธ์๋ SWR ๋์ ๋ฐฉ์์ ์ธ๋ถ์ ์ผ๋ก ์กฐ์ ํ๊ธฐ ์ํ ์ถ๊ฐ ์ต์
์ด๋ค.ย
revalidateTag(tag, profile)
ย
fetch ์ ํ๊ทธ ์ง์ :
fetch('/api/data', { next: { tags: ['posts'] } })
ย
์ฌ๊ฒ์ฆ ํธ์ถ:
// ์บ์๋ฅผ ์ต๋ ์ฑ๋ฅ ๊ธฐ์ค์ผ๋ก ์ฌ๊ฒ์ฆ (stale-while-revalidate ๊ธฐ๋ณธ ์ ๋ต) // โ ์ฆ์ ๊ธฐ์กด ์บ์ ๋ณด์ฌ์ฃผ๊ณ , ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ต์ ๋ฐ์ดํฐ ๊ฐฑ์ revalidateTag('posts', 'max') // 1์๊ฐ๋ง๋ค ๋ฐ์ดํฐ ์๋ก๊ณ ์นจ revalidateTag('news-feed', 'hours') // ํ๋ฃจ๋ง๋ค ๋ฐ์ดํฐ ์๋ก๊ณ ์นจ revalidateTag('analytics', 'days') // ์บ์ ๋ง๋ฃ ์๊ฐ์ 60์ด๋ก ์ง์ (60์ด ํ revalidate) // โ 60์ด ์ง๋๋ฉด ๋ค์ ์์ฒญ ์ ์๋์ผ๋ก ์ต์ ๋ฐ์ดํฐ๋ก ๊ฐฑ์ revalidateTag('posts', { expire: 60 })
ย
ย
๐ updateTag
ย
ย
Next 16์์
updateTag๊ฐ ๋์
๋ ์ด์ ๋, revalidateTag๊ฐ SWR(๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์ ) ๋ฐฉ์์ผ๋ก ๋์ํด ์ฆ์ ๋ฐ์์ด ์ด๋ ค์ ๊ธฐ ๋๋ฌธ์ด๋ค.ย
revalidateTag โ ๋๊ธํ ๋ฐฑ๊ทธ๋ผ์ด๋ ์
๋ฐ์ดํธ์ฉ
updateTag โ ์ฆ์ ์บ์ ๋ฌดํจํย
updateTag๋ ์๋ฒ ์ก์
(Server Actions) ๋ด์์๋ง ์ฌ์ฉํ ์ ์์ผ๋ฉฐ,์ง์ ๋ ์บ์ ํ๊ทธ(
tag)๊ฐ ํฌํจ๋ ๋ฐ์ดํฐ์ ์บ์๋ฅผ ์ฆ์ ๋ง๋ฃ(expire) ์ํจ๋ค.ย
์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ ์งํ, ๊ทธ ๋ณ๊ฒฝ ์ฌํญ์ด UI๋ ๋ค์ ์์ฒญ์ ๋ฐ๋ก ๋ฐ์๋์ด์ผ ํ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋๋ค.
ย
'use server'; import { updateTag } from 'next/cache'; // ์ฌ์ฉ์๊ฐ ๊ฒ์๊ธ์ ์์ฑํ ๋ค ๊ทธ ๊ฒ์๋ฌผ ๊ด๋ จ ์บ์๋ฅผ ์ฆ์ ๋ฌดํจํ export async function createPost(formData: FormData) { const post = await db.post.create({ /* โฆ */ }); updateTag('posts'); // 'posts' ํ๊ทธ๊ฐ ๋ถ์ ๋ชฉ๋ก ์บ์์ updateTag(`post-${post.id}`); // 'post-{id}' ํ๊ทธ๊ฐ ๋ถ์ ๊ฐ๋ณ ๊ฒ์๊ธ ์บ์๋ฅผ ์ฆ์ ๋ง๋ฃ }
ย
ย
๐ refresh
ย
refresh๋ ์๋ฒ ์ก์
(Server Actions) ๋ด์์๋ง ์ฌ์ฉํ ์ ์๋ค.ย
์ด ํจ์๋ ํ์ฌ ํ์ด์ง์ ๋ผ์ฐํฐ(ํ๋ฉด ์ํ)๋ฅผ ์๋ก๊ณ ์นจํ๋ ์ญํ ์ ํ๋ค.
ย
์ฌ๊ธฐ์ ๋งํ๋ โ์๋ก๊ณ ์นจ(refresh)โ์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ fetchํ๋ ๊ฒ์ด ์๋๋ผ,
ํ์ฌ ๋ณด๊ณ ์๋ ํ์ด์ง ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
๋ฐ๋ผ์ ๋ฐ์ดํฐ ์บ์๋ ๊ทธ๋๋ก ์ ์ง๋๋ค.
ย
- ๋จ์ํ ํ๋ฉด๋ง ๋ค์ ๋ ๋๋งํ๊ณ ์ถ๋ค๋ฉด โ
refresh()
- ์ค์ ๋ฐ์ดํฐ ์บ์๋ฅผ ๋ฌดํจํ(๋๋ ๊ฐฑ์ )ํ๊ณ ์ถ๋ค๋ฉด โ
updateTag()๋๋revalidateTag()
ย
'use server'; import { refresh } from 'next/cache'; export async function createPost(data) { await db.createPost(data); refresh(); // ํด๋ผ์ด์ธํธ ๋ผ์ฐํฐ ์ํ๋ฅผ ์๋ก๊ณ ์นจ (์บ์๋ ๊ทธ๋๋ก) }
ย
ย
๐ SVG
ย
Next 16์์๋ Turbopack์ด ๊ฐ๋ฐยท๋น๋ ๊ธฐ๋ณธ ๋ฒ๋ค๋ฌ๊ฐ ๋๋ฉด์,
Turbopack ์ค์ ๋ง์ผ๋ก SVG(SVGR) ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
ย
๋ํ ๊ธฐ์กด
experimental.turbo ์ต์
์ ์ ์ ๋ช
์นญ์ธ turbopack์ผ๋ก ๋ณ๊ฒฝ๋์๋ค.ย
# ์ค์น pnpm add @svgr/webpack
ย
next.config.mjs
const nextConfig = { turbopack: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js', }, }, }, }
ย
ย
๐ Build Adapter API
ย