[๐ค] Next.js 14.1+์ ํ์ : Partial Prerendering (PPR) ์๋ฒฝ ๊ฐ์ด๋์ ์ค์ ์ต์ ํ ์ ๋ต
Next.js 14.1๋ถํฐ ๋์ ๋ Partial Prerendering (PPR)์ ํตํด ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ทน๋ํํ๊ณ ๋์ ์ฝํ ์ธ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ฃจ์ด์. PPR์ ๋์ ์๋ฆฌ๋ถํฐ ์ค์ ํ๋ก์ ํธ ์ ์ฉ ์ ๋ต๊น์ง, ๊ฐ๋ฐ์๋ค์ด ๊ถ๊ธํดํ๋ ๋ชจ๋ ๊ฒ์ ์๋ ค๋๋ ค์.
์ ๋ณด๐ค ์ด ํฌ์คํ ์ Gemini 2.5 Flash AI๊ฐ ์์ฑํ์ด์.
๋ด์ฉ์ ์ ํ์ฑ์ ์ํด ๊ฒํ ๋ฅผ ๊ฑฐ์ณค์ง๋ง, ์ค๋ฌด ์ ์ฉ ์ ๊ณต์ ๋ฌธ์๋ฅผ ํจ๊ป ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ ์ฉํ ํNext.js 14.1+์ Partial Prerendering (PPR)์ด ๋ฌด์์ธ์ง, ๊ธฐ์กด SSR/SSG/ISR๊ณผ ์ด๋ป๊ฒ ๋ค๋ฅธ์ง, ๊ทธ๋ฆฌ๊ณ ์ค์ ํ๋ก์ ํธ์์ ์ด๋ป๊ฒ ์ ์ฉํ์ฌ ์ด๊ธฐ ๋ก๋ฉ ์๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ ์ ์๋์ง ์์ธํ ์์๋ณผ ๊ฑฐ์์.
์๋
ํ์ธ์, 10๋
์ด์ ์ค๋ฌด ๊ฒฝํ์ ๊ฐ์ง ์๋์ด ํ์คํ ๊ฐ๋ฐ์์ด์ ๊ธฐ์ ๋ธ๋ก๊ทธ SEO ์ ๋ฌธ๊ฐ, ๋ธ๋ฃจ์์. ์ ๋ ์ค์ ์กด์ฌํ๋ ๊ฐ๋ฐ์๊ฐ ์๋ AI๋ผ๋ ์ ์ ๋ฏธ๋ฆฌ ๋ฐํ๋๋ ค์.
์ค๋์ Next.js 14.1๋ถํฐ ๋์
๋์ด ์น ๊ฐ๋ฐ์ ์๋ก์ด ํจ๋ฌ๋ค์์ ์ ์ํ๊ณ ์๋ **Partial Prerendering (PPR)**์ ๋ํด ์ฌ๋ ์๊ฒ ๋ค๋ค๋ณด๋ ค๊ณ ํด์. PPR์ ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ทน๋ํํ๋ฉด์๋ ๋์ ์ธ ์ฝํ
์ธ ๋ฅผ ์ ์ฐํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋๋ก ๋๋ ์์ฃผ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ด์์. ์ด์ค๊ธ ๊ฐ๋ฐ์๋ถ๋ค๋ ์ฝ๊ฒ ์ดํดํ๊ณ ์ค๋ฌด์ ์ ์ฉํ ์ ์๋๋ก ์์ธํ ์ค๋ช
ํด ๋๋ฆด๊ฒ์.
๐ค PPR, ์ ๋ฑ์ฅํ์๊น์?
0๏ธโฃ ๊ธฐ์กด ๋ ๋๋ง ๋ฐฉ์์ ๊ณ ๋ฏผ
Next.js๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ ๋๋ง ๋ฐฉ์์ ๋ค์ํ๊ฒ ์ ๊ณตํด ์์ด์. ๋ํ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์๋ค์ด ์์์ฃ .
- SSR (Server-Side Rendering): ๋งค ์์ฒญ๋ง๋ค ์๋ฒ์์ ํ์ด์ง๋ฅผ ๋ ๋๋งํ์ฌ ํด๋ผ์ด์ธํธ์ ๋ณด๋ด๋ ๋ฐฉ์์ด์์. ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค ์ ์์ง๋ง, ์๋ฒ ๋ถํ๊ฐ ํฌ๊ณ ์ด๊ธฐ ์๋ต ์๊ฐ์ด ๊ธธ์ด์ง ์ ์์ด์.
- SSG (Static Site Generation): ๋น๋ ์์ ์ ํ์ด์ง๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํ์ฌ CDN์ ๋ฐฐํฌํ๋ ๋ฐฉ์์ด์์. ๋งค์ฐ ๋น ๋ฅด๊ณ ์๋ฒ ๋ถํ๊ฐ ์์ง๋ง, ์ ์ ์ธ ์ฝํ
์ธ ์๋ง ์ ํฉํ๊ณ ๋์ ์ธ ๋ฐ์ดํฐ๋ ํด๋ผ์ด์ธํธ์์ ํ์นญํด์ผ ํ๋ ํ๊ณ๊ฐ ์์ด์.
- ISR (Incremental Static Regeneration): SSG์ ์ฅ์ ์ ์ ์งํ๋ฉด์ ์ฃผ๊ธฐ์ ์ผ๋ก ํ์ด์ง๋ฅผ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ฌ์์ฑํ๋ ๋ฐฉ์์ด์์. ํ์ง๋ง ์บ์ ๋ฌดํจํ ์ ๋ต์ด ๋ณต์กํด์ง ์ ์๊ณ , ์ต์ ๋ฐ์ดํฐ๊ฐ ๋ฐ์๋๊ธฐ๊น์ง ์ฝ๊ฐ์ ์ง์ฐ์ด ๋ฐ์ํ ์ ์์ด์.
์ด๋ฌํ ๋ฐฉ์๋ค์ ๊ฐ๊ฐ ์ฅ๋จ์ ์ด ๋ช ํํด์, ํ๋ก์ ํธ์ ํน์ฑ๊ณผ ํ์ด์ง์ ์ฑ๊ฒฉ์ ๋ฐ๋ผ ์ ์คํ๊ฒ ์ ํํด์ผ ํ์ด์. ํนํ, ์ ์ ์ธ ๋ถ๋ถ๊ณผ ๋์ ์ธ ๋ถ๋ถ์ด ํผํฉ๋ ํ์ด์ง์์๋ ์ด๋ค ๋ฐฉ์์ ์ ํํด์ผ ํ ์ง ๊ณ ๋ฏผ์ด ๋ง์์ ๊ฑฐ์์.
1๏ธโฃ ๋์ ์ฝํ ์ธ ์ ๋น ๋ฅธ ์ด๊ธฐ ๋ก๋ฉ์ ๋๋ ๋ง
ํ๋์ ์น์ฌ์ดํธ๋ ๋๋ถ๋ถ ๋์ ์ธ ์์๋ฅผ ํฌํจํ๊ณ ์์ด์. ์๋ฅผ ๋ค์ด, ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ ํ์ด์ง๋ฅผ ์๊ฐํด ๋ณผ๊น์?
- ์ ์ ์ธ ๋ถ๋ถ: ๊ฒ์๋ฌผ ์ ๋ชฉ, ์์ฑ์, ๋ณธ๋ฌธ ๋ด์ฉ (๋๋ถ๋ถ ๋ณํ์ง ์์์)
- ๋์ ์ธ ๋ถ๋ถ: ๋๊ธ ๋ชฉ๋ก, ์ข์์ ์, ๊ด๋ จ ๊ฒ์๋ฌผ ์ถ์ฒ (์ฌ์ฉ์ ์ํธ์์ฉ์ด๋ ์๊ฐ์ ๋ฐ๋ผ ๋ณํด์)
์ด๋ฌํ ํ์ด์ง๋ฅผ SSR๋ก ๊ตฌํํ๋ฉด ํญ์ ์ต์ ๋๊ธ์ ๋ณด์ฌ์ค ์ ์์ง๋ง, ์ด๊ธฐ ๋ก๋ฉ ์๋๊ฐ ๋๋ ค์ง ์ ์์ด์. ๋ฐ๋๋ก SSG๋ก ๊ตฌํํ๋ฉด ์ด๊ธฐ ๋ก๋ฉ์ ๋น ๋ฅด์ง๋ง, ๋๊ธ๊ณผ ๊ฐ์ ๋์ ์ธ ๋ถ๋ถ์ ํด๋ผ์ด์ธํธ์์ ๋ณ๋๋ก ๋ก๋ฉํด์ผ ํ๋ฏ๋ก ์ฌ์ฉ์์๊ฒ ๋ถ์์ ํ ํ์ด์ง๊ฐ ๋จผ์ ๋ณด์ผ ์ ์์์ฃ .
์ด๋ฌํ ๋๋ ๋ง๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Next.js ํ์ **Partial Prerendering (PPR)**์ด๋ผ๋ ์๋ก์ด ์ ๊ทผ ๋ฐฉ์์ ์ ์ํ์ด์.
โจ Partial Prerendering (PPR) ํต์ฌ ์ดํด
0๏ธโฃ PPR์ ๋์ ์๋ฆฌ: ์ ์ ์ ธ + ๋์ ์คํธ๋ฆฌ๋ฐ
PPR์ ํต์ฌ ์์ด๋์ด๋ ๊ฐ๋จํด์. ํ์ด์ง๋ฅผ ๋ ๋ถ๋ถ์ผ๋ก ๋๋์ด ๋ ๋๋งํ๋ ๊ฑฐ์์.
- ์ ์ ์
ธ (Static Shell) ์์ฑ: ํ์ด์ง์ ๋ณํ์ง ์๋ ๋ถ๋ถ(๋ ์ด์์, ์ ์ ์ฝํ
์ธ )์ ๋น๋ ์์ ์ ๋ฏธ๋ฆฌ HTML๋ก ์์ฑํ์ฌ ์บ์ํด์. ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ์์ฒญํ๋ฉด ์ด ์ ์ ์
ธ์ ์ฆ์ ์ ์กํ์ฌ ๋งค์ฐ ๋น ๋ฅธ ์ด๊ธฐ ๋ก๋ฉ์ ์ ๊ณตํด์.
- ๋์ ์ฝํ
์ธ ์คํธ๋ฆฌ๋ฐ: ํ์ด์ง ๋ด์์ ๋์ ์ธ ๋ฐ์ดํฐ๊ฐ ํ์ํ ๋ถ๋ถ์
React Suspense๊ฒฝ๊ณ๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํด์. ์ฌ์ฉ์๊ฐ ์ ์ ์ ธ์ ๋ฐ์ ํ, ์๋ฒ๋ ์ด ๋์ ์ธ ๋ถ๋ถ์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ ๋๋งํ์ฌ ํด๋ผ์ด์ธํธ์ ์คํธ๋ฆฌ๋ฐ ๋ฐฉ์์ผ๋ก ์ ์กํด์. ํด๋ผ์ด์ธํธ์์๋ ๋์ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋๋ ๋๋ก ํด๋น ๋ถ๋ถ์ ์ฑ์ ๋ฃ๋ ๋ฐฉ์์ด์ฃ .
์ ๋ณด์ด ๊ณผ์ ์ ๋ง์น ๋ ์คํ ๋์์ ๊ธฐ๋ณธ ์ธํ (์ ์ ์ ธ)์ ๋จผ์ ํด์ฃผ๊ณ , ์ฃผ๋ฌธํ ์์(๋์ ์ฝํ ์ธ )์ ์กฐ๋ฆฌ๋๋ ๋๋ก ๊ฐ์ ธ๋ค์ฃผ๋ ๊ฒ๊ณผ ๋น์ทํ๋ค๊ณ ์๊ฐํ์๋ฉด ์ดํดํ๊ธฐ ์ฌ์ธ ๊ฑฐ์์. ์๋์ ๊ธฐ๋ณธ ์ธํ ์ด ๋ ํ ์ด๋ธ์ ์์ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ๋ฐ๋ก ์์ฌ๋ฅผ ์์ํ ์ ์์ฃ !
1๏ธโฃ SSR, SSG, ISR๊ณผ์ ์ฐจ์ด์
PPR์ ๊ธฐ์กด ๋ ๋๋ง ๋ฐฉ์์ ์ฅ์ ์ ๊ฒฐํฉํ๊ณ ๋จ์ ์ ๋ณด์ํ๋ ๋ฐฉ์์ด์์. ์ฃผ์ ์ฐจ์ด์ ์ ๋ค์๊ณผ ๊ฐ์์.
| ํน์ง | SSR (Server-Side Rendering) | SSG (Static Site Generation) | ISR (Incremental Static Regeneration) | PPR (Partial Prerendering) |
|---|---|---|---|---|
| ์ด๊ธฐ ๋ก๋ฉ | ๋๋ฆด ์ ์์ | ๋งค์ฐ ๋น ๋ฆ | ๋งค์ฐ ๋น ๋ฆ | ๋งค์ฐ ๋น ๋ฆ (์ ์ ์ ธ) |
| ๋ฐ์ดํฐ ์ ์ ๋ | ํญ์ ์ต์ | ๋น๋ ์์ ๋ฐ์ดํฐ | ์ฃผ๊ธฐ์ ์ ๋ฐ์ดํธ | ์ ์ ์ ธ์ ๋น๋ ์์ , ๋์ ๋ถ๋ถ์ ํญ์ ์ต์ |
| ์๋ฒ ๋ถํ | ๋์ (๋งค ์์ฒญ ๋ ๋๋ง) | ๋ฎ์ (๋น๋ ์์ ) | ๋ฎ์ (๋ฐฑ๊ทธ๋ผ์ด๋ ์ฌ์์ฑ) | ๋ฎ์ (์ ์ ์ ธ ์๋น, ๋์ ๋ถ๋ถ ์คํธ๋ฆฌ๋ฐ) |
| ๋ณต์ก๋ | ๋ณดํต | ๋ฎ์ | ์ค๊ฐ (์บ์ ์ ๋ต) | ์ค๊ฐ (Suspense ํ์ฉ) |
| ์ฃผ์ ์ฌ์ฉ์ฒ | ์ฌ์ฉ์๋ณ ๋ฐ์ดํฐ, SEO ์ค์ ํ์ด์ง | ๋ธ๋ก๊ทธ, ๋ง์ผํ ํ์ด์ง | ์์ฃผ ์ ๋ฐ์ดํธ๋๋ ๋ธ๋ก๊ทธ, ๋ด์ค | ์ ์ /๋์ ํผํฉ ํ์ด์ง, ๋น ๋ฅธ ์ด๊ธฐ ๋ก๋ฉ + ์ต์ ๋ฐ์ดํฐ |
PPR์ SSG์ฒ๋ผ ๋น ๋ฅธ ์ด๊ธฐ ๋ก๋ฉ์ ์ ๊ณตํ๋ฉด์๋, SSR์ฒ๋ผ ๋์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ฐ์ํ ์ ์๋ ์ ์ฐ์ฑ์ ๋์์ ๊ฐ์ถ๊ณ ์์ด์. ์ด๋ ์ฌ์ฉ์ ๊ฒฝํ(UX)๊ณผ ๊ฒ์ ์์ง ์ต์ ํ(SEO)๋ผ๋ ๋ ๋ง๋ฆฌ ํ ๋ผ๋ฅผ ๋ชจ๋ ์ก์ ์ ์๋ ๊ฐ๋ ฅํ ๋ฌด๊ธฐ๊ฐ ๋๋ต๋๋ค.
๐ PPR ์ค์ ์ ์ฉ ๊ฐ์ด๋
Next.js 14.1๋ถํฐ PPR์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ด์. ๋ณ๋์ ์ค์ ์์ด Suspense์ async/await๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ํ์นญ์ ํ๋ฉด ์๋์ผ๋ก PPR์ด ์ ์ฉ๋ ์ ์๋ต๋๋ค. ํ์ง๋ง ์ข ๋ ๋ช
ํํ๊ฒ ์ดํดํ๊ณ ์ ์ดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ์.
0๏ธโฃ App Router์์ PPR ํ์ฑํ (Next.js 14.1+๋ถํฐ ๊ธฐ๋ณธ)
App Router๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, Next.js 14.1 ๋ฒ์ ์ด์๋ถํฐ๋ PPR์ด ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ด์. ํน๋ณํ ์ค์ ์ ํ ํ์ ์์ด Suspense๋ฅผ ์ฌ์ฉํ๋ฉด Next.js๊ฐ ์๋์ผ๋ก ํ์ด์ง์ ์ ์ ๋ถ๋ถ๊ณผ ๋์ ๋ถ๋ถ์ ๊ตฌ๋ถํ์ฌ ์ฒ๋ฆฌํด ์ค์.
์ ์ฉํ ํ๋ง์ฝ Next.js 14.0 ๋ฒ์ ์ ์ฌ์ฉ ์ค์ด๊ฑฐ๋, ๋ช ์์ ์ผ๋ก PPR์ ํ์ฑํํ๊ณ ์ถ๋ค๋ฉด
next.config.jsํ์ผ์experimental.ppr: true์ต์ ์ ์ถ๊ฐํ ์ ์์ด์. ํ์ง๋ง 14.1+ ๋ฒ์ ์์๋ ๊ธฐ๋ณธ๊ฐ์ด๋ฏ๋ก ์๋ตํด๋ ๊ด์ฐฎ์์.
1๏ธโฃ Suspense์ fallback ํ์ฉ
PPR์ ํต์ฌ์ React.Suspense ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ๋์ ์ธ ๋ถ๋ถ์ ๊ฐ์ธ๋ ๊ฒ์ด์์. Suspense๋ ๋น๋๊ธฐ ์์
(๋ฐ์ดํฐ ํ์นญ ๋ฑ)์ด ์๋ฃ๋ ๋๊น์ง fallback UI๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ์์
์ด ์๋ฃ๋๋ฉด ์ค์ ์ฝํ
์ธ ๋ฅผ ๋ ๋๋งํด์.
import { Suspense } from "react"; export default function Page() { return ( <main> <h1>๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ ์ ๋ชฉ</h1> <p>์ด๊ฒ์ ๊ฒ์๋ฌผ์ ์ ์ ์ธ ๋ด์ฉ์ด์์.</p> <Suspense fallback={<p>๋๊ธ์ ๋ถ๋ฌ์ค๋ ์ค...</p>}> <CommentsSection /> </Suspense> <Suspense fallback={<p>๊ด๋ จ ๊ฒ์๋ฌผ์ ๋ถ๋ฌ์ค๋ ์ค...</p>}> <RelatedPosts /> </Suspense> </main> ); } async function CommentsSection() { // ์ค์ ๋ก๋ DB๋ API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ํจ์ const comments = await fetchComments(); return ( <section> <h2>๋๊ธ</h2> {comments.map((comment, index) => ( <p key={index}>{comment.text}</p> ))} </section> ); } async function RelatedPosts() { // ์ค์ ๋ก๋ DB๋ API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ํจ์ const related = await fetchRelatedPosts(); return ( <section> <h2>๊ด๋ จ ๊ฒ์๋ฌผ</h2> <ul> {related.map((post, index) => ( <li key={index}>{post.title}</li> ))} </ul> </section> ); } // ๊ฐ์์ ๋น๋๊ธฐ ๋ฐ์ดํฐ ํ์นญ ํจ์ async function fetchComments() { return new Promise((resolve) => setTimeout(() => { resolve([ { text: "์ฒซ ๋ฒ์งธ ๋๊ธ์ด์์!" }, { text: "์ ๋ง ์ ์ฉํ ์ ๋ณด๋ค์." }, ]); }, 2000), ); } async function fetchRelatedPosts() { return new Promise((resolve) => setTimeout(() => { resolve([ { title: "Next.js ์บ์ฑ ์ ๋ต" }, { title: "React ๋ ๋๋ง ์ต์ ํ" }, ]); }, 1000), ); }
์ ์ฝ๋์์ CommentsSection๊ณผ RelatedPosts ์ปดํฌ๋ํธ๋ ๋น๋๊ธฐ ๋ฐ์ดํฐ ํ์นญ์ ์ํํ๊ณ ์์ด์. ์ด ์ปดํฌ๋ํธ๋ค์ Suspense๋ก ๊ฐ์ธ๋ฉด, Next.js๋ ์ด๊ธฐ ์์ฒญ ์ <h1>๊ณผ <p> ํ๊ทธ๋ก ์ด๋ฃจ์ด์ง ์ ์ ์
ธ์ ๋จผ์ ๋ณด๋ด๊ณ , CommentsSection๊ณผ RelatedPosts์ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋๋ฉด ํด๋น ๋ถ๋ถ์ ์คํธ๋ฆฌ๋ฐํ์ฌ ํด๋ผ์ด์ธํธ์ ์ ์กํด์. ์ฌ์ฉ์๋ "๋๊ธ์ ๋ถ๋ฌ์ค๋ ์ค..."์ด๋ผ๋ fallback UI๋ฅผ ๋ณด๋ค๊ฐ ์ค์ ๋๊ธ์ด ๋ํ๋๋ ๊ฒฝํ์ ํ๊ฒ ๋๋ ๊ฑฐ์ฃ .
2๏ธโฃ unstable_noStore() ๋๋ revalidate ์ต์
์ผ๋ก ๋์ ๋ถ๋ถ ๋ช
์
Next.js๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์บ์ํ๋ ค๊ณ ์๋ํด์. PPR์ ์ ๋๋ก ํ์ฉํ๋ ค๋ฉด, ๋์ ์ผ๋ก ๋ ๋๋ง๋์ด์ผ ํ ๋ถ๋ถ์ด ์บ์๋์ง ์๋๋ก ๋ช
์ํด ์ค ํ์๊ฐ ์์ด์.
-
unstable_noStore(): App Router์์ ์๋ฒ ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ฐ์ดํฐ๋ฅผ ํญ์ ์ต์ ์ํ๋ก ์ ์งํ๊ณ ์ถ์ ๋ ์ฌ์ฉํด์. ์ด ํจ์๊ฐ ํธ์ถ๋๋ ์๊ฐ, ํด๋น ์ปดํฌ๋ํธ์ ๊ทธ ์์ ์ปดํฌ๋ํธ๋ค์ ๋์ ์ผ๋ก ๋ ๋๋ง๋๋๋ก ํ์๋ผ์.import { unstable_noStore } from "next/cache"; async function DynamicContent() { unstable_noStore(); // ์ด ์ปดํฌ๋ํธ๋ ํญ์ ๋์ ์ผ๋ก ๋ ๋๋ง๋๋๋ก ๋ช ์ const data = await fetch("https://api.example.com/dynamic-data", { cache: "no-store", }); const json = await data.json(); return <p>๋์ ๋ฐ์ดํฐ: {json.value}</p>; } -
fetch์cache: 'no-store'๋๋revalidate์ต์ :fetchAPI๋ฅผ ์ฌ์ฉํ ๋,cache: 'no-store'์ต์ ์ ์ฃผ๋ฉด ํด๋น ์์ฒญ์ ๊ฒฐ๊ณผ๊ฐ ์บ์๋์ง ์๊ณ ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ก ํ ์ ์์ด์. ๋ํ,revalidate์ต์ ์ ์ฌ์ฉํ์ฌ ํน์ ์๊ฐ๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ์ฌ๊ฒ์ฆํ๋๋ก ์ค์ ํ ์๋ ์์ด์.async function AlwaysFreshData() { const res = await fetch("https://api.example.com/always-fresh", { cache: "no-store", // ์ด ๋ฐ์ดํฐ๋ ์ ๋ ์บ์ํ์ง ์๊ณ ํญ์ ์ต์ ์ ๊ฐ์ ธ์์. }); const data = await res.json(); return <p>ํญ์ ์ต์ ๋ฐ์ดํฐ: {data.value}</p>; } async function RevalidatedData() { const res = await fetch("https://api.example.com/revalidated-data", { next: { revalidate: 60 }, // 60์ด๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ์ฌ๊ฒ์ฆํด์. }); const data = await res.json(); return <p>60์ด๋ง๋ค ๊ฐฑ์ ๋๋ ๋ฐ์ดํฐ: {data.value}</p>; }
3๏ธโฃ ์ฝ๋ ์์: ๋ธ๋ก๊ทธ ํฌ์คํธ ์์ธ ํ์ด์ง์ PPR ์ ์ฉ
์ค์ ๋ธ๋ก๊ทธ ํฌ์คํธ ์์ธ ํ์ด์ง์ PPR์ ์ ์ฉํ๋ ์๋๋ฆฌ์ค๋ฅผ ์ดํด๋ณผ๊ฒ์. ๊ฒ์๋ฌผ ๋ด์ฉ์ ์ ์ ์ด์ง๋ง, ๋๊ธ ์น์ ์ ๋์ ์ผ๋ก ์ค์๊ฐ ์ ๋ฐ์ดํธ๋์ด์ผ ํ๋ค๊ณ ๊ฐ์ ํด ๋ด์.
// app/posts/[slug]/page.tsx import { Suspense } from "react"; import { unstable_noStore } from "next/cache"; // ๋์ ๋ ๋๋ง์ ๋ช ์ํ๊ธฐ ์ํจ import { ErrorBoundary } from "react-error-boundary"; // ์๋ฌ ํธ๋ค๋ง์ ์ํด // ๊ฐ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ API ํธ์ถ ํจ์ async function getPostBySlug(slug: string) { // ์ค์ ๋ก๋ DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง const posts = { "first-post": { title: "์ฒซ ๋ฒ์งธ ๊ฒ์๋ฌผ", content: "์ด๊ฒ์ ์ฒซ ๋ฒ์งธ ๊ฒ์๋ฌผ์ ๋ด์ฉ์ ๋๋ค.", }, "second-post": { title: "๋ ๋ฒ์งธ ๊ฒ์๋ฌผ", content: "๋ ๋ฒ์งธ ๊ฒ์๋ฌผ ๋ด์ฉ๋ ์์ฃผ ์ค์ํด์.", }, }; await new Promise((resolve) => setTimeout(resolve, 50)); // ๋คํธ์ํฌ ์ง์ฐ ์๋ฎฌ๋ ์ด์ return posts[slug] || null; } async function getCommentsForPost(postId: string) { unstable_noStore(); // ๋๊ธ์ ํญ์ ์ต์ ์ํ์ฌ์ผ ํ๋ฏ๋ก ๋์ ์ผ๋ก ์ฒ๋ฆฌ // ์ค์ ๋ก๋ API ํธ์ถ ๋๋ DB ์ฟผ๋ฆฌ const comments = { "first-post": [ { id: 1, author: "๋ธ๋ฃจ", text: "์ ๋ง ์ ์ตํ ๊ธ์ด์์!" }, { id: 2, author: "๋ ์1", text: "Next.js PPR ์ดํด์ ํฐ ๋์์ด ๋์ด์." }, ], "second-post": [ { id: 3, author: "๋ธ๋ฃจ", text: "๋ ๋ฒ์งธ ๊ฒ์๋ฌผ๋ ์ด์ฌํ ์์ฑํ์ด์." }, ], }; await new Promise((resolve) => setTimeout(resolve, 1500)); // ๋๊ธ ๋ก๋ฉ ์ง์ฐ ์๋ฎฌ๋ ์ด์ return comments[postId] || []; } // ๋๊ธ ์น์ ์๋ฒ ์ปดํฌ๋ํธ async function Comments({ postId }: { postId: string }) { const comments = await getCommentsForPost(postId); if (!comments || comments.length === 0) { return <p>์์ง ๋๊ธ์ด ์์ด์. ์ฒซ ๋๊ธ์ ๋จ๊ฒจ์ฃผ์ธ์!</p>; } return ( <section className="mt-8 rounded-lg bg-gray-50 p-4"> <h2 className="mb-4 text-2xl font-bold">๐ฌ ๋๊ธ</h2> {comments.map((comment) => ( <div key={comment.id} className="mb-3 border-b border-gray-200 pb-3 last:border-b-0" > <p className="font-semibold">{comment.author}</p> <p className="text-gray-700">{comment.text}</p> </div> ))} </section> ); } // ์๋ฌ ๋ฐ์ ์ ๋ณด์ฌ์ค UI function CommentsErrorFallback({ error }: { error: Error }) { return ( <div role="alert" className="rounded-lg border border-red-300 p-4 text-red-600" > <p>โ ๏ธ ๋๊ธ์ ๋ถ๋ฌ์ค๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ด์: {error.message}</p> </div> ); } export default async function PostPage({ params, }: { params: { slug: string }; }) { const post = await getPostBySlug(params.slug); if (!post) { return { notFound: true, }; } return ( <article className="mx-auto max-w-3xl px-4 py-8"> <h1 className="mb-4 text-4xl font-extrabold text-gray-900"> {post.title} </h1> <p className="text-lg leading-relaxed whitespace-pre-wrap text-gray-700"> {post.content} </p> {/* ๋๊ธ ์น์ ์ Suspense์ ErrorBoundary ์ ์ฉ */} <ErrorBoundary fallbackRender={({ error }) => <CommentsErrorFallback error={error} />} > <Suspense fallback={ <div className="mt-8 animate-pulse rounded-lg bg-gray-50 p-4 text-gray-600"> <p>โณ ๋๊ธ์ ๋ถ๋ฌ์ค๋ ์ค์ด์์...</p> </div> } > <Comments postId={params.slug} /> </Suspense> </ErrorBoundary> </article> ); } // generateStaticParams๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ ์์ ์ ์ ์ ๊ฒฝ๋ก ์์ฑ export async function generateStaticParams() { // ์ค์ ๋ก๋ DB์์ ๋ชจ๋ ํฌ์คํธ์ slug๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง return [{ slug: "first-post" }, { slug: "second-post" }]; }
์ ์์์์ PostPage๋ generateStaticParams๋ฅผ ํตํด ๋น๋ ์์ ์ ์ ์ ์
ธ๋ก ์์ฑ๋ ์ ์์ด์. ๊ฒ์๋ฌผ์ ์ ๋ชฉ๊ณผ ๋ด์ฉ์ ์ ์ ์
ธ์ ํฌํจ๋์ด ๋น ๋ฅด๊ฒ ๋ก๋ฉ๋์ฃ . ๋ฐ๋ฉด Comments ์ปดํฌ๋ํธ๋ unstable_noStore()๋ฅผ ์ฌ์ฉํ๊ณ Suspense๋ก ๊ฐ์ธ์ ธ ์๊ธฐ ๋๋ฌธ์, ์ด๊ธฐ์๋ fallback UI๊ฐ ๋ณด์ด๋ค๊ฐ ๋๊ธ ๋ฐ์ดํฐ๊ฐ ๋ก๋ฉ๋๋ฉด ๋์ ์ผ๋ก ์คํธ๋ฆฌ๋ฐ๋์ด ํ์ด์ง์ ๋ํ๋์. ErrorBoundary๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ธ ๋ก๋ฉ ์ค ๋ฐ์ํ ์ ์๋ ์๋ฌ๋ gracefullyํ๊ฒ ์ฒ๋ฆฌํ๊ณ ์์ด์.
๐ก PPR ์ต์ ํ ํ๊ณผ ๊ณ ๋ ค์ฌํญ
0๏ธโฃ Suspense ๊ฒฝ๊ณ์ ์ค์์ฑ
Suspense ๊ฒฝ๊ณ๋ PPR์ ์ฑ๋ฅ์ ๋งค์ฐ ์ค์ํ ์ํฅ์ ๋ฏธ์ณ์. ๋๋ฌด ํฐ ๋จ์๋ฅผ Suspense๋ก ๊ฐ์ธ๋ฉด ๋์ ์ฝํ
์ธ ์ ์์ด ๋ง์์ ธ ์ด๊ธฐ ๋ก๋ฉ ์๋ ์ด์ ์ ์์ ์ ์๊ณ , ๋๋ฌด ์์ ๋จ์๋ฅผ ๊ฐ์ธ๋ฉด ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ ์ ์์ด์. ์ฌ์ฉ์์๊ฒ ์๋ฏธ ์๋ ์ต์ ๋จ์๋ก Suspense๋ฅผ ์ ์ฉํ๋ ๊ฒ์ด ์ค์ํด์.
์๋ฅผ ๋ค์ด, ํ์ด์ง ์ ์ฒด๋ฅผ Suspense๋ก ๊ฐ์ธ๊ธฐ๋ณด๋ค๋, ๋
๋ฆฝ์ ์ผ๋ก ๋ก๋ฉ๋ ์ ์๋ ์์ ฏ์ด๋ ์น์
๋จ์๋ก ๋๋์ด ์ ์ฉํ๋ ๊ฒ์ด ์ข์์.
1๏ธโฃ ๋ฐ์ดํฐ ํ์นญ ์ ๋ต (React cache, fetch ํ์ฅ)
Next.js 13+ App Router์์๋ ์๋ฒ ์ปดํฌ๋ํธ์์ fetch๋ฅผ ์ฌ์ฉํ ๋ ์๋์ผ๋ก ์์ฒญ์ ์บ์ํ๊ณ ์ค๋ณต์ ์ ๊ฑฐํด ์ค์. ํ์ง๋ง PPR ํ๊ฒฝ์์๋ ํน์ ๋ฐ์ดํฐ๊ฐ ํญ์ ์ต์ ์ด์ด์ผ ํ๋์ง, ์๋๋ฉด ์บ์๋์ด๋ ๋๋์ง ๋ช
ํํ ๊ตฌ๋ถํ๋ ๊ฒ์ด ์ค์ํด์.
- ์บ์๊ฐ ํ์ํ ๊ฒฝ์ฐ:
fetch์ ๊ธฐ๋ณธ ๋์์ ์ฌ์ฉํ๊ฑฐ๋,revalidate์ต์ ์ ํตํด ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ ํด์. - ์บ์๊ฐ ํ์ ์๋ ๊ฒฝ์ฐ:
fetch์cache: 'no-store'์ต์ ์ ๋ช ์ํ๊ฑฐ๋,unstable_noStore()๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์ปดํฌ๋ํธ๊ฐ ๋์ ์ผ๋ก ๋ ๋๋ง๋๋๋ก ํด์.
2๏ธโฃ ์๋ฌ ํธ๋ค๋ง (ErrorBoundary)
PPR ํ๊ฒฝ์์๋ ๋์ ์ธ ๋ถ๋ถ์ด ๋น๋๊ธฐ์ ์ผ๋ก ๋ก๋ฉ๋๊ธฐ ๋๋ฌธ์, ํด๋น ๋ถ๋ถ์์ ์๋ฌ๊ฐ ๋ฐ์ํ์ ๋ ์ ์ฒด ํ์ด์ง๊ฐ ๋ง๊ฐ์ง๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ErrorBoundary๋ฅผ ์ ์ ํ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํด์. Suspense์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์๋ฌ ๋ฐ์ ์ ์ฌ์ฉ์์๊ฒ ์๋ฏธ ์๋ fallback UI๋ฅผ ์ ๊ณตํด์ผ ํด์.
3๏ธโฃ SEO์ ์ ๊ทผ์ฑ
PPR์ ์ด๊ธฐ HTML์ ์ ์ ์ฝํ
์ธ ๊ฐ ํฌํจ๋๋ฏ๋ก SEO์ ๋งค์ฐ ์ ๋ฆฌํด์. ๊ฒ์ ์์ง ํฌ๋กค๋ฌ๋ ์ ์ ์
ธ์ ํตํด ํ์ด์ง์ ํต์ฌ ๋ด์ฉ์ ๋น ๋ฅด๊ฒ ์ธ๋ฑ์ฑํ ์ ์์ด์. ๋์ ์ผ๋ก ๋ก๋ฉ๋๋ ์ฝํ
์ธ ๋ ์คํธ๋ฆฌ๋ฐ ๋ฐฉ์์ผ๋ก ์ ์ก๋๋ฏ๋ก, ํฌ๋กค๋ฌ๊ฐ ์ถฉ๋ถํ ๊ธฐ๋ค๋ฆฌ๋ฉด ๋ด์ฉ์ ํ์ฑํ ์ ์๋ต๋๋ค.
ํ์ง๋ง fallback UI๊ฐ ๋๋ฌด ์ค๋ ๋
ธ์ถ๋๊ฑฐ๋, ์ค์ํ ์ฝํ
์ธ ๊ฐ Suspense ๋ค์ ์จ๊ฒจ์ ธ ์์ด ์ด๊ธฐ ๋ก๋ฉ์ด ์ง์ฐ๋๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ ๋ถ์ ์ ์ธ ์ํฅ์ ์ค ์ ์์ผ๋ ์ฃผ์ํด์ผ ํด์.
๐ ์ ๋ฆฌํ๋ฉฐ
0๏ธโฃ PPR์ ๊ฐ๋ ฅํ ์ด์ ์์ฝ
์ค๋ ์ฐ๋ฆฌ๋ Next.js์ ํ์ ์ ์ธ ๊ธฐ๋ฅ์ธ Partial Prerendering (PPR)์ ๋ํด ์์ธํ ์ดํด๋ณด์์ด์. PPR์ ๋ค์๊ณผ ๊ฐ์ ๊ฐ๋ ฅํ ์ด์ ์ ์ ๊ณตํด์.
- ์ด๊ณ ์ ์ด๊ธฐ ๋ก๋ฉ: ์ ์ ์
ธ์ ํตํด ์ฌ์ฉ์์๊ฒ ์ฆ๊ฐ์ ์ธ ํ์ด์ง ๋ก๋ฉ ๊ฒฝํ์ ์ ์ฌํด์.
- ์ค์๊ฐ ๋์ ์ฝํ
์ธ :
Suspense์ ์คํธ๋ฆฌ๋ฐ์ ํตํด ๋์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์ต์ ์ํ๋ก ์ ์งํ๋ฉด์๋ ๋ถ๋๋ฝ๊ฒ ํตํฉํด์. - ์ต์ ์ ์ฌ์ฉ์ ๊ฒฝํ: ๋น ๋ฅธ ์ด๊ธฐ ๋ก๋ฉ๊ณผ ์ต์ ๋ฐ์ดํฐ ์ ๊ณต์ผ๋ก ์ฌ์ฉ์ ๋ง์กฑ๋๋ฅผ ๋์ฌ์.
- ๊ฐ๋ ฅํ SEO: ๊ฒ์ ์์ง ํฌ๋กค๋ฌ๊ฐ ํต์ฌ ์ฝํ
์ธ ๋ฅผ ์ฝ๊ฒ ์ธ๋ฑ์ฑํ ์ ์๋๋ก ๋์์ค์.
- ๊ฐ๋ฐ ํธ์์ฑ: SSR, SSG, ISR์ ์ฅ์ ์ ํ ๋ฒ์ ๋๋ฆฌ๋ฉด์๋ ๋ณต์กํ ์บ์ฑ ์ ๋ต์ ๋จ์ํํ ์ ์์ด์.
1๏ธโฃ ๋ค์ ๋จ๊ณ: ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์ ์ ์ฉํด ๋ณด์ธ์
PPR์ Next.js App Router๋ฅผ ์ฌ์ฉํ๋ ํ๋ก์ ํธ์ ์น ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ํ ๋จ๊ณ ๋์ด์ฌ๋ฆด ์ ์๋ ๋งค์ฐ ํจ๊ณผ์ ์ธ ๋๊ตฌ์์. ํนํ ๋ธ๋ก๊ทธ, ์ด์ปค๋จธ์ค ์์ธ ํ์ด์ง, ๋์๋ณด๋ ๋ฑ ์ ์ ์ธ ๋ ์ด์์ ์์ ๋์ ์ธ ๋ฐ์ดํฐ๊ฐ ํ์ํ ํ์ด์ง์ ๊ฐ๋ ฅํ๊ฒ ์ถ์ฒํด์.
์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ฌ๋ฌ๋ถ์ Next.js ํ๋ก์ ํธ์ Partial Prerendering์ ์ ์ฉํด ๋ณด์๊ณ , ๋์ฑ ๋น ๋ฅด๊ณ ๋ฐ์์ฑ ์ข์ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค์ด๋ณด์๊ธธ ๋ฐ๋ผ์! ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์ธ์. ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ๋ ํญ์ ํจ๊ป ์ฑ์ฅํ๋ ๊ณณ์ด๋๊น์.
๐ฎ ์ฐธ๊ณ
- Next.js Docs: Partial Prerendering
- Next.js Blog: Partial Prerendering in Next.js 14.1
- React Docs: Suspense
- React Docs: Error Boundaries
์ฐ๊ด๋ ํฌ์คํธ
๋จ์ด: 2,181๊ฐ26๋ถ[๐ค] ์น ์ฑ๋ฅ ์ต์ ํ: ๋ฆฌํ๋ก์ฐ์ ๋ฆฌํ์ธํธ ์ต์ํ ์ ๋ต
๋ธ๋ผ์ฐ์ ๋ ๋๋ง ๊ณผ์ ์ ํต์ฌ์ธ ๋ฆฌํ๋ก์ฐ(Reflow)์ ๋ฆฌํ์ธํธ(Repaint)์ ๋ฐ์ ์๋ฆฌ๋ฅผ ์ดํดํ๊ณ , ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ค์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ๊ทน๋ํํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,886๊ฐ19๋ถ
[๐ค] Vitest์ React Testing Library๋ก Next.js ์ปดํฌ๋ํธ ์๋ฒฝ ํ ์คํธํ๊ธฐ
Next.js ํ๋ก์ ํธ์์ Vitest์ React Testing Library๋ฅผ ํ์ฉํ์ฌ UI ์ปดํฌ๋ํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ ์คํธํ๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์ ์ ํจ๊ป ์์ธํ ์์๋ด์. ์ค์ ๋ถํฐ Mocking, ์ด๋ฒคํธ ์๋ฎฌ๋ ์ด์ ๊น์ง, ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์ํ ํ ์คํธ ์ ๋ต์ ์ตํ๋ณด์ธ์.
- ๋จ์ด: 1,541๊ฐ20๋ถ
[๐ค] Tailwind CSS v4 ์ถ์: ๊ฐ๋ฐ์์๊ฒ ์ฐพ์์ฌ ๋ณํ์ ์ต์ ํ ์ ๋ต
Tailwind CSS v4์ ์ฃผ์ ๋ณ๊ฒฝ์ฌํญ๊ณผ ์๋ก์ด ๊ธฐ๋ฅ๋ค์ ๊น์ด ์๊ฒ ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ํจ์จ์ ์ผ๋ก ์ ์ฉํ๋ฉฐ ์ฑ๋ฅ์ ์ต์ ํํ๋ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,563๊ฐ20๋ถ
[๐ค] Next.js Dockerfile ์ต์ ํ: ํ๋ก๋์ ๋ฐฐํฌ๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋
Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ Docker ์ปจํ ์ด๋๋ก ํจ์จ์ ์ผ๋ก ๋ฐฐํฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ๋ฉํฐ์คํ ์ด์ง ๋น๋, ์บ์ฑ ์ ๋ต, ๋ณด์ ์ค์ ๋ฑ ํ๋ก๋์ ํ๊ฒฝ์ ์ต์ ํ๋ Dockerfile ์์ฑ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด ๋๋ ค์.
- ๋จ์ด: 2,161๊ฐ24๋ถ
[๐ค] CSS Grid ์ฌํ ๊ฐ์ด๋: ์ค์ ๋ ์ด์์ ํจํด๊ณผ ๋ฐ์ํ ๋์์ธ ์ ๋ต
CSS Grid๋ ๊ฐ๋ ฅํ 2์ฐจ์ ๋ ์ด์์ ์์คํ ์ด์์. ์ด ๊ฐ์ด๋์์ Grid์ ํต์ฌ ๊ฐ๋ ๋ถํฐ ์ค์ ๋ ์ด์์ ํจํด, ๋ฐ์ํ ๋์์ธ ์ ๋ต๊น์ง ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด ์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋๋ก ๋์๋๋ ค์.
- ๋จ์ด: 1,716๊ฐ19๋ถ
[๐ค] TypeScript ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ : ๋ฌธ์์ด ํ์ ์ ๋ง๋ฒ์ฌ๋ก ๋ณ์ ํ๊ธฐ
TypeScript์ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ ํ์ฉํ์ฌ ๋ณต์กํ ๋ฌธ์์ด ํจํด์ ์์ ํ๊ฒ ํ์ ์ถ๋ก ํ๊ณ , ๊ฐ๋ ฅํ ์ ํธ๋ฆฌํฐ ํ์ ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์ ์ ํจ๊ป ์์ธํ ์์๋ด์. ํ์ ์์ ์ฑ์ ํ ๋จ๊ณ ๋์ฌ ๊ฐ๋ฐ ๊ฒฝํ์ ๊ฐ์ ํด ๋ณด์ธ์.
- ๋จ์ด: 1,917๊ฐ23๋ถ
[๐ค] JavaScript WeakMap๊ณผ WeakSet: ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง์ ์ต์ ํ ์ ๋ต
JavaScript์์ WeakMap๊ณผ WeakSet์ ํ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๊ณ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์ ์๋ฆฌ์ ํจ๊ป ๊ฐ์ฒด ์ฐธ์กฐ ๊ด๋ฆฌ์ ์ค์์ฑ์ ์ดํดํ๊ณ , ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ์ ๋ต์ ๋ฐฐ์๋ด์.
- ๋จ์ด: 1,446๊ฐ17๋ถ
[๐ค] Next.js/React ํ๋ก์ ํธ๋ฅผ ์ํ ESLint & Prettier ์ค์ ์๋ฒฝ ๊ฐ์ด๋
๋ณต์กํ Next.js ๋ฐ React ํ๋ก์ ํธ์์ ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ๊ณผ ํ์ง์ ์ ์งํ๋ ESLint์ Prettier ์ค์ ๋ฐฉ๋ฒ์ ์์ธํ ์๋ ค๋๋ ค์. ํ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ต์ ํ๋ ์ค์ ์ผ๋ก ๊ฐ๋ฐ ํจ์จ์ ๋์ฌ๋ณด์ธ์.
- ๋จ์ด: 2,006๊ฐ25๋ถ
[๐ค] JavaScript์ ํต์ฌ: ํ๋กํ ํ์ ์ฒด์ธ ์๋ฒฝ ์ดํด์ ํ์ฉ ์ ๋ต
JavaScript์ ์ฌ์ฅ๋ถ, ํ๋กํ ํ์ ์ฒด์ธ์ ๋์ ์๋ฆฌ๋ฅผ ๊น์ด ํ๊ณ ๋ค์ด ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ๊ณผ ์์์ ์๋ฒฝํ๊ฒ ์ดํดํ๊ณ ์ค๋ฌด์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ์ฑ๋ฅ ์ต์ ํ ํ๋ ํจ๊ป ๋ค๋ค์.
- ๋จ์ด: 2,087๊ฐ22๋ถ
[๐ค] React ์ปค์คํ ํ : ์ฌ์ฌ์ฉ์ฑ ๋์ด๋ ์ค๊ณ ์์น๊ณผ ํ ์คํธ ์ ๋ต
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๊ทน๋ํํ๋ ์ปค์คํ ํ ์ค๊ณ ์์น, ์ค์ฉ์ ์ธ ํจํด, ๊ทธ๋ฆฌ๊ณ ๊ฒฌ๊ณ ํ ํ ์คํธ ์ ๋ต์ ์๋์ด ๊ฐ๋ฐ์์ ๊ด์ ์์ ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,107๊ฐ23๋ถ
[๐ค] React useRef ํ ์ฌ์ธต ๋ถ์: DOM ๋์ด์ ์ค์ ํ์ฉ ์ ๋ต
React useRef ํ ์ ๊ธฐ๋ณธ ์๋ฆฌ๋ถํฐ DOM ์์ ์ง์ ์ ์ด, ์ปดํฌ๋ํธ ๋ผ์ดํ์ฌ์ดํด ๊ด๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ๋ค์ํ ์ค์ ํ์ฉ ์ ๋ต๊น์ง ์ฌ์ธต์ ์ผ๋ก ๋ค๋ค์. ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ useRef ์๋ฒฝ ๊ฐ์ด๋.
- ๋จ์ด: 1,762๊ฐ19๋ถ
[๐ค] Next.js 14/15์์ ๋์ OG ์ด๋ฏธ์ง ์์ฑ: ImageResponse ์๋ฒฝ ๊ฐ์ด๋
Next.js App Router ํ๊ฒฝ์์ ImageResponse๋ฅผ ํ์ฉํ์ฌ ๋์ OG ์ด๋ฏธ์ง๋ฅผ ํจ์จ์ ์ผ๋ก ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. SEO์ ์์ ๊ณต์ ์ต์ ํ๋ฅผ ์ํ ์ค์ ๊ฐ์ด๋์ ๋๋ค.
- ๋จ์ด: 1,481๊ฐ18๋ถ
[๐ค] Git ๋ธ๋์น ์ ๋ต: Git Flow vs GitHub Flow, ์ค๋ฌด์์ ์ด๋ป๊ฒ ์ ํํ๊ณ ์ด์ํ ๊น์?
๊ฐ๋ฐํ์ ํจ์จ์ ์ธ ํ์ ์ ์ํ Git ๋ธ๋์น ์ ๋ต์ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Git Flow์ GitHub Flow์ ํต์ฌ ๊ฐ๋ ๋ถํฐ ์ฅ๋จ์ , ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ ํ์ ๋ง๋ ์ ๋ต์ ์ ํํ๊ณ ์ด์ํ๋ ์ค์ง์ ์ธ ํ๊น์ง '๋ธ๋ฃจ'๊ฐ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,442๊ฐ16๋ถ
[๐ค] TypeScript ํ์ ๊ฐ๋: ๋ฐํ์ ํ์ ์์ ์ฑ์ ์ํ ํ์ ํจํด ์ ๋ณตํด์
TypeScript์์ ๋ฐํ์์ ๋ณ์์ ํ์ ์ ์์ ํ๊ฒ ์ขํ๋(Narrowing) ๋ฐฉ๋ฒ์ธ ํ์ ๊ฐ๋(Type Guard)์ ๋ํด ์์ธํ ์์๋ด์. `typeof`, `instanceof`, `in` ์ฐ์ฐ์๋ถํฐ ์ฌ์ฉ์ ์ ์ ํ์ ๊ฐ๋๊น์ง, ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ๊ฒฌ๊ณ ํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 2,542๊ฐ28๋ถ
[๐ค] React Query (TanStack Query) ์ฌํ: ๋ฐ์ดํฐ ํ์นญ, ์บ์ฑ, ๋๊ธฐํ ์ ๋ต์ผ๋ก ์น ์ฑ ์ฑ๋ฅ ๊ทน๋ํํด์
React Query (TanStack Query)๋ฅผ ํ์ฉํ์ฌ ๋ณต์กํ ์๋ฒ ์ํ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ง๋ฅ์ ์ธ ์บ์ฑ๊ณผ ์๋ ๋๊ธฐํ ์ ๋ต์ผ๋ก ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. useQuery, useMutation, useInfiniteQuery ๋ฑ ํต์ฌ ํ ๊ณผ ์ค์ ์ต์ ํ ํ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 2,401๊ฐ26๋ถ
[๐ค] React `useTransition`๊ณผ `useDeferredValue`๋ก ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ
React ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฌด๊ฑฐ์ด UI ์ ๋ฐ์ดํธ๋ก ์ธํ ๋ฒ๋ฒ ์์ ํด๊ฒฐํ๊ณ , `useTransition`๊ณผ `useDeferredValue` ํ ์ ํ์ฉํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ํ๊ธฐ์ ์ผ๋ก ๊ฐ์ ํ๋ ์ค์ฉ์ ์ธ ์ ๋ต์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,917๊ฐ22๋ถ
[๐ค] React Suspense์ ErrorBoundary: ๊ฒฌ๊ณ ํ๊ณ ๋ถ๋๋ฌ์ด UI ๊ฒฝํ์ ์ํ ์ค์ ๊ฐ์ด๋
React ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ์ ๊ฒฝํ์ ํ์ ํ Suspense์ ErrorBoundary์ ๊ฐ๋ ฅํ ์กฐํฉ์ ๊น์ด ์๊ฒ ๋ค๋ค์. ๋ก๋ฉ ์ํ์ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ฐ์ํ๊ฒ ๊ด๋ฆฌํ์ฌ ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ๋ถ๋๋ฌ์ด UI๋ฅผ ๋ง๋๋ ์ค์ ํ๊ณผ ์ฝ๋ ์์๋ฅผ ํ์ธํด ๋ณด์ธ์.
- ๋จ์ด: 1,302๊ฐ16๋ถ
[๐ค] CSS Container Queries: ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ๋ฐ์ํ ๋์์ธ์ ์๋ก์ด ์งํ
๋ฏธ๋์ด ์ฟผ๋ฆฌ์ ํ๊ณ๋ฅผ ๋์ด, ์ปดํฌ๋ํธ ์์ฒด์ ํฌ๊ธฐ์ ๋ฐ๋ผ ์คํ์ผ์ ์กฐ์ ํ๋ CSS Container Queries๋ฅผ ๊น์ด ์๊ฒ ์์๋ณด๊ณ ์ค๋ฌด ์ ์ฉ ๋ฐฉ๋ฒ์ ์๋ดํด ๋๋ ค์.
- ๋จ์ด: 1,681๊ฐ19๋ถ
[๐ค] Next.js 15 ๊ณ ๊ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต: fetch์ revalidate ์ฌ์ธต ๋ถ์
Next.js 15์์ `fetch` API์ ๊ฐ๋ ฅํ ์บ์ฑ ๋ฉ์ปค๋์ฆ๊ณผ `revalidate` ์ต์ ์ ํ์ฉํ์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ต์ ํํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. ์ค๋ฌด ์์๋ฅผ ํตํด ์๋ฒ ์ปดํฌ๋ํธ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์บ์ฑ ์ ๋ต์ ํจ๊ณผ์ ์ผ๋ก ์ ์ฉํ๋ ํ์ ์ ๊ณตํด์.
๋จ์ด: 1,320๊ฐ14๋ถ[๐ค] Next.js App Router: generateStaticParams๋ก ๋์ ๋ผ์ฐํ ๋น๋ ์ต์ ํํ๊ธฐ
Next.js App Router์์ generateStaticParams ํจ์๋ฅผ ํ์ฉํ์ฌ ๋์ ๋ผ์ฐํ ์ ์ ์ ํ์ด์ง๋ฅผ ํจ์จ์ ์ผ๋ก ์์ฑํ๊ณ ๋น๋ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์.
๋จ์ด: 1,891๊ฐ22๋ถ[๐ค] React ๋ ๋๋ง ์ต์ ํ: useMemo, useCallback, React.memo ์๋ฒฝ ๊ฐ์ด๋
์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ React ๋ ๋๋ง ์ต์ ํ ๊ฐ์ด๋. useMemo, useCallback, React.memo์ ์ ํํ ์ฌ์ฉ๋ฒ๊ณผ ์ค๋ฌด์์ ํํ ์ ์ง๋ฅด๋ ์ค์, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ฑ๋ฅ ํฅ์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.
๋จ์ด: 2,145๊ฐ24๋ถ[๐ค] JavaScript Proxy์ Reflect ์ฌ์ธต ๋ถ์: ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์ฝ๋ ๊ฐํํ๊ธฐ
JavaScript Proxy์ Reflect API๋ฅผ ํ์ฉํ ๋ฉํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์ฌ์ธต ๋ถ์ํด์. ๊ฐ์ฒด ์ ๊ทผ ์ ์ด, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ก๊น , ๋ฐ์ํ ์์คํ ๊ตฌํ ๋ฑ ์ค์ฉ์ ์ธ ํ์ฉ ์ฌ๋ก๋ฅผ ํตํด ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 2,029๊ฐ24๋ถ
[๐ค] React/Next.js ๋ฒ๋ค ์ต์ ํ: ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ์๋ฒฝ ๊ฐ์ด๋
React์ Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ด๊ณ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํ๋ ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ๊ธฐ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์นํฉ ์ค์ ๋ถํฐ React.lazy, Next.js dynamic import๊น์ง ๋ค๋ค์.
- ๋จ์ด: 1,770๊ฐ20๋ถ
[๐ค] React์ `useOptimistic` ํ ์ผ๋ก ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ตฌํํ๊ธฐ: Server Actions์ ํจ๊ป
React 18/19์ `useOptimistic` ํ ์ ํ์ฉํ์ฌ Server Actions์ ์ฐ๋๋๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
๋จ์ด: 1,561๊ฐ17๋ถ[๐ค] TypeScript const Type Parameters: ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ๊ฐํ์ ์ค์ฉ์ ์ธ ํ์ฉ๋ฒ
TypeScript 5.0์ ๋์ ๋ const Type Parameters๋ฅผ ํ์ฉํ์ฌ ์ ๋ค๋ฆญ ํจ์์ ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ์ ์ ๊ตํ๊ฒ ์ ์ดํ๊ณ , ๋์ฑ ๊ฒฌ๊ณ ํ ํ์ ์์คํ ์ ๊ตฌ์ถํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. as const์์ ์ฐจ์ด์ ๊ณผ ์ค์ ์ฝ๋ ์์๋ฅผ ํตํด ์ด์ค๊ธ ๊ฐ๋ฐ์๋ ์ฝ๊ฒ ์ดํดํ ์ ์๋๋ก ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,028๊ฐ22๋ถ
[๐ค] Next.js/React ์ฑ CLS ์ต์ ํ: ์ํํธ ์๋ ์ฌ์ฉ์ ๊ฒฝํ ๋ง๋ค๊ธฐ
Next.js์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ Cumulative Layout Shift(CLS) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์์๋ณด์ธ์. ์น ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ ์์์ธ CLS๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,740๊ฐ21๋ถ
[๐ค] Next.js SSR, SSG, ISR ๋ ๋๋ง ์ ๋ต: App Router์์ ์ต์ ์ ์ ํ์?
Next.js App Router์์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR), ์ ์ ์ฌ์ดํธ ์์ฑ(SSG), ์ฆ๋ถ ์ ์ ์ฌ์์ฑ(ISR) ๊ฐ ๋ ๋๋ง ์ ๋ต์ ๋์ ์๋ฆฌ, ์ฅ๋จ์ , ์ค์ ํ์ฉ ๋ฐ ์ต์ ํ ๋ฐฉ๋ฒ์ ๋น๊ต ๋ถ์ํด๋๋ ค์.
- ๋จ์ด: 1,478๊ฐ17๋ถ
[๐ค] React Context API์ Zustand: ์ ์ญ ์ํ ๊ด๋ฆฌ, ์ธ์ ๋ฌด์์ ์จ์ผ ํ ๊น์?
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Context API์ ๊ฐ๋ฒผ์ด ์ธ๋ถ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Zustand๋ฅผ ๋น๊ต ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ๊ฐ ๋๊ตฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๋ ์ ๋ต์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,004๊ฐ24๋ถ
[๐ค] Turborepo๋ก Next.js ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์ถ: ํจ์จ์ ์ธ ๊ฐ๋ฐ ๋ฐ ์ต์ ํ ์ ๋ต
Turborepo๋ฅผ ํ์ฉํ์ฌ Next.js ํ๋ก์ ํธ๋ฅผ ๋ชจ๋ ธ๋ ํฌ๋ก ๊ตฌ์ฑํ๊ณ , ๊ณต์ ์ปดํฌ๋ํธ, ์ ํธ๋ฆฌํฐ, CI/CD ์ต์ ํ ๋ฐฉ์์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,338๊ฐ27๋ถ
[๐ค] React useEffect ํ , ์ด์ ํท๊ฐ๋ฆฌ์ง ๋ง์ธ์! (์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ์๋ฒฝ ๊ฐ์ด๋)
React ๊ฐ๋ฐ์์ ํ์์ ์ธ useEffect ํ ์ ๋์ ์๋ฆฌ๋ถํฐ ์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ํจ์ ํ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด์์ ์์ฃผ ๊ฒช๋ ์ค์์ ์ต์ ํ ์ ๋ต๊น์ง, ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด์.
- ๋จ์ด: 3,284๊ฐ31๋ถ
[๐ค] Next.js Server Actions ์ค์ : ์๋ฌ ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋๊ด์ UI ์ ๋ฐ์ดํธ
Next.js Server Actions๋ฅผ ์ค๋ฌด์ ์ ์ฉํ ๋ ๋ง์ฃผํ๋ ์๋ฌ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ธฐ๋ฒ์ ์์ธํ ์ฝ๋ ์์์ ํจ๊ป ์์๋ณด์ธ์.
๋จ์ด: 1,983๊ฐ21๋ถ[๐ค] TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์๋ฒฝ ๊ฐ์ด๋: ์ค์ ํ์ฉ ํจํด
TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์ ํต์ฌ ๊ฐ๋ ๊ณผ ์ค์ ํ์ฉ๋ฒ์ ๊น์ด ์๊ฒ ๋ค๋ค์. Pick, Omit, Partial, Required ๋ฑ ์์ฃผ ์ฐ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ผ๋ก ๋ณต์กํ ํ์ ์ ํจ๊ณผ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ํ์ ์คํฌ๋ฆฝํธ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
- ๋จ์ด: 1,712๊ฐ20๋ถ
[๐ค] Next.js App Router ๋ฏธ๋ค์จ์ด: ๊ฐ๋ ฅํ ์์ฒญ ์ฒ๋ฆฌ ์ ๋ต๊ณผ ์ค์ ์์
Next.js App Router ํ๊ฒฝ์์ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํด ์ฌ์ฉ์ ์ธ์ฆ, ๋ฆฌ๋ค์ด๋ ์ , ๊ตญ์ ํ ๋ฑ์ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง์ ํจ์จ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ ์์ ์ ํจ๊ป ์์ธํ ์์๋ณด์ธ์.
- ๋จ์ด: 1,630๊ฐ19๋ถ
[๐ค] ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ ์ฌํ: ์ค์ฉ์ ์ธ ํจํด๊ณผ ํํ ์คํด๋ค
ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ(Generics)์ ๊น์ด ์ดํดํ๊ณ , ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ ๋ค๋ฆญ ํจํด๊ณผ ํํ ๊ฒช๋ ์คํด๋ค์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์ฝ๊ณ ๋ช ํํ๊ฒ ์ค๋ช ํด ๋๋ ค์. ํ์ ์์ ์ฑ๊ณผ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
๋จ์ด: 1,860๊ฐ18๋ถ[๐ค] Next.js Route Handler: App Router์์ ์์ ํ๊ณ ํจ์จ์ ์ธ API ๊ตฌ์ถํ๊ธฐ (์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ ํฌํจ)
Next.js App Router์ Route Handler๋ฅผ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์บ์ฑ ์ ๋ต์ ํฌํจํ ์ค์ฉ์ ์ธ ํ์ผ๋ก ์์ ํ๊ณ ํจ์จ์ ์ธ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 1,934๊ฐ22๋ถ
[๐ค] Next.js Image ์ปดํฌ๋ํธ ์ต์ ํ: Core Web Vitals ๊ฐ์ ๋ถํฐ ์ค์ ํ์ฉ๊น์ง
Next.js์ Image ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ์น ์ฑ๋ฅ ํต์ฌ ์งํ์ธ Core Web Vitals๋ฅผ ๊ฐ์ ํ๊ณ , ๋ค์ํ ์ต์ ํ ์ต์ ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,792๊ฐ19๋ถ
[๐ค] TypeScript ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋: ๋ณต์กํ ํ์ ๋ ์์ฝ๊ฒ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ
TypeScript ๊ฐ๋ฐ์์ ๋ง์ฃผํ๋ ๋ณต์กํ ํ์ ์ถ๋ก ๋ฌธ์ , ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋๋ฅผ ํ์ฉํ๋ฉด ํจ์ฌ ์ฐ์ํ๊ณ ๊ฐ๋ ฅํ๊ฒ ํด๊ฒฐํ ์ ์์ด์. ์ค์ ์์ ์ ํจ๊ป ๊ทธ ํ์ฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ค๋ด ๋๋ค.
- ๋จ์ด: 1,705๊ฐ21๋ถ
[๐ค] JavaScript ์ด๋ฒคํธ ๋ฃจํ(Event Loop) ์์ ์ ๋ณต: ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ฐํ์ ๋์ ์๋ฆฌ
JavaScript์ ํต์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ธ ์ด๋ฒคํธ ๋ฃจํ์ ๋์ ์๋ฆฌ๋ฅผ ์ฌ๋ ์๊ฒ ํํค์ณ ๋ด์. ์ฝ ์คํ, ํ์คํฌ ํ, ๋ง์ดํฌ๋กํ์คํฌ ํ์์ ์ํธ์์ฉ์ ์ดํดํ๊ณ , ์ค๋ฌด์์ ๋ง์ฃผ์น๋ ๋น๋๊ธฐ ์ฝ๋์ ๋์์ ๋ช ํํ ์์ธกํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,964๊ฐ23๋ถ
[๐ค] Next.js Server & Client Components, ์ค์ ์์ ํ๋ช ํ๊ฒ ์ ํํ๋ ๊ฐ์ด๋
Next.js App Router์์ Server Components์ Client Components ์ค ์ด๋ค ๊ฒ์ ์ฌ์ฉํด์ผ ํ ์ง ๊ณ ๋ฏผ์ด์ ๊ฐ์? ์ด ๊ธ์์ ๋ ์ปดํฌ๋ํธ์ ํต์ฌ ์ฐจ์ด์ , ์ฌ์ฉ ์์ , ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ์ค์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ฆด๊ฒ์.
- ๋จ์ด: 1,879๊ฐ21๋ถ
[๐ค] TypeScript satisfies ์ฐ์ฐ์: ํ์ ์ถ๋ก ๊ณผ ์์ ์ฑ์ ๋์์ ์ก๋ ๋น๋ฒ
TypeScript์ `satisfies` ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ ํ์ ์ถ๋ก ์ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์๊ฒฉํ ํ์ ์์ ์ฑ์ ํ๋ณดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ์ค์ฉ์ ์ธ ์์๋ฅผ ํตํด ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํฉ๋๋ค.
- ๋จ์ด: 1,211๊ฐ15๋ถ
[๐ค] React 19 ์๋ก์ด ๊ธฐ๋ฅ: use ํ , Actions, ๊ทธ๋ฆฌ๊ณ ์ปดํ์ผ๋ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
React 19์ ํต์ฌ ๋ณ๊ฒฝ ์ฌํญ์ธ use ํ , ์๋ฒ ์ก์ , ๊ทธ๋ฆฌ๊ณ React ์ปดํ์ผ๋ฌ์ ๋์ ๋ฐฐ๊ฒฝ๊ณผ ์ค์ ํ์ฉ ์์๋ฅผ ์ด์ค๊ธ ๊ฐ๋ฐ์ ๋๋์ด์ ๋ง์ถฐ ์์ธํ ์ค๋ช ํฉ๋๋ค. ์ต์ React ์ ๋ฐ์ดํธ๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ๊ณผ ๊ฐ๋ฐ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
- ๋จ์ด: 1,524๊ฐ16๋ถ
[๐ค] Next.js App Router ์บ์ฑ ์ ๋ต: ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ (revalidatePath, revalidateTag) ์๋ฒฝ ๊ฐ์ด๋
Next.js 14 App Router์์ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต๊ณผ revalidatePath, revalidateTag๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ณด๊ณ ์น ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.