๐Ÿš€ 10. Next.js SSR/SSG ํ™˜๊ฒฝ์˜ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์ฃผ์ž… (Hydration)

2026๋…„ 3์›” 4์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

์„œ๋ฒ„๊ฐ€ ๋ฏธ๋ฆฌ ๊ตฌ์›Œ์ค€ ๋ฐ์ดํ„ฐ ์บ์‹œ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์บ์‹œ๋กœ ์–ด๋–ป๊ฒŒ ์•ˆ์ „ํ•˜๊ฒŒ ๋„˜๊ฒจ์ฃผ์–ด(Hydration), ํด๋ผ์ด์–ธํŠธ์˜ ๋ถˆํ•„์š”ํ•œ ์Šคํ”ผ๋„ˆ๋ฅผ ์™„๋ฒฝ ์‚ญ์ œํ•˜๋Š”์ง€ ๋ฐฐ์›๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ

"์˜์ฒ  ๋‹˜, ์„œ๋ฒ„ ๋ Œ๋”๋ง(SSR) ๋‹ค ํ•ด๋†“๊ณ  ์™œ ๋ธŒ๋ผ์šฐ์ € ์ผœ์ง€์ž๋งˆ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋˜ Fetch ํ•˜๋ ค๊ณ  ๋น„๋น„์ ๋Œ€๋‚˜์š”? ์ˆ˜ํ™”(Hydration)๊ฐ€ ๋Š๊ฒผ์ž–์•„์š”!"

โ˜•๏ธ ์˜์ฒ ์ด์˜ ๊ณ ๋ฏผ: "๋ถ„๋ช…ํžˆ Next.js๊ฐ€ ์งฑ ๋น ๋ฅด๋‹ค ํ–ˆ๋Š”๋ฐ..."

(๊ธˆ์š”์ผ ์•„์นจ, ํฌ๋กฌ ํผํฌ๋จผ์Šค ํƒญ์„ ์ผœ๊ณ  ์ ˆ๋งํ•˜๋Š” ์˜์ฒ )

๐Ÿฃ ์˜์ฒ : ์•„๋‹ˆ ๋ฆฌ๋“œ ๋‹˜! ์ € ์–ด์ œ ๋ฐฐ์šด ๋Œ€๋กœ Next.js App Router ์…‹์—… ๋‹ค ๋๋ƒˆ๊ฑฐ๋“ ์š”? ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ fetch ์น˜๋‹ˆ๊นŒ ํ™”๋ฉด์ด 0์ดˆ ๋งŒ์— ๊ฒ๋‚˜ ๋นก๋นกํ•˜๊ฒŒ ๋ Œ๋”๋ง๋ผ์„œ ์œ ์ €ํ•œํ…Œ HTML ๋”ฑ ๋–จ์–ด์ง€๋Š” ๊ฑฐ ์™„์ „ ๊ฐ๋™ํ–ˆ์–ด์š”!

๐Ÿฆ ์˜ํ˜ธ: ๊ทธ๋Ÿผ ์„ฑ๊ณต์ ์ธ๋ฐ์š”? ๋ญ๊ฐ€ ๋ถˆ๋งŒ์ด์‹œ์ฃ ?

๐Ÿฃ ์˜์ฒ : ์•„ ๊ทธ๊ฒŒ... ๊ทธ ์„œ๋ฒ„์—์„œ ๊ทธ๋ฆฐ ํ™”๋ฉด ์•ˆ์—๋‹ค๊ฐ€ "์ข‹์•„์š” ๋ฒ„ํŠผ"์ด๋‚˜ ๋ฌดํ•œ ์Šคํฌ๋กค ๋„ฃ์œผ๋ ค๊ณ  ์ž์‹ ์ปดํฌ๋„ŒํŠธ(Client Component)๋กœ ์กฐ๊ฐ ํŒŒ์„œ ๋†จ๊ฑฐ๋“ ์š”? ๊ฑฐ๊ธฐ ์•ˆ์— ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ useQuery(['posts'])๋ฅผ ์„ ์–ธํ•˜๋‹ˆ๊นŒ, ์œ ์ €๊ฐ€ ํŽ˜์ด์ง€ ๋“ค์–ด์™€์„œ ํ™”๋ฉด ๋ณด์ž๋งˆ์ž ๋ฐฑ๊ทธ๋ผ์šด๋“œ API๋ฅผ ๋‹ค์‹œ ๋•Œ๋ ค์š”! ๋ถ„๋ช…ํžˆ ๊ทธ posts ๋ฐ์ดํ„ฐ ์œ„์ชฝ 100์ค„์งœ๋ฆฌ ๋ถ€๋ชจ ์„œ๋ฒ„ ์ฃผ๋ฐฉ์—์„œ ๋‹ค ๊ตฌ์›Œ์„œ HTML๋กœ ๋‚ด๋ฆฐ ์ตœ์‹  ๋ฐ์ดํ„ฐ์ธ๋ฐ, ํ”„๋ก ํŠธ ์—”์ง„ ์ผœ์ง€์ž๋งˆ์ž ์ง€๊ฐ€ ๋ชจ๋ฅด๊ณ  ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค์ž–์•„์š”! ์„œ๋ฒ„ ๋ถ€ํ•˜ ๋ฏธ์นœ ๊ฑฐ ์•„๋‹ˆ์—์š”?!

๐Ÿฆ ์˜ํ˜ธ: ์•„ํ•˜, ์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค ์˜์ฒ  ๋‹˜. ์„œ๋ฒ„์˜ ์ฃผ๋ฐฉ์—์„œ ๋ฐ์ดํ„ฐ ์—ด์‹ฌํžˆ ํผ ์™€์„œ ์š”๋ฆฌ๋ฅผ ๊ตฌ์›Œ๋†“๊ณ , ์ •์ž‘ ํ”„๋ก ํŠธ์—”๋“œ ์•ˆ์˜ ์บ์‹œ ๊ธˆ๊ณ (QueryCache) ์—๊ฒŒ๋Š” "์„œ๋ฒ„๊ฐ€ ๋ฐฉ๊ธˆ ๋ฐ์ดํ„ฐ ๊ตฌ์›Œ๋†จ์œผ๋‹ˆ ๋„ˆ ์ด๊ฑฐ ์•Œ๊ณ  ์žˆ์–ด๋ผ" ๋ผ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์ž…์‹œ์ผœ์ฃผ์งˆ ์•Š์•˜๊ตฐ์š”.
๊ฐ€์žฅ ์ค‘์š”ํ•˜๊ณ  ๋‚œ์ด๋„ ์žˆ๋Š” ๋Œ€๋ฏธ์˜ ์žฅ, Hydration (์ˆ˜ํ™”) ์™€ initialData ์˜ ์„ธ๊ณ„์— ์˜ค์‹  ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: ์บ์‹œ ์‚ฌ๊ฐ์ง€๋Œ€ (Cache Blind Spot)

Next.js์˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(RSC)์—์„œ ๋ถ€๋ฆฌ๋‚˜์ผ€ DB๋ฅผ ์ฐ”๋Ÿฌ ์ตœ์‹ ์˜ ์™„์ „ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ๊ฐ€์ ธ์™€์„œ HTML์„ ๊ทธ๋ ธ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ์‹œ๋‹ค. ์ด ํ™”๋ฉด ๊ป๋ฐ๊ธฐ(HTML)๋ฅผ ์œ ์ €์˜ ๋ชจ๋ฐ”์ผ ํฐ์œผ๋กœ ๋นก ๋˜์กŒ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ทธ ํฐ ์•ˆ์—๋Š” React Query๋ผ๋Š” ๋…์ž์ ์ธ ํด๋ผ์ด์–ธํŠธ ๋งค๋‹ˆ์ €๊ฐ€ ์ƒˆ๋กœ ๋ˆˆ์„ ๋œน๋‹ˆ๋‹ค.
๋…€์„์ด ์บ์‹œ ๊ธˆ๊ณ  ๋ฌธ์„ ์—ด์–ด๋ณด๋‹ˆ...? ํ…… ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค.
์™œ? ์„œ๋ฒ„๊ฐ€ HTML๋งŒ ์คฌ์ง€, ํ”„๋ก ํŠธ์—”๋“œ์˜ ์ฟผ๋ฆฌ ํด๋ผ์ด์–ธํŠธ(๋ฉ”๋ชจ๋ฆฌ) ์•ˆ์— "๋‚ด๊ฐ€ ๋ฌด์Šจ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๋Š”์ง€" ์ฃผ์ž…(Inject) ํ•ด์ฃผ์ง„ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

๊ฒฐ๊ตญ React Query๋Š” "์˜ค ๋งˆ์ด ๊ฐ“, ๋ฐ์ดํ„ฐ๊ฐ€ ํ…… ๋น„์—ˆ๋„ค? ๋‚ด๊ฐ€ ๋‹ค์‹œ API ์ด์„œ ๊ฐ€์ ธ์™€์•ผ๊ฒ ๊ตฐ!" ํ•˜๋ฉฐ ๋ฌด์˜๋ฏธํ•œ ์ค‘๋ณต ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ์„ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„์—์„œ ๋”ด ๋ฐ์ดํ„ฐ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์บ์‹œ๋กœ ์™ ์ง‘์–ด๋„ฃ์–ด ์ฃผ๋Š” ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์ฃผ์ž… ๊ธฐ์ˆ  ์„ ๋งˆ์Šคํ„ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ 2๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.


1. ์ „์ˆ  A: initialData (๊ฐ€์žฅ ์‰ฝ๊ณ  ์ง๊ด€์ ์ธ Prop ์ „๋‹ฌ)

TkDodo์˜ ์•„ํ‹ฐํด #17์„ ๋ณด๋ฉด ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ด ์ œ์ผ ์‰ฝ์Šต๋‹ˆ๋‹ค.
์šฐ๋ฆฌ๊ฐ€ RSC(์„œ๋ฒ„ ์ฃผ๋ฐฉ) ์•ˆ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋”ฑ ์–ป์–ด๋ƒˆ๋‹ค๋ฉด, ๊ทธ๊ฑธ ์ž์‹ ์กฐ๊ฐ ์ปดํฌ๋„ŒํŠธ(Client Component) ํ•œํ…Œ props๋กœ ๋‚ด๋ ค์ฐ์€ ๋’ค, ์ฟผ๋ฆฌ์˜ initialData ์„ค์ • ์Šฌ๋กฏ์— ํˆญ ๋ผ์›Œ ๋„ฃ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

// ๐Ÿ“ app/board/page.tsx (์„œ๋ฒ„ ์ฃผ๋ฐฉ - RSC)
import { getPosts } from '@/api/posts'
import { BoardClient } from './board-client'; // ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ (use client)
 
export default async function BoardPage() {
  // 1๏ธโƒฃ ์„œ๋ฒ„์—์„œ DB๋ฅผ ์ง๋นต์œผ๋กœ ์ฐ”๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์›Œ๋ƒ„
  const postsFromServer = await getPosts();
 
  // 2๏ธโƒฃ ๊ทธ ๊ท€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋กœํผํ‹ฐ(Prop)๋กœ ํƒ๋ฐฐ๋ฅผ ์‹ธ์„œ ์ž์‹์—๊ฒŒ ๋˜์ง„๋‹ค!
  return <BoardClient initialPosts={postsFromServer} />
}
// ๐Ÿ“ app/board/board-client.tsx (ํด๋ผ์ด์–ธํŠธ ํฐ - CSR)
'use client'
 
import { useQuery } from '@tanstack/react-query';
import { getPosts } from '@/api/posts';
 
export function BoardClient({ initialPosts }: { initialPosts: Post[] }) {
  const { data } = useQuery({
    queryKey: ['posts'],
    queryFn: () => getPosts(),
    // 3๏ธโƒฃ ๐Ÿš€ ๋งˆ๋ฒ•์˜ ํ•ต์‹ฌ: "Query์•ผ! ๋‚ด ์—„๋งˆ(์„œ๋ฒ„ ์ฃผ๋ฐฉ)๊ฐ€ ๋ฐ์ดํ„ฐ ์‹ธ์ฃผ์…จ์–ด! ์บ์‹œํ†ต์— ์ผ๋‹จ ์ด๊ฑฐ๋ถ€ํ„ฐ ๋ฐ•์•„๋„ฃ๊ณ  ์‹œ์ž‘ํ•ด!"
    initialData: initialPosts,
  });
 
  return (
    <ul> {/* ๊นœ๋นก์ž„ 0%, API ๋ถˆํ•„์š” ์ค‘๋ณต ํ˜ธ์ถœ 0๋ฐฐ ํญ๋ฐœ์  ์„ฑ๋Šฅ */}
      {data.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  )
}

๐Ÿ’ก ์žฅ๋‹จ์ 

  • ์žฅ์ : ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง๊ด€์ ์œผ๋กœ ์ดํ•ดํ•˜๊ธฐ ์—„์ฒญ๋‚˜๊ฒŒ ์‰ฝ๋‹ค.
  • ๋‹จ์ : ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด๋‚˜ ๊น๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ 3๋‹จ๊ณ„, 4๋‹จ๊ณ„ ์•„๋ž˜ ๋„ํŠธ๋จธ๋ฆฌ์— ์žˆ๋Š” ์กฐ๋ง‰๋ง‰ํ•œ ๋ฆฌ์ŠคํŠธ ์ปดํฌ๋„ŒํŠธํ•œํ…Œ ์ „๋‹ฌํ•˜๋ ค๋ฉด Props Drilling ์ง€์˜ฅ์ด ํŽผ์ณ์ง‘๋‹ˆ๋‹ค.

2. ์ „์ˆ  B: <HydrationBoundary> (์ง„์ •ํ•œ ์•„ํ‚คํ…์ฒ˜์˜ ๊ฝƒ)

๊ทธ๋ž˜์„œ ํƒ„์ƒํ•œ ๊ถ๊ทน๊ธฐ๊ฐ€ ํผ๊ฒฉ ๋†’์€ Hydration(Dehydrate/Hydrate) ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค.
์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ๋”ด ๋‹ค์Œ์—, ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ต์กฐ๋ฆผ(DehydrateState)์œผ๋กœ ๊ฝ๊ฝ ๊นกํ†ต ์••์ถ• ์‹œ์ผœ์„œ ๋ธŒ๋ผ์šฐ์ €๋กœ ๋‚ด๋ ค๋ณด๋ƒ…๋‹ˆ๋‹ค.
ํฐ์— ๋„์ฐฉํ•œ ๊นกํ†ต์€ <HydrationBoundary> ๋ผ๋Š” ์ „์ž๋ ˆ์ธ์ง€๋ฅผ ๋งŒ๋‚˜์„œ ํŽ‘! ํ•˜๊ณ  ํ„ฐ์ง€๋ฉด์„œ ์ „์—ญ ์บ์‹œ ๊ธˆ๊ณ (QueryClient)์˜ ๊ตฌ์„๊ตฌ์„ ๋ชจ๋“  ๋ฐฉ๋ฐฉ๊ณก๊ณก์œผ๋กœ ๊ทธ ๋ƒ„์ƒˆ(๋ฐ์ดํ„ฐ)๋ฅผ ํผํŠธ๋ ค๋ฒ„๋ฆฝ๋‹ˆ๋‹ค(Hydrate).

Props๋กœ ํƒ๋ฐฐ ์•„์ €์”จ์ฒ˜๋Ÿผ ์ผ์ผ์ด ์ „๋‹ฌํ•  ํ•„์š”๊ฐ€ ์•„์˜ˆ ์‚ฌ๋ผ์ง€๋Š” ๊ฒ๋‹ˆ๋‹ค! (์˜ค๋กœ์ง€ ์บ์‹œ ์ด๋ฆ„ํ‘œ์ธ queryKey ๋งŒ์œผ๋กœ ๋‹ค ๋นจ์•„๋“ค์ž„)

// ๐Ÿ“ app/board/page.tsx (์„œ๋ฒ„ ์ฃผ๋ฐฉ - RSC)
import { HydrationBoundary, dehydrate, QueryClient } from '@tanstack/react-query'
import { getPosts } from '@/api/posts'
import { BoardClient } from './board-client' // ์ด๊ฑด ์ด์ œ Prop์ด ์ „ํ˜€ ํ•„์š” ์—†์ง€!
 
export default async function BoardPage() {
  // 1๏ธโƒฃ ์ด ์š”์ฒญ ๋‹จ ํ•œ ๋ฒˆ์—๋งŒ ์“ฐ์ด๋Š” 1ํšŒ์šฉ ๋นˆ ๊นกํ†ต ์ƒ์„ฑ
  const queryClient = new QueryClient();
 
  // 2๏ธโƒฃ ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ถฉ๋ถ„ํžˆ ๋‹น๊ฒจ์„œ(Prefetch) ์„œ๋ฒ„์šฉ ๋นˆ ๊นกํ†ตํ†ต ์บ์‹œ ์•ˆ์— ๊ฐ€๋“ ์ฑ„์šด๋‹ค! (queryKey ๋ช…์‹ฌ!!)
  await queryClient.prefetchQuery({
    queryKey: ['posts'],
    queryFn: getPosts,
  });
 
  return (
    // 3๏ธโƒฃ dehydrate(๊นกํ†ต ์••์ถ•๊ธฐ)๋ฅผ ์ด์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ€์–ด๋ฒ„๋ฆฐ๋‹ค!!
    // ์ „์ž๋ ˆ์ธ์ง€(Boundary) ๊ฐ€ ๋Œ์•„๊ฐ€๋Š” ์ด ์•„๋ž˜์ชฝ(์ž์‹) ์˜์—ญ์€ ์ด์ œ ์‹ ๋“ค์˜ ์˜ํ† ๋‹ค.
    <HydrationBoundary state={dehydrate(queryClient)}>
      <BoardClient /> 
    </HydrationBoundary>
  )
}

์ด์ œ ํด๋ผ์ด์–ธํŠธ ์ชฝ์„ ๋ณผ๊นŒ์š”? ๋ˆˆ๋ฌผ์ด ์™ˆ์นต ๋‚  ๋งŒํผ ๊น”๋”ํ•ด์ง‘๋‹ˆ๋‹ค.

// ๐Ÿ“ app/board/board-client.tsx (ํด๋ผ์ด์–ธํŠธ - CSR)
'use client'
 
import { useQuery } from '@tanstack/react-query'
import { getPosts } from '@/api/posts'
 
export function BoardClient() {
  // ๐Ÿš€ ๋!!! Prop์ด ์—†์Šต๋‹ˆ๋‹ค! initialData๋„ ์—†์Šต๋‹ˆ๋‹ค!
  // ์•„๊นŒ ๋ฐ–์—์„œ ์ „์ž๋ ˆ์ธ์ง€(Boundary)๊ฐ€ ํ„ฐ์งˆ ๋•Œ ์ด๋ฏธ ์ด ๋ธŒ๋ผ์šฐ์ € ์ „์—ญ ์บ์‹œ ์•ˆ์— 
  // ['posts'] ๋ผ๋Š” ํ‚ค ๊ฐ’์œผ๋กœ ์บ์‹œ๊ฐ€ "์•„์ฃผ ๊ฝ‰ ์ฐจ" ๋“ค์–ด๊ฐ”๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  const { data } = useQuery({
    queryKey: ['posts'], 
    queryFn: () => getPosts()
  });
 
  return <div>{data.length}๊ฐœ ๊ทธ๋ฆฌ๊ธฐ ์™„๋ฃŒ</div>
}

์ด ํŒจํ„ด์ด ๋ฐ”๋กœ Next.js App Router์™€ React Query๋ฅผ ๊ฒฐํ•ฉ์‹œํ‚ค๋Š” ๊ถ๊ทน์˜ ์—”๋“œ๊ฒŒ์ž„ ์•„ํ‚คํ…์ฒ˜ ์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ์ž์‹ ์ปดํฌ๋„ŒํŠธ ๊นŠ์ˆ™ํ•œ ๊ณณ์—์„œ useQuery(['posts']) ๋ฅผ ์ณ๋ถ€๋ฅด๋”๋ผ๋„ ์บ์‹œ์— ์ด๋ฏธ ๊ฟ€๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐœ๋ผ์ ธ ์žˆ์–ด์„œ ๋ฌด์ค‘๋ณต ๋ฌด๋กœ๋”ฉ ์™„๋ฒฝ ์„ฑ๋Šฅ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค.


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์™€, ๋ฏธ์นœ ๊ฑฐ ์•„๋‹ˆ์•ผ...? ๋‚œ ๋˜ ์–ด์ œ Next.js ํฌ๊ธฐํ•˜๊ณ  ์ „ํ†ต SPA๋กœ ๋Œ์•„๊ฐˆ ๋ป”ํ–ˆ๋Š”๋ฐ.
Props ๋กœ ๋„˜๊ธฐ๋ ค๋‹ค๊ฐ€ depth ๋„ˆ๋ฌด ๊ธธ์–ด์ ธ์„œ ์šธ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ์„œ๋ฒ„์—์„œ ํ”„๋ฆฌํŽ˜์น˜(prefetchQuery) ๋•Œ๋ ค ๋ฐ•๊ณ  Boundary๋กœ ๊ฐ์‹ธ์„œ ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ์— ๊ณต์ค‘ ํˆฌํ•˜(AirDrop) ์‹œ์ผœ๋ฒ„๋ฆฌ๋Š” ๋ฐฉ์‹์€ ์ง„์งœ... ๊ธฐ์ˆ ์˜ ๋‚ญ๋งŒ ๊ทธ ์ž์ฒด๋‹ค.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "์„œ๋ฒ„ ์ฃผ๋ฐฉ์—์„œ ๋ง›์žˆ๋Š” ์š”๋ฆฌ๋ฅผ ํ”ฝ์—…ํ–ˆ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์บ์‹œ ๊ธˆ๊ณ ์—๋„ ๊ทธ ๋ ˆ์‹œํ”ผ๋ฅผ ์ธ์ˆ˜์ธ๊ณ„(Hydration) ํ•ด์ฃผ์ž. ๊ทธ๋ž˜์•ผ ํ”„๋ก ํŠธ๊ฐ€ ๋ฉ์ฒญํ•˜๊ฒŒ API๋ฅผ ๋˜ ์ค‘๋ณตํ˜ธ์ถœํ•˜๋Š” ๋Œ€์ฐธ์‚ฌ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค!"

๋“œ๋””์–ด Basic ๊ธฐ์ดˆ ๊ฐ€์ด๋“œ๋ผ์ธ 10๊ฐœ๊ฐ€ ๋๋‚ฌ๋‹ค. ์†”์งํžˆ ์ด 10๊ฐœ๋งŒ ์™„๋ฒฝํ•˜๊ฒŒ ์•Œ๊ณ  ์ฒด๋“ํ•ด๋„ ์–ด๋”” ๊ฐ€์„œ ์ŠคํŒŒ๊ฒŒํ‹ฐ ์†Œ์Šค ํ†ต์œผ๋กœ ๋’ค์—Ž๋Š” ๋ฏผํ๋Š” ์ ˆ๋Œ€ ์•ˆ ๋ผ์น  ๊ฑฐ ๊ฐ™๋‹ค. ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋‚ด์ผ๋ถ€ํ„ด ๋ฌดํ•œ ์Šคํฌ๋กค ๊ฐ™์€ Advanced ์˜์—ญ(์‹ฌํ™” 7๊ฐ•) ํ•  ๊ฑฐ๋ผ๋Š”๋ฐ, ์ผ๋‹จ ์˜ค๋Š˜๊นŒ์ง„ ๋Œ€๊ฐ€๋ฆฌ์— ๋ฐ•ํžŒ ์งœ๋ฆฟํ•จ ์ฆ๊ธฐ๋ฉด์„œ ๊ฟ€์ž ์ž์•ผ์ง€ ใ…‹ใ…‹ใ…‹ใ…‹. ๐Ÿถ


๐Ÿ“ ๋ฐฐ์šด ๋‚ด์šฉ ์ ๊ฒ€ํ•˜๊ธฐ (Quiz)

Q. ์˜์ฒ ์ด๊ฐ€ ์ž๋ž‘์Šค๋Ÿฝ๊ฒŒ <HydrationBoundary> ํŒจํ„ด์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ, ์ปดํฌ๋„ŒํŠธ ์ง„์ž… ์ฆ‰์‹œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋„คํŠธ์›Œํฌ ์žฌํ˜ธ์ถœ(Refetching) ์ด ํ•œ ๋ฒˆ ๋” ๋ฐœ์ƒํ•˜๋Š” ๊ฑธ ๋ชฉ๊ฒฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์„œ๋ฒ„ ๋ Œ๋”๋ง์— ์„ฑ๊ณตํ–ˆ์Œ์—๋„ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•œ ๋ฒˆ ๋‹ค์‹œ ์˜๋Š” ๊ธฐ์ดํ•œ ๋ฒ„๊ทธ์ธ๋ฐ, ์›์ธ์ด ๋ฌด์—‡์ผ๊นŒ์š”? ์ด ์˜ต์…˜์˜ ๊ธฐ๋ณธ๊ฐ’ ์ฒ ํ•™์— ๋น„์ถ”์–ด ์„ค๋ช…ํ•ด๋ณด์„ธ์š”.

โœ… ์ •๋‹ต: ์„œ๋ฒ„์—์„œ ํ”„๋ฆฌํŽ˜์น˜ํ•˜์—ฌ ๋„˜๊ฒจ์ค€ ๋ฐ์ดํ„ฐ(ํ†ต์กฐ๋ฆผ)์˜ 'staleTime'์ด 0(๊ธฐ๋ณธ๊ฐ’)์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์„œ๋ฒ„๊ฐ€ ์•„๋ฌด๋ฆฌ ์—ด์‹ฌํžˆ prefetchQuery ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฑ„์›Œ์ฃผ๊ณ , ํด๋ผ์ด์–ธํŠธ๋กœ Hydrate(์ˆ˜ํ™”) ๋„˜๊ฒจ์ค˜ ๋ดค์ž React Query์˜ ์‹ ์„ ๋„(staleTime) ๊ธฐ๋ณธ๊ฐ’์€ 0์ดˆ์ž…๋‹ˆ๋‹ค! ๋ธŒ๋ผ์šฐ์ €์˜ ์ฟผ๋ฆฌ ๋…€์„์€ ๋ˆˆ์„ ๋œจ์ž๋งˆ์ž "์˜ค ๋ฐ์ดํ„ฐ ๋“ค์–ด์žˆ๋„ค?(์Šคํ”ผ๋„ˆ ๋ง‰์Œ, ์ผ๋‹จ ํ™”๋ฉด ๊ทธ๋ฆผ) ์ž ๊น, ๊ทผ๋ฐ ์ด๊ฑฐ stale(์ƒํ•œ ๋นต) ์ƒํƒœ์ž–์•„? ๋‹น์žฅ ์„œ๋ฒ„์— ๋’ท๋‹จ์—์„œ ๋‹ค์‹œ ๊ฐ€์„œ ์ง„์งœ ์ตœ์‹  ๋งž๋Š”์ง€ ๋ฌผ์–ด๋ด์•ผ๊ฒ ๋‹ค!" ๋ผ๋ฉฐ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฆฌํŒจ์นญ์„ ์กฐ์šฉํžˆ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ์ดˆ๊ธฐ Hydrate ์งํ›„ ๋ถˆํ•„์š”ํ•œ ๋’ท๋‹จ ๋ฆฌํŒจ์นญ(Refetch)๋ฅผ ๋ง‰๊ณ  ์‹ถ๋‹ค๋ฉด, ์„œ๋ฒ„์—์„œ Prefetch ํ•  ๋•Œ๋ผ๋˜๊ฐ€ ์ตœ์ƒ๋‹จ QueryClient ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐ์–ด๋‚ผ ๋•Œ staleTime: 60 * 1000 (1๋ถ„ ์ •๋„) ์˜ ๋„‰๋„‰ํ•œ ์‹ ์„ ๋„ ์œ ํ†ต๊ธฐํ•œ์„ ๊ฐ•์ œ๋กœ ๋ถ€์—ฌํ•ด์ฃผ์…”์•ผ๋งŒ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž ์‹œ ์˜์‹ฌ์„ ๊ฑฐ๋‘๊ณ  ๋„คํŠธ์›Œํฌ ๋กœ๋“œ๋ฅผ ์ ˆ๋ฌ˜ํ•˜๊ฒŒ ์•„๋‚๋‹ˆ๋‹ค!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ์ˆ˜ํ™”์˜ ๋งˆ์ง€๋ง‰ ์กฐ๊ฐ์€ ์–ธ์ œ๋‚˜ ๋„‰๋„‰ํ•œ ์œ ํ†ต๊ธฐํ•œ(staleTime) ๋ณด์žฅ!