⚛️ ๋ฆฌ์•กํŠธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ธํŒ…: CRA vs Vite

โš›๏ธ ๋ฆฌ์•กํŠธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ธํŒ…: CRA vs Vite

๐Ÿš€ React Query ์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ ์—…๋ฐ์ดํŠธ ์‹ฌ์ธต ๊ฐ€์ด๋“œ

๐Ÿค” ์™œ '์ข‹์•„์š”' ๋ฒ„ํŠผ์€ ์ฆ‰์‹œ ๋นจ๊ฐœ์ ธ์•ผ ํ• ๊นŒ์š”?

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

ํ•ต์‹ฌ ๋ฌธ์ œ: ๋„คํŠธ์›Œํฌ ์†๋„๋Š” ์šฐ๋ฆฌ๊ฐ€ ํ†ต์ œํ•  ์ˆ˜ ์—†์ง€๋งŒ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์€ ํ†ต์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์„œ๋ฒ„ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋ฐœ์ƒํ•˜๋Š” '๋ฏธ์„ธํ•œ ๋ฉˆ์นซ๊ฑฐ๋ฆผ'์ด ์•ฑ์˜ ์™„์„ฑ๋„๋ฅผ ๋–จ์–ด๋œจ๋ฆฝ๋‹ˆ๋‹ค.

์˜ค๋Š˜ ์šฐ๋ฆฌ๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” React Query์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ, '์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ ์—…๋ฐ์ดํŠธ(Optimistic Updates)'์— ๋Œ€ํ•ด ๊นŠ์ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
์ด ๊ธฐ์ˆ ์„ ์ ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ๋ถ„์˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งˆ์น˜ ๋กœ์ปฌ ์•ฑ์ฒ˜๋Ÿผ ๋น ๋ฆฟ๋น ๋ฆฟํ•˜๊ฒŒ ๋А๊ปด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.



๐Ÿ’ก ์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ ์—…๋ฐ์ดํŠธ๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”?

์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, '๋‚™๊ด€์ (Optimistic)'์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ธกํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋ƒˆ์„ ๋•Œ, "์„ฑ๊ณตํ•  ๊ฒƒ์ด๋ผ ๊ฐ€์ •ํ•˜๊ณ " ๋ฏธ๋ฆฌ ํ™”๋ฉด(UI)์„ ๋ฐ”๊ฟ”๋ฒ„๋ฆฌ๋Š” ์ „๋žต์ด์ฃ .

  • ๐Ÿ”ธ ๊ธฐ์กด ๋ฐฉ์‹: ์š”์ฒญ ์ „์†ก → ์„œ๋ฒ„ ์ฒ˜๋ฆฌ ๋Œ€๊ธฐ (๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ) → ์‘๋‹ต ๋„์ฐฉ → UI ์—…๋ฐ์ดํŠธ
  • ๐Ÿ”น ์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ ๋ฐฉ์‹: ์š”์ฒญ ์ „์†ก → ์ฆ‰์‹œ UI ์—…๋ฐ์ดํŠธ → (๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์„œ๋ฒ„ ์ฒ˜๋ฆฌ) → ์„ฑ๊ณต ์‹œ ์œ ์ง€ / ์‹คํŒจ ์‹œ ๋กค๋ฐฑ

์ด ๋ฐฉ์‹์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋А๋ผ๋Š” ์ฒด๊ฐ ์†๋„(Perceived Performance)๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ๋†’์—ฌ์ค๋‹ˆ๋‹ค.
ํŠนํžˆ ์ข‹์•„์š”, ์ฆ๊ฒจ์ฐพ๊ธฐ, ํ•  ์ผ ์ฒดํฌ ๋“ฑ ์ƒํ˜ธ์ž‘์šฉ์ด ๋นˆ๋ฒˆํ•œ ๊ธฐ๋Šฅ์—์„œ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

๐Ÿ› ️ React Query์—์„œ์˜ ๋™์ž‘ ์›๋ฆฌ 3๋‹จ๊ณ„

React Query(TanStack Query)๋Š” ์ด๋Ÿฌํ•œ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด useMutation ํ›… ๋‚ด๋ถ€์— ์ •๊ตํ•œ ๋ผ์ดํ”„์‚ฌ์ดํด ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
ํฌ๊ฒŒ 3๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆ„์–ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ๊ณ„ (LifeCycle) ์—ญํ•  ๋ฐ ์„ค๋ช…
1. onMutate ์š”์ฒญ์ด ์‹œ์ž‘๋˜๋Š” ์ฆ‰์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฅผ ์Šค๋ƒ…์ƒท(๋ฐฑ์—…) ์ฐ๊ณ , UI๋ฅผ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๊ฐ•์ œ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
2. onError ์„œ๋ฒ„ ์š”์ฒญ์ด ์‹คํŒจํ–ˆ์„ ๋•Œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
onMutate์—์„œ ์ €์žฅํ•ด๋‘” ์Šค๋ƒ…์ƒท์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋กค๋ฐฑ(๋ณต๊ตฌ)ํ•˜์—ฌ ์˜ค๋ฅ˜ ์—†๋Š” ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.
3. onSettled ์„ฑ๊ณตํ•˜๋“  ์‹คํŒจํ•˜๋“  ์š”์ฒญ์ด ๋๋‚˜๋ฉด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
์„œ๋ฒ„์˜ ์ตœ์‹  ๋ฐ์ดํ„ฐ์™€ ๋™๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”(invalidate)ํ•˜์—ฌ ์žฌ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
Flowchart of React Query's useMutation lifecycle showing onMutate, onError, and onSettled.

๐Ÿ’ป ์‹ค์ œ ์ฝ”๋“œ๋กœ ์‚ดํŽด๋ณด๊ธฐ

๋ฐฑ๋ฌธ์ด ๋ถˆ์—ฌ์ผ๊ฒฌ์ด์ฃ ? 'ํ•  ์ผ ๋ชฉ๋ก(Todo)'์— ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜ ์ฝ”๋“œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ '์ถ”๊ฐ€' ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด์ž๋งˆ์ž ๋ชฉ๋ก์— ์ƒˆ ํ•ญ๋ชฉ์ด ๋ณด์ด๋Š” ๋งˆ๋ฒ•์„ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

const mutation = useMutation({
  mutationFn: newTodo => axios.post('/todos', newTodo),

  // 1. ์š”์ฒญ ์ฆ‰์‹œ ์‹คํ–‰ (๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ)
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const previousTodos = queryClient.getQueryData(['todos']);

    // UI๋ฅผ ๋ฏธ๋ฆฌ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค!
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);

    return { previousTodos }; // ์Šค๋ƒ…์ƒท ๋ฐ˜ํ™˜
  },

  // 2. ์‹คํŒจ ์‹œ ๋กค๋ฐฑ
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previousTodos);
  },

  // 3. ์™„๋ฃŒ ํ›„ ๋™๊ธฐํ™”
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  }
});

์ด ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž๋Š” ๋„คํŠธ์›Œํฌ ์†๋„์™€ ๊ด€๊ณ„์—†์ด ์ฆ‰๊ฐ์ ์ธ ๋ฐ˜์‘์„ ๊ฒฝํ—˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
์‹คํŒจํ•  ํ™•๋ฅ ์ด ๋‚ฎ์€ ์š”์ฒญ์— ์ ์šฉํ• ์ˆ˜๋ก ๊ทธ ํšจ๊ณผ๋Š” ๋ฐฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ“ ์š”์•ฝ ๋ฐ ์‹ค์ฒœ ๊ฐ€์ด๋“œ

๐Ÿ‘ ์žฅ์ 

  • ์•ฑ์ด ํ›จ์”ฌ ๋น ๋ฅด๊ณ  ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋А๊ปด์ง.
  • ์„œ๋ฒ„ ์‘๋‹ต ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋™์•ˆ ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์Œ.
  • ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ์ˆ˜์ค€์˜ ๊ณ ํ’ˆ์งˆ UX ์ œ๊ณต.

⚠️ ์ฃผ์˜์‚ฌํ•ญ

  • ์‹คํŒจ ํ™•๋ฅ ์ด ๋†’์€ ์š”์ฒญ(๊ฒฐ์ œ ๋“ฑ)์—๋Š” ์‚ฌ์šฉ ์ž์ œ.
  • ๋กค๋ฐฑ ๋กœ์ง(onError)์„ ๊ผผ๊ผผํ•˜๊ฒŒ ์ž‘์„ฑํ•ด์•ผ ํ•จ.
  • ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์ด ๋งค์šฐ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ์—๋Š” ์‹ ์ค‘ํ•˜๊ฒŒ ์ ์šฉ.


Illustration of a satisfied user looking at a smartphone with a responsive app.


React Query์˜ ์˜ตํ‹ฐ๋ฏธ์Šคํ‹ฑ ์—…๋ฐ์ดํŠธ๋Š” ๋‹จ์ˆœํžˆ ๊ธฐ์ˆ ์ ์ธ ๊ธฐ๊ต๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž์—๊ฒŒ "๋‹น์‹ ์˜ ์š”์ฒญ์€ ์ด๋ฏธ ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ์‹ ๋ขฐ๋ฅผ ์ฃผ๋Š” UX ๋””์ž์ธ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.
์˜ค๋Š˜ ๋‹น์žฅ ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ์— ์žˆ๋Š” '์ข‹์•„์š”' ๋ฒ„ํŠผ๋ถ€ํ„ฐ ์ด ๊ธฐ์ˆ ์„ ์ ์šฉํ•ด๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ์š”?

๐Ÿš€ ์ง€๊ธˆ ๋ฐ”๋กœ ์‹ค์Šตํ•ด๋ณด์„ธ์š”!
React Query ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ผœ๊ณ , `useMutation` ํ›…์„ ์ž‘์„ฑํ•ด๋ณด์„ธ์š”. ์ž‘์€ ๋ณ€ํ™”๊ฐ€ ํฐ ์ฐจ์ด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๋Œ“๊ธ€ ์“ฐ๊ธฐ