07. ๐Ÿ“ก ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ์™€ ์บ์‹ฑ ์ „๋žต ๋งˆ์Šคํ„ฐ

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

๐Ÿ“‹ ๊ฐœ์š”

React Query(TanStack Query)๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ '์ƒํƒœ'๊ฐ€ ์•„๋‹Œ '์บ์‹œ'๋กœ ๋‹ค๋ฃจ๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ณ€ํ™”๋ฅผ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ ์ด ๋ฉด์ ‘ ํ•ญ๋ชฉ์˜ ๋ชฉํ‘œ

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 24๋ถ„ (ํ•ต์‹ฌ ์š”์•ฝ: 12๋ถ„)

๐Ÿ—บ๏ธ ์ด ์ฑ•ํ„ฐ์˜ ํ๋ฆ„
[๊ฐœ๋… ์‚ฌ์ „] โ†’ [์งˆ๋ฌธ 1: ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ vs ์„œ๋ฒ„ ์ƒํƒœ] โ†’ [์งˆ๋ฌธ 2: ์บ์‹ฑ ๋ฐ ์‹ ์„ ๋„ ๊ด€๋ฆฌ] โ†’ [์‹ค์ „ ๋ณ€ํ˜• ์งˆ๋ฌธ]

๐ŸŽฏ ์ด ์ฑ•ํ„ฐ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • ์™œ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์—ญ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Zustand ๋“ฑ)์— ์ €์žฅํ•˜๋ฉด ์•ˆ ๋˜๋Š”์ง€ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
  • Stale-While-Revalidate ์ „๋žต์ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์–ด๋–ป๊ฒŒ ๊ทน๋Œ€ํ™”ํ•˜๋Š”์ง€ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฌดํ•œ ์Šคํฌ๋กค, ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๋“ฑ ๋ณต์žกํ•œ ๋น„๋™๊ธฐ ํŒจํ„ด์˜ ๊ตฌํ˜„ ์›๋ฆฌ๋ฅผ ๋งˆ์Šคํ„ฐํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“š ํ•ต์‹ฌ ๊ฐœ๋… ์‚ฌ์ „ (Concept Glossary)

1. ์„œ๋ฒ„ ์ƒํƒœ (Server State)

๋‚ด๊ฐ€ ์†Œ์œ ํ•˜์ง€ ์•Š์€, ์›๊ฒฉ์ง€์˜ DB์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ์–ธ์ œ๋“  ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์— ์˜ํ•ด ๋ฐ”๋€” ์ˆ˜ ์žˆ์œผ๋ฉฐ(Out-of-sync), ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ๋น„๋™๊ธฐ์  ํŠน์„ฑ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

2. SWR (Stale-While-Revalidate)

"์ƒํ•œ(Stale) ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ๋ณด์—ฌ์ฃผ๋˜, ๊ทธ๋™์•ˆ ์„œ๋ฒ„์—์„œ ์‹ ์„ ํ•œ(Fresh) ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์™€(Revalidate) ์กฐ์šฉํžˆ ๊ต์ฒดํ•œ๋‹ค"๋Š” ์บ์‹ฑ ์ „๋žต์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ '๋Œ€๊ธฐ ์‹œ๊ฐ„ 0'์˜ ๊ฒฝํ—˜์„ ์ค๋‹ˆ๋‹ค.

3. ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ (Optimistic Update)

์„œ๋ฒ„์˜ ์‘๋‹ต์ด ์˜ค๊ธฐ๋„ ์ „์— ๋ฏธ๋ฆฌ UI๋ฅผ '์„ฑ๊ณต'ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ฐ”๊พธ๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ข‹์•„์š” ๋ฒ„ํŠผ์ด๋‚˜ ๋Œ“๊ธ€ ์ž‘์„ฑ์ฒ˜๋Ÿผ ๋น ๋ฅด๊ธฐ ์ฐจ์ด๊ฐ€ UX์— ํฐ ์˜ํ–ฅ์„ ์ฃผ๋Š” ๊ธฐ๋Šฅ์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.


๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "์˜ํ˜ธ ๋‹˜! '์˜์ˆ˜๋„ค ๊ฒŒ์‹œํŒ' ๋ฐ์ดํ„ฐ๋ฅผ Redux์— ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋А๋ผ Loading, Error, Success ์ƒํƒœ ๋ณ€์ˆ˜๋งŒ 6๊ฐœ๋ฅผ ๋งŒ๋“ค์—ˆ์–ด์š”. ๋น„๋™๊ธฐ ์ฝ”๋“œ ์งœ๋‹ค๊ฐ€ ๋Š™์–ด๋ฒ„๋ฆด ๊ฒƒ ๊ฐ™์•„์š”... ๐Ÿ‘ด"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋Š” '์ƒํƒœ'๊ฐ€ ์•„๋‹ˆ๋ผ '์›๊ฒฉ ์Šค๋ƒ…์ƒท'์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฑธ ๊ตณ์ด ์šฐ๋ฆฌ ์•ฑ ์ƒํƒœ์— ์–ต์ง€๋กœ ๋ผ์›Œ ๋งž์ถ”๋ ค๋‹ˆ๊นŒ ํž˜๋“  ๊ฑฐ์˜ˆ์š”. React Query ๊ฐ™์€ ๋„๊ตฌ์—๊ฒŒ '๊ด€๋ฆฌ'๋ฅผ ๋งก๊ธฐ๊ณ  ์šฐ๋ฆฌ๋Š” ๊ทธ์ € '๊ตฌ๋…'๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค."

Q1. ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ(Client State)์™€ ์„œ๋ฒ„ ์ƒํƒœ(Server State)๋ฅผ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

๐ŸŽฏ ์ถœ์ œ ์˜๋„

๋ฐ์ดํ„ฐ์˜ ์†Œ์œ ๊ถŒ๊ณผ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ์™€ ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜(Data Inconsistency) ๋ฒ„๊ทธ์— ์‹œ๋‹ฌ๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ Naive ๊ตฌํ˜„ (Bad Case)

์˜์ฒ ์ด๋Š” ๋ชจ๋“  API ์‘๋‹ต์„ ์ „์—ญ ์Šคํ† ์–ด์— ๊พธ์—ญ๊พธ์—ญ ์ง‘์–ด๋„ฃ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

// ๐Ÿฃ ์˜์ฒ : "์œ ์ € ๋ชฉ๋ก๋„ ์ „์—ญ ์ƒํƒœ๋‹ˆ๊นŒ Redux์— ์ €์žฅํ•ด์•ผ์ง€!"
function Users() {
  const dispatch = useDispatch();
  const { data, isLoading } = useSelector(state => state.users);
 
  useEffect(() => {
    dispatch(fetchUsers()); // โš ๏ธ ๋กœ๋”ฉ, ์—๋Ÿฌ, ์บ์‹œ ๋ฌดํšจํ™” ๋กœ์ง์„ ์ง์ ‘ ๋‹ค ๊ตฌํ˜„ํ•ด์•ผ ํ•จ
  }, []);
  
  // โš ๏ธ ๋ฌธ์ œ: ๋‹ค๋ฅธ ํƒญ์—์„œ ์œ ์ €๊ฐ€ ์ถ”๊ฐ€๋˜์–ด๋„ ์ด ์•ฑ์€ ์•Œ ๋ฐฉ๋ฒ•์ด ์—†์Œ (Outdated)
}

๐Ÿฆ ์˜ํ˜ธ์˜ ํŒฉํญ ์กฐ์–ธ
"์˜์ฒ  ๋‹˜, ์œ ์ € ๋ชฉ๋ก์€ ์šฐ๋ฆฌ ์•ฑ์ด ์ฃผ์ธ์ด ์•„๋‹ˆ์—์š”. ์„œ๋ฒ„๊ฐ€ ์ฃผ์ธ์ด์ฃ . ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์„œ๋ฒ„๊ฐ€ ์•Œ๋ ค์ฃผ๊ฑฐ๋‚˜ ์šฐ๋ฆฌ๊ฐ€ ๋‹ค์‹œ ๋ฌผ์–ด๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ(๋‹คํฌ๋ชจ๋“œ ์—ฌ๋ถ€ ๋“ฑ)์™€๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ์ƒํƒœ๊ณ„์˜ˆ์š”."

๐Ÿฆ ์˜ํ˜ธ์˜ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ (Good Case)

์˜ํ˜ธ ๋ฆฌ๋“œ๋Š” React Query๋ฅผ ์‚ฌ์šฉํ•ด ์„ ์–ธ์ ์œผ๋กœ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

// ๐Ÿฆ ์˜ํ˜ธ: "๋ฐ์ดํ„ฐ ํŽ˜์นญ์€ '๋ช…๋ น'์ด ์•„๋‹ˆ๋ผ '์„ ์–ธ'์ž…๋‹ˆ๋‹ค."
 
function Users() {
  // โœ… ๋กœ๋”ฉ, ์—๋Ÿฌ, ๋ฐ์ดํ„ฐ, ์‹ ์„ ๋„ ์ฒดํฌ๊นŒ์ง€ ํ•œ ์ค„๋กœ ํ•ด๊ฒฐ
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 1000 * 60 * 5, // 5๋ถ„ ๋™์•ˆ์€ ์‹ ์„ ํ•œ ๋ฐ์ดํ„ฐ๋กœ ๊ฐ„์ฃผ (์บ์‹œ ํ™œ์šฉ)
  });
 
  if (isLoading) return <Spinner />;
  return <div>{data.map(user => <div key={user.id}>{user.name}</div>)}</div>;
}

๐Ÿ“Š ๋ ˆ๋ฒจ๋ณ„ ๋‹ต๋ณ€ ๊ฐ€์ด๋“œ (Self-Check)

  • Level 1 (Junior): "์„œ๋ฒ„ ์ƒํƒœ๋Š” API๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ์ดํ„ฐ๊ณ , ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋Š” UI์šฉ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋Š” ๋กœ๋”ฉ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
  • Level 2 (Senior): "์†Œ์œ ๊ถŒ(Ownership)์˜ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ƒํƒœ๋Š” ๋น„๋™๊ธฐ์ ์ด๊ณ  ๋™๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๊ณต์œ ํ•œ๋‹ค๋Š” ์ ์„ ์ง€์ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์บ์‹ฑ, ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์—…๋ฐ์ดํŠธ ๋“ฑ์ด ํ•„์š”ํ•จ์„ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค."
  • Level 3 (Specialist): "์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์–ด๋–ป๊ฒŒ 'SSOT(Single Source of Truth)' ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋ฆฌ์•กํŠธ์˜ '์„ ์–ธ์  UI' ํŒจ๋Ÿฌ๋‹ค์ž„๊ณผ ์–ด๋–ป๊ฒŒ ์ผ์น˜ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ด๋กœ ์ธํ•ด ์ „์—ญ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์—ญํ• ์ด '์ˆœ์ˆ˜ UI ์ƒํƒœ'๋กœ๋งŒ ์ถ•์†Œ๋˜๋Š” ์•„ํ‚คํ…์ฒ˜์  ์šฐ์•„ํ•จ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค."

Q2. staleTime๊ณผ cacheTime์˜ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•˜๊ณ , ์ด๋ฅผ ํ™œ์šฉํ•œ ์ตœ์ ์˜ ์บ์‹œ ์ „๋žต์„ ์ œ์‹œํ•ด ๋ณด์„ธ์š”.

๐ŸŽฏ ์ถœ์ œ ์˜๋„

React Query์˜ ํ•ต์‹ฌ ๋งค์ปค๋‹ˆ์ฆ˜์ธ '์‹ ์„ ๋„'์™€ '๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ'๋ฅผ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ  ์ƒํ™ฉ์— ๋งž๊ฒŒ ํŠœ๋‹ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ Naive ๊ตฌํ˜„ (Bad Case)

์˜์ฒ ์ด๋Š” ๋ชจ๋“  API ์š”์ฒญ์— ๋Œ€ํ•ด ๊ธฐ๋ณธ ์„ค์ •๋งŒ ์“ฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

// ๐Ÿฃ ์˜์ฒ : "์„ค์ •๊ฐ’์ด ๋„ˆ๋ฌด ๋งŽ์•„์„œ ๊ทธ๋ƒฅ ๊ธฐ๋ณธ์œผ๋กœ ์จ์š”. ์•Œ์•„์„œ ์ž˜ ๋˜๊ฒ ์ฃ ?"
const { data } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts }); 
// โš ๏ธ ๋ฌธ์ œ: ํŽ˜์ด์ง€๋ฅผ ์ž ๊น ๋‚˜๊ฐ”๋‹ค ์˜ฌ ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ API ์žฌํ˜ธ์ถœ ๋ฐœ์ƒ (์„œ๋ฒ„ ๋ถ€ํ•˜)

๐Ÿฆ ์˜ํ˜ธ์˜ ํŒฉํญ ์กฐ์–ธ
"์˜์ฒ  ๋‹˜, ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์€ 1์ดˆ ๋งŒ์— ์•ˆ ๋ฐ”๋€Œ์–ด์š”. ๊ทธ๋Ÿฐ๋ฐ ์™œ ๋งค๋ฒˆ ์„œ๋ฒ„๋ฅผ ๊ดด๋กญํžˆ๋‚˜์š”? ๋ฐ์ดํ„ฐ๋ฅผ ์–ผ๋งˆ๋‚˜ '์‹ ์„ (Fresh)'ํ•˜๋‹ค๊ณ  ๋ฏฟ์–ด์ค„์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒŒ ์‹œ๋‹ˆ์–ด์˜ ์„ธ์‹ฌํ•จ์ž…๋‹ˆ๋‹ค."

๐Ÿฆ ์˜ํ˜ธ์˜ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ (Good Case)

์˜ํ˜ธ ๋ฆฌ๋“œ๊ฐ€ ์ƒํ™ฉ์— ๋”ฐ๋ฅธ ์บ์‹œ ์„ค์ •๋ฒ•์„ ์ „์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.

// ๐Ÿฆ ์˜ํ˜ธ: "๋ฐ์ดํ„ฐ์˜ ์„ฑ๊ฒฉ์— ๋”ฐ๋ผ ์œ ํ†ต๊ธฐํ•œ์„ ์ •ํ•˜์„ธ์š”."
 
// 1. ๊ณต์ง€์‚ฌํ•ญ: ์ž˜ ์•ˆ ๋ณ€ํ•จ -> staleTime์„ ๊ธธ๊ฒŒ (์˜ˆ: 1์‹œ๊ฐ„)
const noticeQuery = useQuery({
  queryKey: ['notices'],
  staleTime: 1000 * 60 * 60,
});
 
// 2. ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…: ๊ณ„์† ๋ณ€ํ•จ -> staleTime์€ 0 (๋งค๋ฒˆ ์žฌ๊ฒ€์ฆ)
const chatQuery = useQuery({
  queryKey: ['chats'],
  staleTime: 0,
});
 
// ๐Ÿ’ก ํŒ: cacheTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์•„์žˆ๋Š” ์‹œ๊ฐ„(Unused data)์ž…๋‹ˆ๋‹ค. 
// ๋ณดํ†ต staleTime๋ณด๋‹ค ๊ธธ๊ฒŒ ์„ค์ •ํ•˜์—ฌ ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ์‹œ ์ด์ ์„ ์–ป์Šต๋‹ˆ๋‹ค.

๐Ÿ“Š ๋ ˆ๋ฒจ๋ณ„ ๋‹ต๋ณ€ ๊ฐ€์ด๋“œ (Self-Check)

  • Level 1 (Junior): "staleTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ ์„ ํ•œ ์‹œ๊ฐ„์ด๊ณ , cacheTime์€ ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์•„์žˆ๋Š” ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค."
  • Level 2 (Senior): "๋ฐ์ดํ„ฐ๊ฐ€ 'stale'ํ•ด์ ธ๋„ ์ฆ‰์‹œ ์‚ฌ๋ผ์ง€์ง€ ์•Š๊ณ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์žฌ๊ฒ€์ฆ(Revalidation) ํŠธ๋ฆฌ๊ฑฐ๊ฐ€ ๋จ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. cacheTime์€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ž‘๋™ํ•˜๊ธฐ ์ „๊นŒ์ง€์˜ ์—ฌ์œ  ์‹œ๊ฐ„์ž„์„ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค."
  • Level 3 (Specialist): "์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ด€์ ์—์„œ staleTime์„ ๋ฌดํ•œ(Infinity)์œผ๋กœ ๋‘๊ณ  Invalidate๋ฅผ ์ˆ˜๋™์œผ๋กœ ์กฐ์ ˆํ•˜๋Š” '์ •์  ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ' ์ „๋žต์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” Prefetching๊ณผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‹ค์Œ ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ stale ์ƒํƒœ๋กœ ์บ์‹œ์— ๋„ฃ์–ด๋‘์–ด ์ „ํ™˜ ์†๋„๋ฅผ 0์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ธฐ๋ฒ•์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค."

Q72. ์ฟผ๋ฆฌ ๋ฌดํšจํ™”(Invalidation)์™€ ์ˆ˜๋™ ์—…๋ฐ์ดํŠธ(SetQueryData)์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

  • ๐ŸŽฏ ์ถœ์ œ ์˜๋„: ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์ง€ํ‚ค๋Š” ๋‘ ๊ฐ€์ง€ ์ „๋žต์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ์ดํ•ดํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ก ํ•ต์‹ฌ ์›๋ฆฌ & ๋‹ต๋ณ€: Invalidate๋Š” "์ด ๋ฐ์ดํ„ฐ๋Š” ์ƒํ–ˆ์œผ๋‹ˆ ์„œ๋ฒ„์—์„œ ๋‹ค์‹œ ๋ฐ›์•„์™€!"๋ผ๊ณ  ์„ ์–ธํ•˜๋Š” ์•ˆ์ „ํ•œ ๋ฐฉ์‹์ด๋ฉฐ, ํ•ญ์ƒ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด SetQueryData๋Š” ์„œ๋ฒ„ ์‘๋‹ต ์ „์— ์บ์‹œ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋Š” '๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ'์— ์ฃผ๋กœ ์“ฐ์ด๋ฉฐ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ํ•œ ๋ฒˆ ์•„๋‚„ ์ˆ˜ ์žˆ์–ด ํ›จ์”ฌ ๋น ๋ฆ…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์‹ฌ์Šค๋Ÿฝ๊ฒŒ ์กฐ์ž‘ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ณต์žก๋„๊ฐ€ ๋†’๊ณ  ์‹คํŒจ ์‹œ ๋กค๋ฐฑ ๋กœ์ง์ด ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

Q85. ๋ฌดํ•œ ์Šคํฌ๋กค(Infinite Scroll) ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ๊ณ ๋ คํ•ด์•ผ ํ•  ํ•ต์‹ฌ ์„ฑ๋Šฅ ํฌ์ธํŠธ๋Š”?

  • ๐ŸŽฏ ์ถœ์ œ ์˜๋„: ๋ฐ์ดํ„ฐ ์–‘์ด ๋Š˜์–ด๋‚  ๋•Œ ์ƒ๊ธฐ๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ํ•œ๊ณ„์™€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์กฐํ™”์‹œํ‚ค๋Š” ๋Šฅ๋ ฅ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ก ํ•ต์‹ฌ ์›๋ฆฌ & ๋‹ต๋ณ€: ์ฒซ์งธ, useInfiniteQuery๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ pageParam ๋‹จ์œ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์ด์ „/๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ๋งค๋„๋Ÿฝ๊ฒŒ ํŽ˜์นญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‘˜์งธ, ๋ Œ๋”๋ง ์ตœ์ ํ™”์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์ง€๋ฉด DOM ๋…ธ๋“œ๊ฐ€ ๋ฌด๊ฑฐ์›Œ์ง€๋ฏ€๋กœ Intersection Observer๋Š” ๋ฌผ๋ก , ํ™”๋ฉด ๋ฐ–์˜ ๋…ธ๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” '์œˆ๋„์ž‰(Windowing/Virtualization)' ๊ธฐ๋ฒ•์„ ๋ณ‘ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์Šคํฌ๋กค ์ด๋ฒคํŠธ ๋””๋ฐ”์šด์‹ฑ์ด๋‚˜ ์“ฐ๋กœํ‹€๋ง์„ ํ†ตํ•ด ๋ถˆํ•„์š”ํ•œ API ํ˜ธ์ถœ ํญํ’์„ ๋ง‰์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q91. ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ™์€ Query๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ํ•œ ๋ฒˆ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ์›๋ฆฌ๋Š”?

  • ๐ŸŽฏ ์ถœ์ œ ์˜๋„: ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋‚ด๋ถ€ ์บ์‹ฑ ๋ ˆ์ด์–ด์™€ ์š”์ฒญ ๋ณ‘ํ•ฉ(Request Deduplication) ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ก ํ•ต์‹ฌ ์›๋ฆฌ & ๋‹ต๋ณ€: React Query๋Š” QueryClient๋ผ๋Š” ์ „์—ญ ์ €์žฅ์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ๋ชจ๋“  ์ฟผ๋ฆฌ๋Š” ๊ณ ์œ ํ•œ QueryKey๋ฅผ ์‹๋ณ„์ž๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋™์ผํ•œ ํ‚ค๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ˜„์žฌ '์ง„ํ–‰ ์ค‘์ธ ์š”์ฒญ(Pending Promise)'์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋ณ„๋„์˜ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ ์—†์ด ํ•ด๋‹น ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋ผ๋ฉด ์บ์‹œ๋œ ๊ฐ’์„ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ staleTime์— ๋”ฐ๋ผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์—…๋ฐ์ดํŠธ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํŒŒํŽธํ™”๋˜์–ด ์žˆ์–ด๋„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ๋ณต๊ธฐ ์ผ๊ธฐ

์˜ค๋Š˜ '์„œ๋ฒ„ ์ƒํƒœ'๋ผ๋Š” ๊ฐœ๋…์„ ๋ฐฐ์šฐ๊ณ  ๋‚˜์„œ ๊ทธ๋™์•ˆ ๋‚ด๊ฐ€ ์™œ ๊ทธ๋ ‡๊ฒŒ ๋ถˆํ–‰(?)ํ–ˆ๋Š”์ง€ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค. ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด ์ง€์—ญ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ๋ถ€๋ฆฌ๋ ค ํ–ˆ๋˜ ์˜ค๋งŒํ•จ์ด ๋ฌธ์ œ์˜€๋‹ค. ์ด์ œ React Query๋ผ๋Š” ๋“ ๋“ ํ•œ ํŒŒํŠธ๋„ˆ์—๊ฒŒ ๋ณต์žกํ•œ ๊ฑด ๋งก๊ธฐ๊ณ , ๋‚˜๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์–ผ๋งˆ๋‚˜ ๋นจ๋ฆฌ ๋ณด์—ฌ์ค„์ง€๋งŒ ๊ณ ๋ฏผํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ’ก "์ตœ๊ณ ์˜ ์ „์—ญ ์ƒํƒœ๋Š” ์•„์˜ˆ ์ „์—ญ ์ƒํƒœ์— ๋„ฃ์ง€ ์•Š์•„๋„ ๋˜๋Š” ์ƒํƒœ๋‹ค."

๋‚ด์ผ์€ ๋ชจ๋˜ ์›น์˜ ํ‘œ์ค€, 'Next.js์™€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ'์˜ ์„ธ๊ณ„๋กœ ๋“ค์–ด๊ฐ„๋‹ค. App Router๋Š” ๋˜ ์–ผ๋งˆ๋‚˜ ํ˜๋ช…์ ์ผ์ง€, ์˜ํ˜ธ ๋‹˜๊ป˜ ์ œ๋Œ€๋กœ ๋ฐฐ์›Œ๋ด์•ผ์ง€! ๐Ÿš€๐Ÿ’ป