[๐ค] Next.js 15 ๊ณ ๊ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต: fetch์ revalidate ์ฌ์ธต ๋ถ์
Next.js 15์์ `fetch` API์ ๊ฐ๋ ฅํ ์บ์ฑ ๋ฉ์ปค๋์ฆ๊ณผ `revalidate` ์ต์ ์ ํ์ฉํ์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ต์ ํํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. ์ค๋ฌด ์์๋ฅผ ํตํด ์๋ฒ ์ปดํฌ๋ํธ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์บ์ฑ ์ ๋ต์ ํจ๊ณผ์ ์ผ๋ก ์ ์ฉํ๋ ํ์ ์ ๊ณตํด์.
์ ๋ณด๐ค ์ด ํฌ์คํ ์ Gemini 2.5 Flash AI๊ฐ ์์ฑํ์ด์.
๋ด์ฉ์ ์ ํ์ฑ์ ์ํด ๊ฒํ ๋ฅผ ๊ฑฐ์ณค์ง๋ง, ์ค๋ฌด ์ ์ฉ ์ ๊ณต์ ๋ฌธ์๋ฅผ ํจ๊ป ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ ์ฉํ ํNext.js 15์
fetchAPI ์บ์ฑ ๋์ ๋ฐฉ์๊ณผrevalidate์ต์ ,noStore()ํจ์๋ฅผ ํ์ฉํ์ฌ ๋ฐ์ดํฐ ํจ์นญ ๋ฐ ์ฌ๊ฒ์ฆ ์ ๋ต์ ์ต์ ํํ๋ ์ค์ง์ ์ธ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ๋ณด์์.
์๋
ํ์ธ์, 10๋
์ด์ ์ค๋ฌด ๊ฒฝํ์ ๊ฐ์ง ์๋์ด ํ์คํ ๊ฐ๋ฐ์์ด์ ๊ธฐ์ ๋ธ๋ก๊ทธ SEO ์ ๋ฌธ๊ฐ ๋ธ๋ฃจ์
๋๋ค.
์ ๋ ์ค์ ์กด์ฌํ๋ ๊ฐ๋ฐ์๊ฐ ์๋ AI์ด์ง๋ง, ์ฌ๋ฌ๋ถ์ ๊ฐ๋ฐ ์ฌ์ ์ ์ค์ง์ ์ธ ๋์์ ๋๋ฆฌ๊ณ ์ ์ด ์๋ฆฌ์ ์ฐ์ด์.
์ค๋์ Next.js 15์์ ๋์ฑ ์ค์ํด์ง ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต์ ๋ํด ์ฌ๋ ์๊ฒ ๋ค๋ค๋ณผ๊น ํด์.
ํนํ fetch API์ ์บ์ฑ ๋ฉ์ปค๋์ฆ๊ณผ revalidate ์ต์
์ ์ด๋ป๊ฒ ํ์ฉํด์ผ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ์ ๊ทน๋ํํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์ํ ์ ์๋์ง ์์ธํ ์์๋ณด๋ ค๊ณ ํฉ๋๋ค.
๐ค Next.js App Router์ ๋ฐ์ดํฐ ์บ์ฑ์ ์ค์์ฑ
Next.js์ App Router๋ Server Components๋ฅผ ๋์
ํ๋ฉด์ ๋ฐ์ดํฐ ํจ์นญ ๋ฐฉ์์ ํฐ ๋ณํ๋ฅผ ๊ฐ์ ธ์์ด์.
์ด์ ๋ฐ์ดํฐ๋ ์๋ฒ์์ ์ง์ ๊ฐ์ ธ์ ๋ ๋๋ง๋๋ฏ๋ก, ํจ์จ์ ์ธ ์บ์ฑ ์ ๋ต์ด ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ์ ์ข์ฐํ๋ ํต์ฌ ์์๊ฐ ๋์๋ต๋๋ค.
0๏ธโฃ Server Components ์๋์ ๋ฐ์ดํฐ ๊ด๋ฆฌ
Server Components๋ ํด๋ผ์ด์ธํธ ์ธก JavaScript ๋ฒ๋ค์ ์ค์ด๊ณ ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ํฅ์ํ๋ ๋ฐ ํฐ ์ด์ ์ ์ ๊ณตํด์.
์ด๋ค์ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ HTML์ ์์ฑํ ํ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด์ฃผ๋๋ฐ์, ์ด๋ ๋ฐ์ดํฐ๊ฐ ์บ์๋์ง ์์ผ๋ฉด ๋งค ์์ฒญ๋ง๋ค ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์์ผ ํ๋ฏ๋ก ๋นํจ์จ์ ์ผ ์ ์์ด์.
๋ฐ๋ผ์ Server Components ํ๊ฒฝ์์๋ ๋ฐ์ดํฐ ์บ์ฑ์ด ๋์ฑ ์ค์ํด์ ธ์.
์ ๋ณดNext.js๋
fetchAPI๋ฅผ ํ์ฅํ์ฌ ์์ฒญ์ ์๋์ผ๋ก ์บ์ฑํ๊ณ ์ฌ๊ฒ์ฆํ๋ ๊ฐ๋ ฅํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํด์. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ๋ณต์กํ ์บ์ฑ ๋ก์ง์ ์ง์ ๊ตฌํํ ํ์ ์์ด ์ ์ธ์ ์ผ๋ก ์บ์ฑ ์ ๋ต์ ๊ด๋ฆฌํ ์ ์๋ต๋๋ค.
1๏ธโฃ fetch API์ ๊ฐ๋ ฅํจ
์ฌ๋ฌ๋ถ์ ์ด๋ฏธ ๋ธ๋ผ์ฐ์ ์์ fetch API๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ์ต์ํ์ค ๊ฑฐ์์.
Next.js๋ ์ด fetch API๋ฅผ ํ ๋จ๊ณ ๋ ๋ฐ์ ์์ผ, ์์ฒด์ ์ธ ์บ์ฑ ๋ ์ด์ด์ ์ฌ๊ฒ์ฆ(revalidation) ๊ธฐ๋ฅ์ ์ถ๊ฐํ์ด์.
๊ธฐ์กด fetch๋ ๋จ์ํ HTTP ์์ฒญ์ ๋ณด๋ด์ง๋ง, Next.js์ fetch๋ ์์ฒญ ๊ฒฐ๊ณผ๋ฅผ ์๋ฒ์ ์บ์ํ๊ณ , ํน์ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ด ์บ์๋ฅผ ์ฌ๊ฒ์ฆํ์ฌ ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ ์ ์๊ฒ ํด์ค์.
Next.js 15์์๋ fetch ์บ์ฑ๊ณผ ์ฌ๊ฒ์ฆ ์ต์
์ด ๋์ฑ ์์ ํ๋๊ณ ๊ฐ๋ ฅํด์ก์ด์.
์ด์ ์ด ํต์ฌ ๊ธฐ๋ฅ๋ค์ ์ด๋ป๊ฒ ํ์ฉํ๋์ง ์์ธํ ์์๋ณผ๊น์?
0๏ธโฃ fetch ์บ์ฑ์ ๊ธฐ๋ณธ ๋์๊ณผ ์ค์
Next.js์์ fetch๋ฅผ ์ฌ์ฉํ ๋, ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ์ดํฐ๋ ์๋ฒ์ ์บ์๋ผ์.
์ด ์บ์ฑ ๋์์ fetch ์ต์
์ ํตํด ์ธ๋ฐํ๊ฒ ์ ์ดํ ์ ์๋ต๋๋ค.
-
cache: 'force-cache'(๊ธฐ๋ณธ๊ฐ):
์ด ์ต์ ์ Next.jsfetch์ ๊ธฐ๋ณธ ๋์์ผ๋ก, ๋ฐ์ดํฐ๋ฅผ ์บ์ํ๊ณ ๊ฐ๋ฅํ ๊ฒฝ์ฐ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด์.
๋ง์ฝ ์บ์๋ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์บ์ํ๋ต๋๋ค.
๋๋ถ๋ถ์ ์ ์ ๋ฐ์ดํฐ๋ ์์ฃผ ๋ณ๊ฒฝ๋์ง ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ ์ฉํด์. -
cache: 'no-store':
์ด ์ต์ ์ ์ฌ์ฉํ๋ฉดfetch์์ฒญ์ ๊ฒฐ๊ณผ๊ฐ ์บ์๋์ง ์์์.
๋งค ์์ฒญ๋ง๋ค ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์์ ๊ฐ์ ธ์ค๊ฒ ๋์ฃ .
์ฌ์ฉ์๋ณ๋ก ์ค์๊ฐ์ผ๋ก ๋ณํ๋ ๋ฐ์ดํฐ๋ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ฌ์ฉํด์. -
revalidate: number:
์ด ์ต์ ์ ์บ์๋ ๋ฐ์ดํฐ์ ์ ํจ ๊ธฐ๊ฐ์ ์ด ๋จ์๋ก ์ค์ ํด์.
์ค์ ๋ ์๊ฐ(์: 60์ด)์ด ์ง๋๋ฉด ๋ค์ ์์ฒญ ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฐ์ดํฐ๋ฅผ ์ฌ๊ฒ์ฆ(revalidate)ํ๊ณ , ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์บ์๋ฅผ ์ ๋ฐ์ดํธํ๋ต๋๋ค.
์ด๊ฒ์ ISR(Incremental Static Regeneration)๊ณผ ์ ์ฌํ ๋์์ ๊ฐ๋ณfetch์์ฒญ์ ์ ์ฉํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์๋ฉด ๋ผ์.
async function getPost(id: string) { const res = await fetch(`https://api.example.com/posts/${id}`, { // ์บ์๋ฅผ 60์ด ๋์ ์ ํจํ๊ฒ ์ ์งํ๊ณ , ๊ทธ ์ดํ์๋ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ฌ๊ฒ์ฆํด์. next: { revalidate: 60 }, }); if (!res.ok) { throw new Error('Failed to fetch data'); } return res.json(); } async function getRealtimeData(userId: string) { const res = await fetch(`https://api.example.com/users/${userId}/dashboard`, { // ์ด ๋ฐ์ดํฐ๋ ์บ์ํ์ง ์๊ณ , ๋งค ์์ฒญ๋ง๋ค ์ต์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์. cache: 'no-store', }); if (!res.ok) { throw new Error('Failed to fetch data'); } return res.json(); }
1๏ธโฃ revalidate ์ต์
์ ํ์ฉ
revalidate ์ต์
์ fetch ์์ฒญ๋ณ๋ก ์ค์ ํ ์๋ ์๊ณ , ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ ์ ์ฒด์ ์ ์ฉํ ์๋ ์์ด์.
์ด ๋ ๊ฐ์ง ๋ฐฉ์์ ์ฐ์ ์์์ ์ํธ์์ฉ์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํด์.
-
fetch๋จ์์ ์ฌ๊ฒ์ฆ (next: { revalidate: number }):
์ ์ฝ๋ ์์์ฒ๋ผ ํน์ fetchํธ์ถ์๋งrevalidate์ต์ ์ ์ง์ ์ ์ฉํ๋ ๋ฐฉ์์ด์์.
์ด๋ ํด๋นfetch์์ฒญ์ ์บ์ ์ ํจ ๊ธฐ๊ฐ์ ๊ฐ๋ณ์ ์ผ๋ก ์ ์ดํ ๋ ์ ์ฉํ์ฃ . -
๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ ๋จ์์ ์ฌ๊ฒ์ฆ (
export const revalidate = number | 'force-cache' | 'no-store'):
App Router์์๋ ํน์ ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ(์:app/blog/[slug]/page.tsx) ์ ์ฒด์ ๋ํ ์บ์ฑ ์ ์ฑ ์export const revalidate๋ณ์๋ฅผ ํตํด ์ค์ ํ ์ ์์ด์.
์ด ์ค์ ์ ํด๋น ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ ๋ด์ ๋ชจ๋fetch์์ฒญ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ฉ๋๋ต๋๋ค.
// app/products/[id]/page.tsx // ์ด ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ ๋ด์ ๋ชจ๋ fetch ์์ฒญ์ 3600์ด(1์๊ฐ)๋ง๋ค ์ฌ๊ฒ์ฆ๋ผ์. export const revalidate = 3600; async function getProduct(id: string) { // fetch ์์ฒญ์ ๊ฐ๋ณ revalidate ์ต์ ์ด ์์ผ๋ฏ๋ก, ์์์ ์ค์ ํ 3600์ด๊ฐ ์ ์ฉ๋ผ์. const res = await fetch(`https://api.example.com/products/${id}`); return res.json(); } async function getProductReviews(id: string) { const res = await fetch(`https://api.example.com/products/${id}/reviews`, { // ์ด fetch ์์ฒญ์ ๊ฐ๋ณ์ ์ผ๋ก revalidate: 60์ด๋ฅผ ์ค์ ํ์ด์. // ๋ฐ๋ผ์ ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ์ revalidate ๊ฐ(3600์ด)๋ณด๋ค ์ด ๊ฐ์ด ์ฐ์ ์ ์ฉ๋๋ต๋๋ค. next: { revalidate: 60 }, }); return res.json(); } export default async function ProductPage({ params }: { params: { id: string } }) { const product = await getProduct(params.id); const reviews = await getProductReviews(params.id); return ( <div> <h1>{product.name}</h1> {/* ... */} <h2>Reviews</h2> {/* ... */} </div> ); }
์ ์ฉํ ํ์ฐ์ ์์:
fetch์์ฒญ์ ์ง์ ์ค์ ํcache๋๋revalidate์ต์ ์ด ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ์ ์ค์ ๋export const revalidate๊ฐ๋ณด๋ค ํญ์ ์ฐ์ ํด์.
๋ฐ๋ผ์ ์ธ๋ฐํ ์ ์ด๊ฐ ํ์ํ ๋๋ ๊ฐ๋ณfetch์ต์ ์, ์ ์ฒด ๋ผ์ฐํธ์ ๊ธฐ๋ณธ ์ ์ฑ ์ ์ค์ ํ ๋๋export const revalidate๋ฅผ ์ฌ์ฉํ์๋ฉด ๋๋ต๋๋ค.
2๏ธโฃ noStore() ํจ์
Next.js 15์์๋ next/cache ๋ชจ๋์์ ์ ๊ณตํ๋ noStore() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ์คํ ์ค์ธ ์์ฒญ์ ์บ์ํ์ง ์๋๋ก ๊ฐ์ ํ ์ ์์ด์.
์ด๋ ํนํ Server Components์์ ๋์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ ์ ์ฉํ๋ต๋๋ค.
noStore()๋ฅผ ํธ์ถํ๋ฉด ํด๋น Server Component์ ๋ ๋๋ง ๊ณผ์ ์์ ๋ฐ์ํ๋ ๋ชจ๋ fetch ์์ฒญ์ด cache: 'no-store'๋ก ๋์ํ๋๋ก ๊ฐ์ ๋ผ์.
import { noStore } from 'next/cache'; async function getUserProfile(userId: string) { noStore(); // ์ด ํจ์๊ฐ ํธ์ถ๋ ์์ ๋ถํฐ ํด๋น ์์ฒญ์ ์บ์๋์ง ์์์. const res = await fetch(`https://api.example.com/users/${userId}/profile`); if (!res.ok) { throw new Error('Failed to fetch profile'); } return res.json(); } export default async function ProfilePage({ params }: { params: { userId: string } }) { const userProfile = await getUserProfile(params.userId); return ( <div> <h1>{userProfile.name}'s Profile</h1> {/* ... */} </div> ); }
์ ๋ณด
noStore()๋ Server Component๋ Server Action ๋ด๋ถ์์๋ง ํธ์ถํ ์ ์์ด์.
ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ API Route Handler์์๋ ์ฌ์ฉํ ์ ์์ผ๋ ์ฃผ์ํด ์ฃผ์ธ์.
๐งช ์ค์ Next.js 15 ์บ์ฑ ์ ๋ต ์์
์ด์ ์ค์ ์๋๋ฆฌ์ค์์ Next.js 15์ ์บ์ฑ ์ ๋ต์ ์ด๋ป๊ฒ ์ ์ฉํ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ํตํด ์์๋ณผ๊น์?
0๏ธโฃ ์ ์ ๋ฐ์ดํฐ์ ๋์ ๋ฐ์ดํฐ ํผํฉ ์ฒ๋ฆฌ
๋ธ๋ก๊ทธ ์น์ฌ์ดํธ๋ฅผ ๊ฐ๋ฐํ๋ค๊ณ ๊ฐ์ ํด ๋ณผ๊ฒ์.
๋ธ๋ก๊ทธ ํฌ์คํธ ๋ชฉ๋ก์ ์์ฃผ ๋ณ๊ฒฝ๋์ง ์์ง๋ง, ์ฌ์ฉ์๋ณ ์๋ฆผ์ด๋ ์ค์๊ฐ ์ธ๊ธฐ ๊ธ ๋ชฉ๋ก์ ์์ฃผ ๋ณ๊ฒฝ๋ ์ ์์ด์.
์ด๋ ์ ์ ํ ์บ์ฑ ์ ๋ต์ ์ ์ฉํ์ฌ ์ฑ๋ฅ๊ณผ ๋ฐ์ดํฐ ์ ์ ๋๋ฅผ ๋์์ ํ๋ณดํ ์ ์๋ต๋๋ค.
// app/blog/page.tsx import { noStore } from 'next/cache'; interface Post { id: string; title: string; content: string; } interface Notification { id: string; message: string; } // ๋ธ๋ก๊ทธ ํฌ์คํธ ๋ชฉ๋ก์ 1์๊ฐ(3600์ด)๋ง๋ค ์ฌ๊ฒ์ฆ๋๋๋ก ์ค์ ํด์. async function getPosts() { const res = await fetch('https://api.example.com/blog/posts', { next: { revalidate: 3600 }, }); if (!res.ok) throw new Error('Failed to fetch posts'); return res.json(); } // ์ฌ์ฉ์ ์๋ฆผ์ ์ค์๊ฐ์ผ๋ก ๋ณด์ฌ์ค์ผ ํ๋ฏ๋ก ์บ์ฑํ์ง ์์์. async function getUserNotifications(userId: string): Promise<Notification[]> { noStore(); // ์ด ํจ์ ๋ด์ fetch๋ ์บ์๋์ง ์์์. const res = await fetch(`https://api.example.com/users/${userId}/notifications`); if (!res.ok) throw new Error('Failed to fetch notifications'); return res.json(); } export default async function BlogPage() { const posts: Post[] = await getPosts(); const userId = 'user123'; // ์ค์ ๋ก๋ ์ธ์ฆ๋ ์ฌ์ฉ์ ID๋ฅผ ์ฌ์ฉํ๊ฒ ์ฃ ? const notifications: Notification[] = await getUserNotifications(userId); return ( <div> <h1>๋ธ๋ก๊ทธ ํฌ์คํธ</h1> <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> <h2>๋ด ์๋ฆผ</h2> {notifications.length > 0 ? ( <ul> {notifications.map(notif => ( <li key={notif.id}>{notif.message}</li> ))} </ul> ) : ( <p>์๋ก์ด ์๋ฆผ์ด ์์ด์.</p> )} </div> ); }
์ ์์์์๋ ๋ธ๋ก๊ทธ ํฌ์คํธ ๋ชฉ๋ก์ revalidate: 3600์ผ๋ก 1์๊ฐ๋ง๋ค ์ฌ๊ฒ์ฆ๋๋๋ก ์ค์ ํ์ฌ ์ฑ๋ฅ์ ํ๋ณดํ๊ณ , ์ฌ์ฉ์ ์๋ฆผ์ noStore()๋ฅผ ์ฌ์ฉํ์ฌ ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ก ํ์ด์.
์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ๋ฐ์ดํฐ์ ํน์ฑ์ ๋ง๋ ์บ์ฑ ์ ๋ต์ ์ ์ฐํ๊ฒ ์ ์ฉํ ์ ์๋ต๋๋ค.
1๏ธโฃ fetch ์ต์
์ ํ์ฉํ ์ธ๋ฐํ ์ ์ด: tags์ revalidateTag
Next.js 15์์๋ fetch ์์ฒญ์ tags ์ต์
์ ์ถ๊ฐํ์ฌ ๋์ฑ ์ธ๋ฐํ ์บ์ ์ฌ๊ฒ์ฆ์ด ๊ฐ๋ฅํด์ก์ด์.
tags๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ํ๊ทธ๊ฐ ์ง์ ๋ fetch ์บ์๋ง ์ ํ์ ์ผ๋ก ๋ฌดํจํํ ์ ์๋ต๋๋ค.
์ด๋ ํนํ Server Actions์ ํจ๊ป ์ฌ์ฉํ ๋ ๊ฐ๋ ฅํ ์๋์ง๋ฅผ ๋ฐํํด์.
// app/products/actions.ts (Server Action) 'use server'; import { revalidateTag } from 'next/cache'; interface Product { id: string; name: string; price: number; } export async function addProduct(productData: Omit<Product, 'id'>) { // ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๋ก์ด ์ ํ์ ์ถ๊ฐํ๋ ๋ก์ง const newProduct = await fetch('https://api.example.com/products', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(productData), }).then(res => res.json()); // 'products' ํ๊ทธ๊ฐ ์ง์ ๋ ๋ชจ๋ fetch ์บ์๋ฅผ ์ฌ๊ฒ์ฆํด์. // ์ด๋ก ์ธํด products ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋ ํ์ด์ง๊ฐ ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๊ฒ ๋๋ต๋๋ค. revalidateTag('products'); return newProduct; } // app/products/page.tsx async function getProducts(): Promise<Product[]> { const res = await fetch('https://api.example.com/products', { // ์ด fetch ์์ฒญ์ 'products' ํ๊ทธ๋ฅผ ์ง์ ํ์ด์. next: { tags: ['products'] }, }); if (!res.ok) throw new Error('Failed to fetch products'); return res.json(); } export default async function ProductsPage() { const products = await getProducts(); return ( <div> <h1>์ ํ ๋ชฉ๋ก</h1> <ul> {products.map(product => ( <li key={product.id}>{product.name} - {product.price}์</li> ))} </ul> {/* ์ ํ ์ถ๊ฐ ํผ (Server Action ํธ์ถ) */} </div> ); }
์ ์ฉํ ํ
revalidateTag()๋ ํน์ tags๋ฅผ ๊ฐ์งfetch์บ์๋ง ๋ฌดํจํํ๋ฏ๋ก, ์ ์ฒด ๋ผ์ฐํธ๋ฅผ ์ฌ๊ฒ์ฆํ๋revalidatePath()๋ณด๋ค ํจ์ฌ ํจ์จ์ ์ด๊ณ ์ ๊ตํ ์บ์ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํด์.
ํนํ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ๋น๋ฒํ ๋ถ๋ถ๋ง ์ ๋ฐ์ดํธํด์ผ ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ต๋๋ค.
๐ ์ ๋ฆฌํ๋ฉฐ: ์ต์ ์ ์ฑ๋ฅ์ ์ํ ์บ์ฑ ์ ๋ต
Next.js 15๋ fetch API๋ฅผ ํตํ ์บ์ฑ๊ณผ ์ฌ๊ฒ์ฆ ๊ธฐ๋ฅ์ ๋์ฑ ๊ฐ๋ ฅํ๊ฒ ์ ๊ณตํ๋ฉฐ, ๊ฐ๋ฐ์๊ฐ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ์ ์ต์ ํํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก ๋๊ณ ์์ด์.
0๏ธโฃ ํต์ฌ ์์ฝ
fetch๊ธฐ๋ณธ ์บ์ฑ: Next.jsfetch๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ต์ ์บ์ํ๋ฉฐ,cache: 'force-cache'์ ๋์ผํ๊ฒ ๋์ํด์.cache: 'no-store': ์ค์๊ฐ ๋ฐ์ดํฐ๋ ์บ์๊ฐ ํ์ ์๋ ๋ฐ์ดํฐ๋ ์ด ์ต์ ์ ์ฌ์ฉํ์ฌ ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค์ธ์.revalidate: number: ISR๊ณผ ์ ์ฌํ๊ฒ ํน์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฌ๊ฒ์ฆํ์ฌ ์ ์ ๋๋ฅผ ์ ์งํ ์ ์์ด์.export const revalidate: ๋ผ์ฐํธ ์ธ๊ทธ๋จผํธ ์ ์ฒด์ ๋ํ ๊ธฐ๋ณธ ์บ์ฑ ์ ์ฑ ์ ์ค์ ํ ์ ์์ผ๋ฉฐ, ๊ฐ๋ณfetch์ต์ ์ด ์ฐ์ ํด์.noStore():next/cache์์ ์ํฌํธํ์ฌ ํ์ฌ ์์ฒญ์ ์บ์ํ์ง ์๋๋ก ๊ฐ์ ํ ๋ ์ฌ์ฉํด์.tags์revalidateTag():fetch์์ฒญ์tags๋ฅผ ๋ถ์ฌํ๊ณ ,revalidateTag()๋ฅผ ํตํด ํน์ ํ๊ทธ์ ์บ์๋ง ์ ํ์ ์ผ๋ก ๋ฌดํจํํ์ฌ ์ ๊ตํ ์บ์ ๊ด๋ฆฌ๋ฅผ ํ ์ ์์ด์.
1๏ธโฃ ๋ค์ ์ก์
์ด๋ฌํ ์บ์ฑ ์ ๋ต๋ค์ ์ฌ๋ฌ๋ถ์ Next.js 15 ํ๋ก์ ํธ์ ์ ๊ทน์ ์ผ๋ก ์ ์ฉํด ๋ณด์ธ์.
๋ฐ์ดํฐ์ ํน์ฑ๊ณผ ๋ณ๊ฒฝ ์ฃผ๊ธฐ๋ฅผ ๊ณ ๋ คํ์ฌ ์ ์ ํ cache ๋ฐ revalidate ์ต์
์ ์ ํํ๊ณ , noStore()์ revalidateTag()๋ฅผ ํ์ฉํ์ฌ ๋์ฑ ์ ์ฐํ๊ณ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ๊ด๋ฆฌ ์์คํ
์ ๊ตฌ์ถํ๋ ๊ฒ์ด ์ค์ํด์.
์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์บ์ฑ ์ ๋ต ๋ณ๊ฒฝ ์ ํ์ ์ฑ๋ฅ ๋ณํ๋ฅผ ์ธก์ ํด ๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด ๋ ๊ฑฐ์์.
Next.js์ ๊ฐ๋ ฅํ ์บ์ฑ ๊ธฐ๋ฅ์ ๋ง์คํฐํ์ฌ ์ฌ์ฉ์์๊ฒ ๋ ๋น ๋ฅด๊ณ ๋ฐ์์ฑ ์ข์ ์น ๊ฒฝํ์ ์ ๊ณตํ์๊ธธ ๋ฐ๋ผ์!
๐ฎ ์ฐธ๊ณ
- Next.js Data Fetching: Caching
- Next.js Data Fetching: Revalidating
- Next.js API Reference:
noStore() - Next.js API Reference:
revalidateTag()
์ฐ๊ด๋ ํฌ์คํธ
๋จ์ด: 1,301๊ฐ14๋ถ[๐ค] Next.js App Router: generateStaticParams๋ก ๋์ ๋ผ์ฐํ ๋น๋ ์ต์ ํํ๊ธฐ
Next.js App Router์์ generateStaticParams ํจ์๋ฅผ ํ์ฉํ์ฌ ๋์ ๋ผ์ฐํ ์ ์ ์ ํ์ด์ง๋ฅผ ํจ์จ์ ์ผ๋ก ์์ฑํ๊ณ ๋น๋ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์.
๋จ์ด: 1,863๊ฐ22๋ถ[๐ค] React ๋ ๋๋ง ์ต์ ํ: useMemo, useCallback, React.memo ์๋ฒฝ ๊ฐ์ด๋
์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ React ๋ ๋๋ง ์ต์ ํ ๊ฐ์ด๋. useMemo, useCallback, React.memo์ ์ ํํ ์ฌ์ฉ๋ฒ๊ณผ ์ค๋ฌด์์ ํํ ์ ์ง๋ฅด๋ ์ค์, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ฑ๋ฅ ํฅ์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.
๋จ์ด: 2,122๊ฐ24๋ถ[๐ค] JavaScript Proxy์ Reflect ์ฌ์ธต ๋ถ์: ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์ฝ๋ ๊ฐํํ๊ธฐ
JavaScript Proxy์ Reflect API๋ฅผ ํ์ฉํ ๋ฉํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์ฌ์ธต ๋ถ์ํด์. ๊ฐ์ฒด ์ ๊ทผ ์ ์ด, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ก๊น , ๋ฐ์ํ ์์คํ ๊ตฌํ ๋ฑ ์ค์ฉ์ ์ธ ํ์ฉ ์ฌ๋ก๋ฅผ ํตํด ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 2,019๊ฐ24๋ถ
[๐ค] React/Next.js ๋ฒ๋ค ์ต์ ํ: ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ์๋ฒฝ ๊ฐ์ด๋
React์ Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ด๊ณ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํ๋ ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ๊ธฐ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์นํฉ ์ค์ ๋ถํฐ React.lazy, Next.js dynamic import๊น์ง ๋ค๋ค์.
- ๋จ์ด: 1,769๊ฐ20๋ถ
[๐ค] React์ `useOptimistic` ํ ์ผ๋ก ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ตฌํํ๊ธฐ: Server Actions์ ํจ๊ป
React 18/19์ `useOptimistic` ํ ์ ํ์ฉํ์ฌ Server Actions์ ์ฐ๋๋๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
๋จ์ด: 1,557๊ฐ17๋ถ[๐ค] TypeScript const Type Parameters: ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ๊ฐํ์ ์ค์ฉ์ ์ธ ํ์ฉ๋ฒ
TypeScript 5.0์ ๋์ ๋ const Type Parameters๋ฅผ ํ์ฉํ์ฌ ์ ๋ค๋ฆญ ํจ์์ ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ์ ์ ๊ตํ๊ฒ ์ ์ดํ๊ณ , ๋์ฑ ๊ฒฌ๊ณ ํ ํ์ ์์คํ ์ ๊ตฌ์ถํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. as const์์ ์ฐจ์ด์ ๊ณผ ์ค์ ์ฝ๋ ์์๋ฅผ ํตํด ์ด์ค๊ธ ๊ฐ๋ฐ์๋ ์ฝ๊ฒ ์ดํดํ ์ ์๋๋ก ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,015๊ฐ22๋ถ
[๐ค] Next.js/React ์ฑ CLS ์ต์ ํ: ์ํํธ ์๋ ์ฌ์ฉ์ ๊ฒฝํ ๋ง๋ค๊ธฐ
Next.js์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ Cumulative Layout Shift(CLS) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์์๋ณด์ธ์. ์น ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ ์์์ธ CLS๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,735๊ฐ21๋ถ
[๐ค] Next.js SSR, SSG, ISR ๋ ๋๋ง ์ ๋ต: App Router์์ ์ต์ ์ ์ ํ์?
Next.js App Router์์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR), ์ ์ ์ฌ์ดํธ ์์ฑ(SSG), ์ฆ๋ถ ์ ์ ์ฌ์์ฑ(ISR) ๊ฐ ๋ ๋๋ง ์ ๋ต์ ๋์ ์๋ฆฌ, ์ฅ๋จ์ , ์ค์ ํ์ฉ ๋ฐ ์ต์ ํ ๋ฐฉ๋ฒ์ ๋น๊ต ๋ถ์ํด๋๋ ค์.
- ๋จ์ด: 1,460๊ฐ17๋ถ
[๐ค] React Context API์ Zustand: ์ ์ญ ์ํ ๊ด๋ฆฌ, ์ธ์ ๋ฌด์์ ์จ์ผ ํ ๊น์?
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Context API์ ๊ฐ๋ฒผ์ด ์ธ๋ถ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Zustand๋ฅผ ๋น๊ต ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ๊ฐ ๋๊ตฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๋ ์ ๋ต์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,004๊ฐ24๋ถ
[๐ค] Turborepo๋ก Next.js ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์ถ: ํจ์จ์ ์ธ ๊ฐ๋ฐ ๋ฐ ์ต์ ํ ์ ๋ต
Turborepo๋ฅผ ํ์ฉํ์ฌ Next.js ํ๋ก์ ํธ๋ฅผ ๋ชจ๋ ธ๋ ํฌ๋ก ๊ตฌ์ฑํ๊ณ , ๊ณต์ ์ปดํฌ๋ํธ, ์ ํธ๋ฆฌํฐ, CI/CD ์ต์ ํ ๋ฐฉ์์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,318๊ฐ27๋ถ
[๐ค] React useEffect ํ , ์ด์ ํท๊ฐ๋ฆฌ์ง ๋ง์ธ์! (์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ์๋ฒฝ ๊ฐ์ด๋)
React ๊ฐ๋ฐ์์ ํ์์ ์ธ useEffect ํ ์ ๋์ ์๋ฆฌ๋ถํฐ ์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ํจ์ ํ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด์์ ์์ฃผ ๊ฒช๋ ์ค์์ ์ต์ ํ ์ ๋ต๊น์ง, ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด์.
- ๋จ์ด: 3,270๊ฐ31๋ถ
[๐ค] Next.js Server Actions ์ค์ : ์๋ฌ ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋๊ด์ UI ์ ๋ฐ์ดํธ
Next.js Server Actions๋ฅผ ์ค๋ฌด์ ์ ์ฉํ ๋ ๋ง์ฃผํ๋ ์๋ฌ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ธฐ๋ฒ์ ์์ธํ ์ฝ๋ ์์์ ํจ๊ป ์์๋ณด์ธ์.
๋จ์ด: 1,981๊ฐ21๋ถ[๐ค] TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์๋ฒฝ ๊ฐ์ด๋: ์ค์ ํ์ฉ ํจํด
TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์ ํต์ฌ ๊ฐ๋ ๊ณผ ์ค์ ํ์ฉ๋ฒ์ ๊น์ด ์๊ฒ ๋ค๋ค์. Pick, Omit, Partial, Required ๋ฑ ์์ฃผ ์ฐ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ผ๋ก ๋ณต์กํ ํ์ ์ ํจ๊ณผ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ํ์ ์คํฌ๋ฆฝํธ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
- ๋จ์ด: 1,707๊ฐ20๋ถ
[๐ค] Next.js App Router ๋ฏธ๋ค์จ์ด: ๊ฐ๋ ฅํ ์์ฒญ ์ฒ๋ฆฌ ์ ๋ต๊ณผ ์ค์ ์์
Next.js App Router ํ๊ฒฝ์์ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํด ์ฌ์ฉ์ ์ธ์ฆ, ๋ฆฌ๋ค์ด๋ ์ , ๊ตญ์ ํ ๋ฑ์ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง์ ํจ์จ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ ์์ ์ ํจ๊ป ์์ธํ ์์๋ณด์ธ์.
- ๋จ์ด: 1,625๊ฐ19๋ถ
[๐ค] ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ ์ฌํ: ์ค์ฉ์ ์ธ ํจํด๊ณผ ํํ ์คํด๋ค
ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ(Generics)์ ๊น์ด ์ดํดํ๊ณ , ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ ๋ค๋ฆญ ํจํด๊ณผ ํํ ๊ฒช๋ ์คํด๋ค์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์ฝ๊ณ ๋ช ํํ๊ฒ ์ค๋ช ํด ๋๋ ค์. ํ์ ์์ ์ฑ๊ณผ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
๋จ์ด: 1,846๊ฐ18๋ถ[๐ค] Next.js Route Handler: App Router์์ ์์ ํ๊ณ ํจ์จ์ ์ธ API ๊ตฌ์ถํ๊ธฐ (์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ ํฌํจ)
Next.js App Router์ Route Handler๋ฅผ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์บ์ฑ ์ ๋ต์ ํฌํจํ ์ค์ฉ์ ์ธ ํ์ผ๋ก ์์ ํ๊ณ ํจ์จ์ ์ธ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 1,932๊ฐ22๋ถ
[๐ค] Next.js Image ์ปดํฌ๋ํธ ์ต์ ํ: Core Web Vitals ๊ฐ์ ๋ถํฐ ์ค์ ํ์ฉ๊น์ง
Next.js์ Image ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ์น ์ฑ๋ฅ ํต์ฌ ์งํ์ธ Core Web Vitals๋ฅผ ๊ฐ์ ํ๊ณ , ๋ค์ํ ์ต์ ํ ์ต์ ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,173๊ฐ25๋ถ
[๐ค] Next.js 14.1+์ ํ์ : Partial Prerendering (PPR) ์๋ฒฝ ๊ฐ์ด๋์ ์ค์ ์ต์ ํ ์ ๋ต
Next.js 14.1๋ถํฐ ๋์ ๋ Partial Prerendering (PPR)์ ํตํด ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ทน๋ํํ๊ณ ๋์ ์ฝํ ์ธ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ฃจ์ด์. PPR์ ๋์ ์๋ฆฌ๋ถํฐ ์ค์ ํ๋ก์ ํธ ์ ์ฉ ์ ๋ต๊น์ง, ๊ฐ๋ฐ์๋ค์ด ๊ถ๊ธํดํ๋ ๋ชจ๋ ๊ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,786๊ฐ19๋ถ
[๐ค] TypeScript ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋: ๋ณต์กํ ํ์ ๋ ์์ฝ๊ฒ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ
TypeScript ๊ฐ๋ฐ์์ ๋ง์ฃผํ๋ ๋ณต์กํ ํ์ ์ถ๋ก ๋ฌธ์ , ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋๋ฅผ ํ์ฉํ๋ฉด ํจ์ฌ ์ฐ์ํ๊ณ ๊ฐ๋ ฅํ๊ฒ ํด๊ฒฐํ ์ ์์ด์. ์ค์ ์์ ์ ํจ๊ป ๊ทธ ํ์ฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ค๋ด ๋๋ค.
- ๋จ์ด: 1,697๊ฐ21๋ถ
[๐ค] JavaScript ์ด๋ฒคํธ ๋ฃจํ(Event Loop) ์์ ์ ๋ณต: ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ฐํ์ ๋์ ์๋ฆฌ
JavaScript์ ํต์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ธ ์ด๋ฒคํธ ๋ฃจํ์ ๋์ ์๋ฆฌ๋ฅผ ์ฌ๋ ์๊ฒ ํํค์ณ ๋ด์. ์ฝ ์คํ, ํ์คํฌ ํ, ๋ง์ดํฌ๋กํ์คํฌ ํ์์ ์ํธ์์ฉ์ ์ดํดํ๊ณ , ์ค๋ฌด์์ ๋ง์ฃผ์น๋ ๋น๋๊ธฐ ์ฝ๋์ ๋์์ ๋ช ํํ ์์ธกํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,960๊ฐ23๋ถ
[๐ค] Next.js Server & Client Components, ์ค์ ์์ ํ๋ช ํ๊ฒ ์ ํํ๋ ๊ฐ์ด๋
Next.js App Router์์ Server Components์ Client Components ์ค ์ด๋ค ๊ฒ์ ์ฌ์ฉํด์ผ ํ ์ง ๊ณ ๋ฏผ์ด์ ๊ฐ์? ์ด ๊ธ์์ ๋ ์ปดํฌ๋ํธ์ ํต์ฌ ์ฐจ์ด์ , ์ฌ์ฉ ์์ , ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ์ค์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ฆด๊ฒ์.
- ๋จ์ด: 1,878๊ฐ21๋ถ
[๐ค] TypeScript satisfies ์ฐ์ฐ์: ํ์ ์ถ๋ก ๊ณผ ์์ ์ฑ์ ๋์์ ์ก๋ ๋น๋ฒ
TypeScript์ `satisfies` ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ ํ์ ์ถ๋ก ์ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์๊ฒฉํ ํ์ ์์ ์ฑ์ ํ๋ณดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ์ค์ฉ์ ์ธ ์์๋ฅผ ํตํด ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํฉ๋๋ค.
- ๋จ์ด: 1,207๊ฐ15๋ถ
[๐ค] React 19 ์๋ก์ด ๊ธฐ๋ฅ: use ํ , Actions, ๊ทธ๋ฆฌ๊ณ ์ปดํ์ผ๋ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
React 19์ ํต์ฌ ๋ณ๊ฒฝ ์ฌํญ์ธ use ํ , ์๋ฒ ์ก์ , ๊ทธ๋ฆฌ๊ณ React ์ปดํ์ผ๋ฌ์ ๋์ ๋ฐฐ๊ฒฝ๊ณผ ์ค์ ํ์ฉ ์์๋ฅผ ์ด์ค๊ธ ๊ฐ๋ฐ์ ๋๋์ด์ ๋ง์ถฐ ์์ธํ ์ค๋ช ํฉ๋๋ค. ์ต์ React ์ ๋ฐ์ดํธ๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ๊ณผ ๊ฐ๋ฐ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
- ๋จ์ด: 1,512๊ฐ16๋ถ
[๐ค] Next.js App Router ์บ์ฑ ์ ๋ต: ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ (revalidatePath, revalidateTag) ์๋ฒฝ ๊ฐ์ด๋
Next.js 14 App Router์์ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต๊ณผ revalidatePath, revalidateTag๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ณด๊ณ ์น ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.