์๋ฒ์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ, ์ด๋ป๊ฒ ํด๋ผ์ด์ธํธ์์๋ ๊ทธ๋๋ก ์ธ๊น์?
"์๋ก๊ณ ์นจํ ๋๋ง๋ค ๋ฐ์ดํฐ๊ฐ ๊น๋นก๊ฑฐ๋ฆฌ๋์?"
Next.js ๊ฐ์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR) ํ๊ฒฝ์ ์ฐ๋๋ฐ๋, ํ๋ฉด์ด ๋ฐ ๋ '๋ก๋ฉ
์ค...'์ด ๋ณด์ธ๋ค๋ฉด ๋ญ๊ฐ ์๋ชป๋ ๊ฒ์
๋๋ค.
์๋ฒ์์ ํ๋ค๊ฒ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์๋๋ฐ, ๋ธ๋ผ์ฐ์ ๊ฐ ๊ทธ๊ฑธ ๋ชจ๋ฅด๊ณ ๋ค์
๊ฐ์ ธ์ค๋ ค ํ๊ธฐ ๋๋ฌธ์ด์ฃ .
์ด ๋นํจ์จ์ ํด๊ฒฐํ๋ ํต์ฌ ์ด์ , ๋ฐ๋ก
Hydration(์๋ถ ๊ณต๊ธ)์ ๋ํด ์์ฃผ ์ฝ๊ฒ ์๋ ค๋๋ฆฝ๋๋ค.
๋ง์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ค์ด React Query๋ฅผ SSR๊ณผ ํจ๊ป ์ฌ์ฉํ ๋
๋ฐ์ดํฐ ๋๊ธฐํ ๋ฌธ์ ๋ก ๊ณจ๋จธ๋ฆฌ๋ฅผ ์์ต๋๋ค.
์ค๋ ์ด ๊ธ์ ํตํด ์๋ฒ์ ์ํ๋ฅผ ํด๋ผ์ด์ธํธ์ '๋ฌผ ํ๋ฅด๋ฏ' ์ ๋ฌํ๋ ์ ๋ต์
๋ง์คํฐํด ๋ด
์๋ค.
๐ค 1. Dehydration & Hydration์ด ๋ญ๊ฐ์?
์ฉ์ด๊ฐ ๋๋ฌด ์ด๋ ต์ฃ ? ์ฌ์ด ๋น์ ๋ก ์ดํดํด ๋ด
์๋ค.
์ฐ๋ฆฌ๊ฐ ์บ ํ์ ๊ฐ๋ค๊ณ ์์ํด ๋ณด๊ฒ ์ต๋๋ค.
๐ ์ปต๋ผ๋ฉด ๋น์ (The Cup Noodle Analogy)
-
1. ์๋ฒ (๊ณต์ฅ):
๋ง์๋ ๋ผ๋ฉด(๋ฐ์ดํฐ)์ ๋์ฌ์ ์๋ถ์ ์ซ ๋บ๋๋ค. ์ด๋ฅผ Dehydration(๊ฑด์กฐ/์๋ถ ์ ๊ฑฐ)์ด๋ผ๊ณ ํฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ๊ฐ๋ณ๊ฒ ํฌ์ฅํด์ ๋ธ๋ผ์ฐ์ ๋ก ๋ฐฐ์กํ๊ธฐ ์ข์ต๋๋ค.
-
2. ์ด๋ (HTML ์ ์ก):
๊ฑด์กฐ๋ ๋ผ๋ฉด(์ง๋ ฌํ๋ JSON ๋ฐ์ดํฐ)์ด HTML์ ๋ด๊ฒจ ๋ธ๋ผ์ฐ์ ๋ก ๋ ์๊ฐ๋๋ค.
-
3. ํด๋ผ์ด์ธํธ (๋ธ๋ผ์ฐ์ ):
๋ธ๋ผ์ฐ์ ๋ ๋ฐ์๋ง์ ๋จ๊ฑฐ์ด ๋ฌผ์ ๋ถ์ด ๋จน์ ์ ์๋ ์ํ๋ก ๋ง๋ญ๋๋ค.
์ด๊ฒ์ด ๋ฐ๋ก Hydration(์๋ถ ๊ณต๊ธ)์ ๋๋ค.
์ฆ, ์๋ฒ์์ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ
"์ผ๋ ค๋์๋ค๊ฐ" ํด๋ผ์ด์ธํธ์์
"๋
น์ฌ์" ๋ฐ๋ก ์ฌ์ฉํ๋ ๊ธฐ์ ์
๋๋ค.
⚔️ 2. ๋ ๊ฐ์ง ํต์ฌ ์ ๋ต: initialData vs Hydration
React Query์์ SSR์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
์ํฉ์ ๋ง์ถฐ ์ ์ ํ ๋ฐฉ๋ฒ์ ์ ํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
์ ๋ต A: `initialData` ์ฌ์ฉํ๊ธฐ
๊ฐ์ฅ ๋จ์ํ ๋ฐฉ๋ฒ์
๋๋ค. ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ปดํฌ๋ํธ์
props๋ก ๋๊ฒจ์ฃผ๊ณ , ๊ทธ๊ฑธ useQuery์ ์ด๊ธฐ๊ฐ์ผ๋ก
๋ฃ๋ ๋ฐฉ์์
๋๋ค.
๐ ๋จ์ : ์ปดํฌ๋ํธ ๊น์ด๊ฐ ๊น์ด์ง๋ฉด ๋ฐ์ดํฐ ์ ๋ฌ(Prop Drilling)์ด ํ๋ญ๋๋ค.
์ ๋ต B: Hydration API ์ฌ์ฉํ๊ธฐ (๊ถ์ฅ)
Next.js์ dehydrate์ HydrationBoundary๋ฅผ
์ฌ์ฉํ๋ ํ์ค ๋ฐฉ์์
๋๋ค.
React Query๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํด ์ฃผ๋ฏ๋ก Props๋ฅผ ๊ณ์ ๋๊ฒจ์ค ํ์๊ฐ
์์ต๋๋ค.
| ๊ตฌ๋ถ | initialData ๋ฐฉ์ | Hydration ๋ฐฉ์ (์ถ์ฒ) |
|---|---|---|
| ๋ณต์ก๋ | ๋ฎ์ (์ฌ์) | ์ค๊ฐ (์ค์ ํ์) |
| ๋ฐ์ดํฐ ๊ด๋ฆฌ | ์ปดํฌ๋ํธ ๋จ์ | ์ ์ญ ๊ด๋ฆฌ (Cache) |
| ์ฌ์ฉ์ฒ | ๋จ์ํ ํ์ด์ง | ๋ณต์กํ ์ฑ / ๊น์ ๊ตฌ์กฐ |
๐ป 3. Hydration ํจํด ์ค์ ๊ตฌํ (Next.js 14 App Router ๊ธฐ์ค)
์ต์ Next.js ํ๊ฒฝ์์ ๊ฐ์ฅ ๊ถ์ฅ๋๋ ์๋ฒ ์ปดํฌ๋ํธ(Server Component)์์์ ๊ตฌํ ํ๋ฆ์ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
// app/posts/page.tsx (์๋ฒ ์ปดํฌ๋ํธ)
export default async function PostsPage() {
// 1. ์๋ฒ ์ ์ฉ QueryClient ์์ฑ
const queryClient = new QueryClient();
// 2. ์๋ฒ์์ ๋ฏธ๋ฆฌ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (prefetch)
await queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: getPosts,
});
return (
// 3. ๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํ(Dehydrate)ํ์ฌ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ
<HydrationBoundary state={dehydrate(queryClient)}>
<PostsList />
</HydrationBoundary>
);
}
⚠️ ์ฃผ์์ฌํญ: makeQueryClient
์๋ฒ ์ปดํฌ๋ํธ์์๋ ์์ฒญ๋ง๋ค ์๋ก์ด QueryClient๋ฅผ
์์ฑํด์ผ ํฉ๋๋ค.
์ฑ๊ธํค(์ ์ญ ๋ณ์)์ผ๋ก ๋ง๋ค๋ฉด ์ฌ๋ฌ ์ฌ์ฉ์์ ๋ฐ์ดํฐ๊ฐ ์์ด๋ ๋์ฐํ ์ฌ๊ณ ๊ฐ
๋ฐ์ํ ์ ์์ต๋๋ค!
๐ ๋ง์น๋ฉฐ
์ฌ๋ฌ๋ถ์ ๋ฐ์ดํฐ๊ฐ ์๋ฒ์์ ํด๋ผ์ด์ธํธ๊น์ง ์์ ํ๊ฒ ๋ฐฐ์ก๋๊ธธ ์์ํฉ๋๋ค!

๋๊ธ ์ฐ๊ธฐ