๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ ํ•˜๊ธฐ

minyoung
  • #๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ
  • #optimistic
  • #@tanstack/react-query
  • #Jotai

์•ˆ๋…•ํ•˜์„ธ์š”, ๋…ธ๋จธ์Šค ํ”„๋ก ํŠธ์—”๋“œ ํŒ€ ๊น€๋ฏผ์˜์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•œ ๋ฐฉ์‹์„ ๊ณต์œ ํ•ด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

Jotai๋กœ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ๋˜ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ ์ƒํƒœ

fromm ์•ฑ์€ ํŒฌ๊ณผ ์•„ํ‹ฐ์ŠคํŠธ๊ฐ€ ์†Œํ†ตํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธฐ๋Šฅ์ธ ์ฑ„๋„ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์—๋Š” ์ฑ„๋„์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์ƒํƒœ๋ฅผ Jotai๋กœ ๊ด€๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฏธ๋””์–ด ํฌ์ŠคํŠธ API๋ฅผ ์š”์ฒญํ•ด ๋ฐ์ดํ„ฐ๋ฅผ Atom์— ์ €์žฅํ•˜๊ณ , ์ข‹์•„์š” ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด Atom์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•ด ๋‹ค์‹œ ๋ฐ˜์˜ํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ์—ฌ๋Ÿฌ ์ด์ ๋„ ์žˆ์—ˆ์ง€๋งŒ, ๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๋ถˆ์ผ์น˜์™€ ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง€๋Š” ์น˜๋ช…์ ์ธ ๋‹จ์ ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋กœ ์ธํ•ด ํ•„์š”ํ•œ ์‹œ์ ์— ๋งค๋ฒˆ ์ „์—ญ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ–ˆ๊ณ , ๊ทธ ํƒ€์ด๋ฐ ์ด์Šˆ๋กœ ๊ฒฐ๊ตญ ๋นˆ ๋ฐฐ์—ด์ด ๋˜์–ด ๋ฐ์ดํ„ฐ๊ฐ€ ์•„์˜ˆ ๋ณด์ด์ง€ ์•Š๋Š” ์ด์Šˆ๋ฅผ ์ดˆ๋ž˜ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.์„œ๋ฒ„ ์‘๋‹ต๊ณผ ์ƒ๊ด€์—†์ด ์ผ๋‹จ ํด๋ผ์ด์–ธํŠธ ๊ฐ’์ด ๋จผ์ € ๋ณ€๊ฒฝ๋˜๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ๋Œ“๊ธ€ ์Šคํƒ๋ทฐ์—์„œ โ€˜์ข‹์•„์š”โ€™๋ฅผ ๋ˆŒ๋Ÿฌ UI๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์ง€๋งŒ, ์‹ค์ œ ์„œ๋ฒ„ ์š”์ฒญ์ด ์‹คํŒจํ•œ ์ƒํƒœ์—์„œ ์Šคํƒ๋ทฐ๋ฅผ ๋‹ซ์•˜๋‹ค๊ฐ€ ๋‹ค์‹œ ์—ด๋ฉด โ€˜์ข‹์•„์š”โ€™๊ฐ€ ๋ฐ˜์˜๋˜์ง€ ์•Š์•„ ์‚ฌ์šฉ์ž๊ฐ€ ์˜ค๋ฅ˜๋กœ ์ธ์‹ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„ ์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด์„œ ๋ฐ›์•„์˜ค๋Š” ๋ฐ์ดํ„ฐ๋Š” @tanstack/react-query ์—์„œ๋งŒ ๊ด€๋ฆฌํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” Jotai๋กœ ๊ด€๋ฆฌํ•˜์ž๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ ธ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ์œ ์ง€๋ณด์ˆ˜ ์‹œ ๋ณต์žก์„ฑ์„ ์ค„์ด๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๊ด€์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด์˜ Jotai๋กœ ๊ด€๋ฆฌํ•˜๋˜ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ @tanstack/react-query ๋กœ ์ˆ˜์ •ํ•˜๋Š” ๊ณผ์ •์—์„œ @tanstack/react-query์˜ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ(Optimisic Update)์— ๋Œ€ํ•ด ์•Œ๊ฒŒ๋˜์—ˆ๊ณ , ์ด๋ฅผ ์„œ๋น„์Šค์— ์ ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ?

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ(Optimistic Update)๋Š” ์„œ๋ฒ„ API ์‘๋‹ต์„ ๋ฐ›๊ธฐ ์ „์— UI๋ฅผ ๋จผ์ € ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๊ฒฐ๊ตญ ์„ฑ๊ณต ์‘๋‹ต์„ ๋ฐ›์„ ๊ฒƒ์ด๋ผ๊ณ  ๋‚™๊ด€์ ์œผ๋กœ ๊ฐ€์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ๋น ๋ฅด๊ณ  ๋ฐ˜์‘์„ฑ ๋†’์€ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฌด์กฐ๊ฑด ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„๊นŒ์š”?

ํฌ์ŠคํŠธ ์ž‘์„ฑ/์‚ญ์ œ/์ข‹์•„์š”, ๋Œ“๊ธ€ ์ž‘์„ฑ/์‚ญ์ œ/์‹ ๊ณ /์ข‹์•„์š” ๋“ฑ @tanstack/react-query๋ฅผ ์ ์šฉํ•ด์•ผ ํ•  ์„œ๋ฒ„ ์ƒํƒœ๋Š” ๋งค์šฐ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๋ถ€๋ถ„์— ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€์ง€ ์˜๋ฌธ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋Œ“๊ธ€ ์ž‘์„ฑ input์— ๊ธˆ์น™์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด API ์š”์ฒญ ์‹œ ์—๋Ÿฌ๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ๊ธˆ์น™์–ด ์—๋Ÿฌ์ผ ๊ฒฝ์šฐ, ํ…์ŠคํŠธ๊ฐ€ input์— ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๊ณ  ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œน๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋Œ“๊ธ€ ์ž‘์„ฑ์— ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ ์šฉํ•˜๋ฉด, ๋Œ“๊ธ€์ด ์ด๋ฏธ ์ž‘์„ฑ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๊ธฐ๋Šฅ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ํŒ๋‹จํ•˜์—ฌ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ ์ ˆํ•˜๊ฒŒ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๋ฐฉ์‹

์ €ํฌ ํŒ€์€ ํฌ์ŠคํŠธ, ๋Œ“๊ธ€, ๋‹ต๊ธ€ ๋“ฑ ์ข‹์•„์š”/์ข‹์•„์š” ์ทจ์†Œ ๊ธฐ๋Šฅ์— ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ๊ฐ€ ๊ตฌํ˜„ํ•œ ๋ฏธ๋””์–ด ํฌ์ŠคํŠธ ์ข‹์•„์š” ๊ธฐ๋Šฅ์˜ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • mutationFn ์ •์˜ํ•˜๊ธฐ
    • ์ข‹์•„์š”, ์ข‹์•„์š” ์ทจ์†Œ ๊ธฐ๋Šฅ์€ ์—ฌ๋Ÿฌ๋ฒˆ ๋น ๋ฅด๊ฒŒ ํด๋ฆญ ์‹œ ๋‹ค์ˆ˜์˜ API ์š”์ฒญ์ด ๊ฐˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋””๋ฐ”์šด์Šค(debounce)๋ฅผ ์ ์šฉํ•˜์—ฌ API ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
      mutationFn: (isLike: boolean) => debouncedMutationFn({ isLike }),
  • onMutate ์ •์˜ํ•˜๊ธฐ : onMutate ๋Š” mutation์ด ์ˆ˜ํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    • 1๏ธโƒฃ queryClient.cancelQueries ์ฒ˜๋ฆฌ: ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ์™€ ๋ณ„๊ฐœ๋กœ ๊ด€๋ จ ๋ฐ์ดํ„ฐ์˜ refetch๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๋„์ฐฉ ํƒ€์ด๋ฐ์ด ์—‡๊ฐˆ๋ ค ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ ์ด์ „ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ฒŒ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด queryClient.cancelQueries๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ โ€˜์ข‹์•„์š”โ€™ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฆ„๊ณผ ๋™์‹œ์— ํฌ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ refetch๋˜๋Š” ์ƒํ™ฉ์—์„œ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๊ฐ€ ๋จผ์ € ์ ์šฉ๋œ ํ›„ refetch๊ฐ€ ์ผ์–ด๋‚˜๋ฉด ์ด์ „ UI ์ƒํƒœ๊ฐ€ ๋‹ค์‹œ ๋ณด์ด๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • 2๏ธโƒฃย ์ด์ „ ๋ฐ์ดํ„ฐ return : queryClient.getQueryData๋ฅผ ํ†ตํ•ด ์ด์ „ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ context์— ์ €์žฅํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • 3๏ธโƒฃย ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ : queryClient.setQueryData ๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ๋  ๋ฐ์ดํ„ฐ (nextData)๋ฅผ ๋ฏธ๋ฆฌ ์—…๋ฐ์ดํŠธ ํ•ด์ค๋‹ˆ๋‹ค.
      onMutate: async (isLike: boolean) => {
          await queryClient.cancelQueries({ queryKey: getMediaPostKey }); // 1๏ธโƒฃ
          
          const previousData = queryClient.getQueryData<MediaPostVo['post']>(getMediaPostKey);  // 2๏ธโƒฃ 
          
          if (!previousData) return;
          
          const nextData = () => isLike ? likeById(previousData) : dislikeById(previousData);
          queryClient.setQueryData(getMediaPostKey, nextData); // 3๏ธโƒฃ
      
          return { previousData };
      },
  • onError ์ •์˜ํ•˜๊ธฐ : onError๋Š” mutation์ด ์‹คํŒจํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    • setQueryData๋ฅผ ํ†ตํ•ด context์— ์ €์žฅํ•ด ๋‘” ์ด์ „ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์„ธํŒ…ํ•˜์—ฌ UI๋ฅผ ๋ณต๊ตฌํ•ฉ๋‹ˆ๋‹ค.
      onError: (_err, _vars, context) => {
          if (context?.previousData) {
              queryClient.setQueryData(getMediaPostKey, { ...context.previousData });
          }
      }
  • onSettled ์ •์˜ํ•˜๊ธฐ : onSettled๋Š” ์š”์ฒญ ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจํ•œ ํ›„ ํ•ญ์ƒ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    • queryClient.invalidateQueries ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ตœ์ข…์ ์œผ๋กœ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋™๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
      onSettled: () =>
          queryClient.invalidateQueries({ queryKey: getMediaPostKey }),

๊ณต์‹๋ฌธ์„œ์— ๋‚˜์™€์žˆ๋Š” onSettled ์ •์˜๋Š” ์ด๋ฒˆ ์ž‘์—…์—์„œ ์ œ์™ธํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฌดํ•œ์Šคํฌ๋กค๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ๋ฆฌ์ŠคํŠธ์—์„œ ์ •ํ™•ํ•œ ํ•ด๋‹น ํŽ˜์ด์ง€์˜ querykey์— ํ•„์š”ํ•œ id ๊ฐ’์„ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋Œ“๊ธ€ ์Šคํƒ๋ทฐ๋ฅผ ๋‹ค์‹œ ์—ด๊ฑฐ๋‚˜ ํ•ด๋‹น ํฌ์ŠคํŠธ ํƒญ์„ ๋ˆŒ๋ €์„ ๋•Œ ์ตœ์‹ ํ™”ํ•˜๋„๋ก ์„ค์ •ํ•ด๋‘์—ˆ๊ธฐ ๋•Œ๋ฌธ์— onSettled ์‹œ queryClient.invalidateQueris ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.


๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ์ ์šฉ ์ „ ํ™”๋ฉด


๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ์ ์šฉ ๊ฒฐ๊ณผ ํ™”๋ฉด
โœ…ย ๋””๋ฐ”์šด์Šค ์ ์šฉ
โœ…ย ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ์ ์šฉ


์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ํ™”๋ฉด

์ถ”๊ฐ€์ ์œผ๋กœ ๊ณ ๋ฏผํ•œ ๋ถ€๋ถ„

invalidateQueries vs refetchQueries

๋Œ“๊ธ€ ์Šคํƒ๋ทฐ๊ฐ€ ์—ด๋ฆด ๋•Œ๋งˆ๋‹ค ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ๊ณ ๋ฏผํ–ˆ๋˜ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. @tanstack/react-query์—์„œ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ๋ฐฉ๋ฒ•์€ refetchQueries์™€ invalidateQueries์ž…๋‹ˆ๋‹ค. ์ด ๋‘˜์€ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ์‹์—์„œ ์ค‘์š”ํ•œ ์ฐจ์ด๋ฅผ ๋ณด์ž…๋‹ˆ๋‹ค.

refetchQueries๋Š” ๊ฐ•์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ โ€˜๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š”โ€™ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ˜ธ์ถœ๋˜๋ฉด ์บ์‹œ ์ƒํƒœ์™€ ์ƒ๊ด€์—†์ด ๋ฌด์กฐ๊ฑด ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ƒˆ๋กœ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ refetch๋Š” ํŠน์ • ์‹œ์ ์— ์ตœ์‹  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด, invalidateQueries๋Š” ๋ฐ์ดํ„ฐ๋ฅผ โ€˜๋ฌดํšจํ™”ํ•˜๋Š”โ€™ ๋™์ž‘์ž…๋‹ˆ๋‹ค. ํŠน์ • ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ โ€œ์˜ค๋ž˜๋œ(stale) ์ƒํƒœโ€๋กœ ํ‘œ์‹œํ•˜๊ณ , ์ด ์ฟผ๋ฆฌ๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ค„์ด๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ๋ณด๋ฉด์„œ ๋” ๋น ๋ฅธ ๋ฐ˜์‘์„ฑ์„ ๋А๋‚„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

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

์ฟผ๋ฆฌํ‚ค ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•

Jotai์—์„œ @tanstack/react-query ๋กœ ์ „ํ™˜ํ•˜๋ฉฐ ์ฟผ๋ฆฌ ํ‚ค ๊ด€๋ฆฌ ๋ฐฉ์‹๋„ ๋ณ€๊ฒฝ๋˜์–ด ์ €ํฌ์˜ ๋ฐฉ์‹์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด์—๋Š” ์‚ฌ์šฉํ•˜๋Š” ๊ณณ๋งˆ๋‹ค ํ‚ค๋ฅผ ์ผ์ผ์ด ์ ์–ด์ฃผ๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค๋ฉด, ๋ณ€๊ฒฝ๋œ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

export const QueryKeys = {
  // ๋Œ“๊ธ€
  comment: {
      rootKey: ['getComment'],
      detailKey: ({ tab, commentId }: { tab: TabT; commentId?: number }) => ['getComment', { tab, commentId }]
  },
  ...
  
  // ๋ฏธ๋””์–ด
  mediaPost: {
      rootKey: ['getMediaPost'],
      detailKey: ({ postId }: { postId: number }) => ['getMediaPost', { postId }]
  },
  
	...
}

ํ•˜๋‚˜์˜ QueryKeys ๋ผ๋Š” Constant ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๊ทธ ๋‚ด๋ถ€์— ๊ฐ ํ‚ค์— ๋งž๋Š” ์ด๋ฆ„(ex. comment)์„ ์„ค์ •ํ•˜์—ฌ ๋‚ด๋ถ€์— rootKey, detailKey ๋กœ ๋‚˜๋ˆ„์–ด ๊ด€๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•œ๊ณณ์— ๋ชจ์•„์„œ ์ฟผ๋ฆฌ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•˜๋‹ค ๋ณด๋‹ˆ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ์ข‹์•„์กŒ๊ณ , ์ฟผ๋ฆฌ ํ‚ค๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ•˜๋Š” ์‹ค์ˆ˜๋„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

// ์‚ฌ์šฉํ•˜๋Š” ๊ณณ ์˜ˆ์‹œ
const getMediaPostKey = QueryKeys.mediaPost.detailKey({ postId });

๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” fromm ์ฑ„๋„ ์„œ๋น„์Šค์˜ ์„œ๋ฒ„๋ฐ์ดํ„ฐ๋ฅผ @tanstack/react-query ๋กœ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋œ ๋ฐฐ๊ฒฝ๋ถ€ํ„ฐ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ, ์ฟผ๋ฆฌ ํ‚ค ๊ด€๋ฆฌ ๋ฐฉ์‹๊นŒ์ง€ ๊ณต์œ ๋“œ๋ ธ์Šต๋‹ˆ๋‹ค.

Jotai๋กœ ๊ด€๋ฆฌํ•˜๋˜ ์ดˆ๊ธฐ ๋ฌธ์ œ๋ถ€ํ„ฐ ์ตœ์ ์˜ ์ฟผ๋ฆฌ ํ‚ค๋ฅผ ์ฐพ์•„ ์ ์šฉํ•˜๊ธฐ๊นŒ์ง€ ์ˆ˜๋งŽ์€ ๊ณ ๋ฏผ๊ณผ ์‹œํ–‰์ฐฉ์˜ค๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ, ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , โ€˜์ข‹์•„์š”โ€™์™€ ๊ฐ™์€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž์˜ ๋ฐ˜์‘ ์†๋„๊ฐ€ ์ฒด๊ฐ๋  ๋งŒํผ ๋นจ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋“  ๊ณผ์ •์ด ๋” ๋‚˜์€ ์„œ๋น„์Šค๋ฅผ ํ–ฅํ•œ ์ €ํฌ ํŒ€์˜ ๋Š์ž„์—†๋Š” ๋…ธ๋ ฅ์ด์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ๋„ ๋…ธ๋จธ์Šค ํ”„๋ก ํŠธ์—”๋“œ ํŒ€์€ ๋‹จ์ˆœํžˆ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ ๊ณผ ๋ฐ์ดํ„ฐ ์•ˆ์ •์„ฑ์„ ์œ„ํ•ด ์ง€์†์ ์œผ๋กœ ๋…ธ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊พธ์ค€ํžˆ ๋ฐœ์ „ํ•ด๋‚˜๊ฐ€๋Š” fromm ์ฑ„๋„ ์„œ๋น„์Šค ๋งŽ์€ ๊ด€์‹ฌ๊ณผ ๊ธฐ๋Œ€ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ˜ƒ

ํ˜„์žฌ ํ”„๋ก ํŠธ์—”๋“œ ํŒ€์€ ํ•จ๊ป˜ ์„œ๋น„์Šค์˜ ํ’ˆ์งˆ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๊ณ ๋ฏผํ•˜๊ณ  ํ•จ๊ป˜ ์„ฑ์žฅํ•ด๋‚˜๊ฐˆ ๊ฐœ๋ฐœ์ž๋ฅผ ์ฐพ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค!
๊ด€์‹ฌ ์žˆ์œผ์‹  ๋ถ„๋“ค์€ ์•„๋ž˜์— ์žˆ๋Š” ์ฑ„์šฉํŽ˜์ด์ง€๋ฅผ ํ†ตํ•ด ์–ธ์ œ๋“ ์ง€ ์ง€์› ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๐Ÿค—

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ