2025 . ์˜ค์€

repo

์ค‘๋ณต fetch ํ•ด๊ฒฐํ•˜๊ธฐ

๐Ÿ“Œ ๋ฌธ์ œ ์ •์˜

์ตœ๊ทผ Inertia.js + React + Laravel ์กฐํ•ฉ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ์ค‘, Context ๋‚ด๋ถ€์—์„œ fetch๊ฐ€ ๋‘ ๋ฒˆ ์ด์ƒ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ์Šต๋‹ˆ๋‹ค.

  • Context ๋‚ด๋ถ€์—์„œ ์ดˆ๊ธฐ fetch ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์žˆ์—ˆ๊ณ , ์ด ์š”์ฒญ์€ ํŠน์ • state์— ์˜์กดํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ ํŽ˜์ด์ง€ ์ด๋™ ์‹œ Context๊ฐ€ ์žฌ๋งˆ์šดํŠธ๋˜๋ฉฐ initialState๊ฐ€ ๋‹ค์‹œ ์ ์šฉ๋˜์—ˆ๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ fetch๊ฐ€ ๋‘ ๋ฒˆ ์ด์ƒ ์‹คํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด ๋“œ๋ฌผ๊ฒŒ ์ดˆ๊ธฐ๊ฐ’์ด UI์— ๋ฐ˜์˜๋˜๋Š” ๋ฌธ์ œ, ํ˜น์€ ๊นœ๋นก์ž„(flickering)์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

โš™๏ธ ๋ฌธ์ œ ์›์ธ ๋ถ„์„

Inertia ํ™˜๊ฒฝ์—์„  ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ œ์•ฝ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค:

  1. Context Provider๋ฅผ <App /> ์œ„์— ๋‘˜ ์ˆ˜ ์—†๋Š” ๊ตฌ์กฐ
    โ†’ Laravel์—์„œ ๋ Œ๋”๋ง๋˜๋Š” Blade ํŒŒ์ผ์ด ์žˆ๊ณ , ๊ทธ ์•ˆ์—์„œ React๊ฐ€ ๋งˆ์šดํŠธ๋˜๊ธฐ ๋•Œ๋ฌธ์— Context๋ฅผ ๋ฃจํŠธ ์ตœ์ƒ๋‹จ์—์„œ ๊ฐ์‹ธ๋Š” ๊ฒŒ ์‚ฌ์‹ค์ƒ ๋ถˆ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค.

  2. Inertia ํŽ˜์ด์ง€ ์ด๋™ ์‹œ ์ „์ฒด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ๋งˆ์šดํŠธ๋จ
    โ†’ ๋ผ์šฐํ„ฐ๋ณด๋‹ค ํ•˜์œ„์—์„œ Provider๊ฐ€ ์„ ์–ธ๋ผ ์žˆ์œผ๋ฏ€๋กœ ํŽ˜์ด์ง€ ์ด๋™๋งˆ๋‹ค Context๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์ดˆ๊ธฐํ™”๋จ.

  3. useReducer์˜ initialState๋Š” ์ฒซ ๋งˆ์šดํŠธ ์‹œ ํ•œ ๋ฒˆ๋งŒ ์ ์šฉ๋จ
    โ†’ ํ•˜์ง€๋งŒ ํŽ˜์ด์ง€ ์ „ํ™˜๋งˆ๋‹ค Context ์ž์ฒด๊ฐ€ ์žฌ๋งˆ์šดํŠธ๋˜๋ฏ€๋กœ, ๊ฒฐ๊ณผ์ ์œผ๋กœ initialState๊ฐ€ ์ž์ฃผ ์žฌ์ ์šฉ๋˜๊ณ  fetch๊ฐ€ ๋ฐ˜๋ณต๋จ.

๐Ÿ› ๏ธ ํ•ด๊ฒฐ ๊ณผ์ •

์‹œ๋„ 1. initialState์— pageProps๋ฅผ ํ™œ์šฉ

๋‹คํ–‰ํžˆ state๊ฐ€ url์— ์˜์กดํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ž˜์ฒ˜๋Ÿผ initialState๋ฅผ ๋™์ ์œผ๋กœ ์„ค์ •ํ•ด ๋ฌธ์ œ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค:

const pageProps = usePage();
const [state, dispatch] = useReducer(
  adminReducer,
  getInitialAdminState(pageProps), // ์ดˆ๊ธฐ๊ฐ’์„ props๋กœ๋ถ€ํ„ฐ ์„ค์ •
);

ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๋ฐฉ๋ฒ•์€ ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ดˆ๊ธฐํ™” ๊ธฐ์ค€์ด URL์ด๋‚˜ props์ฒ˜๋Ÿผ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ฃผ์–ด์ง€๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž์˜ ํด๋ฆญ์ด๋‚˜ ์ž…๋ ฅ์ฒ˜๋Ÿผ ๋‚ด๋ถ€ ์ƒํƒœ ๋ณ€ํ™”์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ์—” ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์‹œ๋„ 2. ์ดˆ๊ธฐ๊ฐ’์„ null๋กœ ํ•˜๊ณ , useEffect์—์„œ ์กฐ๊ฑด๋ถ€ fetch

๋ณด๋‹ค ๋ฒ”์šฉ์ ์ธ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ์•„๋ž˜ ๋ฐฉ์‹์„ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค:

  1. ์ƒํƒœ๋ฅผ ์ตœ์†Œํ•œ์œผ๋กœ ์ •์˜ (targetId: null)
  2. targetId๊ฐ€ ์„ค์ •๋˜๋ฉด ๊ทธ๋•Œ fetch๋ฅผ ํŠธ๋ฆฌ๊ฑฐ
  3. ์›ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ dispatch๋ฅผ ํ†ตํ•ด targetId ์„ค์ •
const [state, dispatch] = useReducer(reducer, {
  targetId: null,
  data: null,
  loading: false,
});

useEffect(() => {
  if (!state.targetId) return;

  dispatch({ type: 'FETCH_START' });
  fetchData(state.targetId)
    .then((data) => dispatch({ type: 'FETCH_SUCCESS', payload: data }))
    .catch(() => dispatch({ type: 'FETCH_FAIL' }));
}, [state.targetId]);

์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค:

onClick={() => dispatch({ type: 'SET_ID', payload: 123 })}

โœจ ๋ฐฐ์šด ์  & ์ธ์‚ฌ์ดํŠธ

  • Context๋Š” ์œ„์น˜๊ฐ€ ์ „๋ถ€๋‹ค.
    ์ตœ์ƒ๋‹จ์—์„œ ๊ฐ์‹ธ์ง€ ๋ชปํ•˜๋Š” ๊ตฌ์กฐ๋ผ๋ฉด, initialState์˜ ์˜์กด์„ฑ๋งŒ์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌ์„ฑํ•˜๊ธด ์–ด๋ ต๋‹ค.

  • ์ดˆ๊ธฐ๊ฐ’์€ ์ตœ์†Œํ™”ํ•˜๊ณ , side-effect๋กœ ๋™์ž‘์„ ๋ถ„๋ฆฌํ•˜์ž.
    useEffect๋Š” ์˜์กด์„ฑ ๊ธฐ๋ฐ˜์œผ๋กœ fetch ๋™์ž‘์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ํ›จ์”ฌ ์œ ์—ฐํ•˜๊ณ  ์•ˆ์ •์ ์ด๋‹ค.

  • ์ƒํƒœ ๊ธฐ๋ฐ˜ ์กฐ๊ฑด๋ถ€ fetch๋Š” ๊นœ๋นก์ž„์„ ์ค„์ด๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค.
    ํŠนํžˆ ์ดˆ๊ธฐ๊ฐ’์ด null์ธ ์ƒํƒœ์—์„œ fetch๋ฅผ ๋ง‰๊ณ , ํ•„์š”ํ•œ ์‹œ์ ์—๋งŒ ๋ช…ํ™•ํžˆ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ๋ฐฉ์‹์€ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ์•ˆ์ •์„ฑ ์ธก๋ฉด์—์„œ ๋งค์šฐ ๊ฐ•๋ ฅํ–ˆ๋‹ค.

  • Context๋กœ ๋ชจ๋“  ๊ฑธ ํ•ด๊ฒฐํ•˜๋ ค ํ•˜์ง€ ๋ง์ž.
    ํŒ€ ์ƒํ™ฉ์ƒ ์ „์—ญ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ์ง€ ์•Š๊ธฐ๋กœ ํ–ˆ์ง€๋งŒ, ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ๋ฐ˜๋ณต๋œ๋‹ค๋ฉด Zustand๋ฅผ ์“ฐ๋Š”๊ฒŒ ํŽธํ•œ๋‹ค!