๐Ÿ’ก 06. Optimistic Updates (๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ) ์‹ค์ „

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

๐Ÿ“‹ ๊ฐœ์š”

์„œ๋ฒ„ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  UI๋ฅผ ๋จผ์ € ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์•ฑ์˜ ์ฒด๊ฐ ์†๋„(UX)๋ฅผ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ๊ธฐ๋ฒ•๊ณผ ์—๋Ÿฌ ๋กค๋ฐฑ ๋งค์ปค๋‹ˆ์ฆ˜์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ

"์˜์ˆ™(UX ๋””์ž์ด๋„ˆ) ๋‹˜, ์‚ฌ์šฉ์ž๊ฐ€ ์ข‹์•„์š” ๋ฒ„ํŠผ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค 0.5์ดˆ์”ฉ ๋ ‰ ๊ฑธ๋ฆฌ๋˜ ๊ฑฐ ๋ฐฉ๊ธˆ ๊ณ ์ณค์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋ฒˆ๊ฐœ์ฒ˜๋Ÿผ ๋ณ€ํ•  ๊ฑฐ์˜ˆ์š”."

โ˜•๏ธ ์˜์ฒ ์ด์˜ ๊ณ ๋ฏผ: "์„œ๋ฒ„๊ฐ€ ๋А๋ฆฌ๋ฉด ํ™”๋ฉด๋„ ๋А๋ ค์•ผ ํ•˜๋‚˜์š”?"

(์•„์นจ ํšŒ์˜ ์‹œ๊ฐ„, ์˜์ˆ™ ๋””์ž์ด๋„ˆ์˜ ํ”ผ๋“œ๋ฐฑ์„ ๋“ฃ๋Š” ์˜์ฒ )

๐ŸŽจ ์˜์ˆ™ (UX ๋””์ž์ด๋„ˆ): "์˜์ฒ  ๋‹˜, ์šฐ๋ฆฌ ์ปค๋ฎค๋‹ˆํ‹ฐ ์•ฑ ๋ง์ธ๋ฐ์š”... ์ธ์Šคํƒ€๊ทธ๋žจ์ด๋‚˜ ํŠธ์œ„ํ„ฐ๋Š” '์ข‹์•„์š”' ๋ˆ„๋ฅด๋ฉด ๋ฐ”๋กœ ๋นจ๊ฐ›๊ฒŒ ๋ณ€ํ•˜์ž–์•„์š”? ๊ทผ๋ฐ ์šฐ๋ฆฌ ์•ฑ์€ ๋ˆ„๋ฅด๊ณ  ๋‚˜์„œ ํ•œ 0.5์ดˆ ๋’ค์—์•ผ ์ƒ‰๊น”์ด ๋ณ€ํ•ด์„œ ๋˜๊ฒŒ ๋‹ต๋‹ตํ•ด์š”. ๊ฐ€๋”์€ ๋‚ด๊ฐ€ ์•ˆ ๋ˆŒ๋ €๋‚˜ ์‹ถ์–ด์„œ ๋˜ ๋ˆ„๋ฅด๊ฒŒ ๋ผ์š”."

๐Ÿฃ ์˜์ฒ : ์•„... ๊ทธ๊ฒŒ, ์ €ํฌ ์„œ๋ฒ„ ์‘๋‹ต ์†๋„๋ž‘ ๋„คํŠธ์›Œํฌ ์ง€์—ฐ(Latency) ๋•Œ๋ฌธ์— ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์–ด์ฉ” ์ˆ˜๊ฐ€ ์—†๋Š” ๋ถ€๋ถ„์ด์—์š”. ๋ฐฑ์—”๋“œ์—์„œ 200 OK ์‚ฌ์ธ์ด ๋–จ์–ด์ ธ์•ผ ์ œ๊ฐ€ ๋ง˜ ๋†“๊ณ  ํ™”๋ฉด ์ƒ‰๊น”์„ ๋ฐ”๊ฟ”์ค„ ์ˆ˜ ์žˆ๋‹ค๊ณ ์š” .

๐Ÿฆ ์˜ํ˜ธ: ์˜์ฒ  ๋‹˜, ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ๋ฐฑ์—”๋“œ ์†๋„์— ํ•‘๊ณ„๋ฅผ ๋Œ€๋ฉด ์•ˆ ๋˜์ฃ . ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์š”์ฒญ(Mutation)์ด ๋‹น์—ฐํžˆ ์„ฑ๊ณตํ•  ๊ฒƒ์ด๋ผ ๋‚™๊ด€(Optimistic) ํ•˜๊ณ  ์ผ๋‹จ ํ™”๋ฉด๋ถ€ํ„ฐ ๋ฐ”๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” Optimistic Updates ๊ธฐ๋ฒ•์„ ์“ฐ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.


๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: UX์˜ ์งˆ์  ๋„์•ฝ

์ธ์Šคํƒ€๊ทธ๋žจ, ๋…ธ์…˜, ํŠธ๋ ๋กœ ๊ฐ™์€ ๊ธ€๋กœ๋ฒŒ ํƒ‘ํ‹ฐ์–ด ์•ฑ๋“ค์€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋Š” ์ˆœ๊ฐ„ 1๋ฐ€๋ฆฌ์ดˆ์˜ ์ง€์—ฐ๋„ ์—†์ด ์ƒํ˜ธ์ž‘์šฉ์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๋’ค์—์„œ๋Š” ์—ฌ์ „ํžˆ ๋„คํŠธ์›Œํฌ fetch ํ†ต์‹ ์ด ๋Œ์•„๊ฐ€๊ณ  ์žˆ์ง€๋งŒ ๋ง์ด์ฃ .

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

ํ•˜์ง€๋งŒ ๋ฌดํ„ฑ๋Œ€๊ณ  ํ™”๋ฉด๋งŒ ๋ฐ”๊ฟจ๋‹ค๊ฐ€ ์ง„์งœ๋กœ ์„œ๋ฒ„์—์„œ ์˜ค๋ฅ˜(500 Error)๊ฐ€ ๋‚˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ์ž˜๋ชป ๋ฐ”๋€ ํ™”๋ฉด์„ ์›๋ž˜๋Œ€๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๋กค๋ฐฑ(Rollback) ์ž‘์—…์ด ์ •๊ตํ•˜๊ฒŒ ๋™๋ฐ˜๋˜์–ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฑ•ํ„ฐ์—์„œ๋Š” ์„œ๋ฒ„ ์—๋Ÿฌ์— ๋Œ€์ฒ˜ํ•˜๋Š” ์šฐ์•„ํ•œ ๋กค๋ฐฑ ๋กœ์ง์„ TypeScript์™€ ํ•จ๊ป˜ ์„ค๊ณ„ํ•ด๋ด…๋‹ˆ๋‹ค.


1. ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ์˜ ํ•„์ˆ˜ ์ฝœ๋ฐฑ 3์ด์‚ฌ

useMutation ํ›… ์•ˆ์—๋Š” ๋‚™๊ด€์  ์ƒํƒœ๊ณ„๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” 3๊ฐ€์ง€ ์ƒ๋ช…์ฃผ๊ธฐ(Lifecycle) ์ฝœ๋ฐฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. onMutate: ๋Œ์—ฐ๋ณ€์ด ์‹คํ–‰ ์ง์ „์— ๋ฐœ๋™! (์—ฌ๊ธฐ์„œ ํ™”๋ฉด์„ ๊ฐ€์งœ๋กœ ๋ฐ”๊พผ๋‹ค)
  2. onError: ์ง„์งœ๋กœ ์„œ๋ฒ„ ํ†ต์‹ ํ–ˆ๋Š”๋ฐ ์—๋Ÿฌ ๊ฐ€ ๋‚ฌ์„ ๋•Œ! (์—ฌ๊ธฐ์„œ ํ™”๋ฉด์„ ๋‹ค์‹œ ์˜›๋‚ ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค)
  3. onSettled: ์„ฑ๊ณตํ•˜๋“  ์‹คํŒจํ•˜๋“  ๋งˆ์ง€๋ง‰ ์— ๋ฐ˜๋“œ์‹œ ์‹คํ–‰! (์–ด์จŒ๋“  ๋๋‚ฌ์œผ๋‹ˆ ์„œ๋ฒ„ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋กœ ์ตœ์ข… ๋ฎ์–ด์”Œ์šด๋‹ค)

๐Ÿ“Œ TypeScript ํƒ€์ž…์˜ ์ค‘์š”์„ฑ

TypeScript ํ™˜๊ฒฝ์—์„œ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์งค ๋•Œ๋Š” ์ œ๋„ค๋ฆญ(Generic) ํƒ€์ž…์„ ์ •ํ™•ํžˆ ๋งž์ถ”๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ onError๋กœ ์˜›๋‚  ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ฃผ๋ ค๋ฉด(Context), onMutate์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ํƒ€์ž…๊ณผ onError์—์„œ ๋ฐ›๋Š” Context์˜ ํƒ€์ž…์ด ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// ๐Ÿ“ Mutation Context ํƒ€์ž… ํ‘œ๊ธฐ
// ์ด์ „ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด๋’€๋‹ค๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋ฉด ๋กค๋ฐฑํ•  ๋•Œ ์“ฐ๋Š” '์•ˆ์ „ ์žฅ์น˜'
type TodoContext = {
  previousTodos: Todo[] | undefined;
};

2. ์‹ค์ „ ์ฝ”๋“œ ์›Œํฌ์ƒต: ์ข‹์•„์š” ๊ธฐ๋Šฅ ์™„๋ฒฝ ๊ตฌํ˜„

์˜์กฐ ๋ฆฌ๋“œ๊ฐ€ ์ง์ ‘ ์งœ์ฃผ๋Š” "์ ˆ๋Œ€ ์‹คํŒจํ•˜์ง€ ์•Š๋Š” ๋‚™๊ด€์  ์ข‹์•„์š” ๋ฒ„ํŠผ" ์ฝ”๋“œ๋ฅผ ํ•œ ์ค„์”ฉ ๋œฏ์–ด๋ด…์‹œ๋‹ค.

import { useMutation, useQueryClient } from '@tanstack/react-query';
 
// DB์— ์ €์žฅ๋œ Post ํƒ€์ž…
type Post = {
  id: number;
  title: string;
  likes: number; // ์ข‹์•„์š” ๊ฐœ์ˆ˜
};
 
export function useLikePost() {
  const queryClient = useQueryClient();
 
  return useMutation({
    // 1๏ธโƒฃ ์ง„์งœ ์„œ๋ฒ„ ํ†ต์‹ 
    mutationFn: async (postId: number) => {
      // (๊ฐ€์ •) ์‹œ๊ฐ„์ด 1์ดˆ ๊ฑธ๋ฆฌ๋Š” ๋А๋ฆฐ API
      const res = await axios.post(`/posts/${postId}/like`);
      return res.data;
    },
 
    // 2๏ธโƒฃ ๐Ÿš€ [ํ•ต์‹ฌ] onMutate: ๋ฒ„ํŠผ ํด๋ฆญ ์ฆ‰์‹œ ๋ฐœ๋™! (์„œ๋ฒ„ ๋Œ€๊ธฐ ์•ˆ ํ•จ)
    onMutate: async (postId: number) => {
      // 1. ํ˜น์‹œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋Œ๊ณ  ์žˆ๋Š” ๋‹ค๋ฅธ ํŽ˜์น˜๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ฐ•์ œ๋กœ ์ทจ์†Œ!
      // (์ด ๊ฐ’์ด ์ด์ „ ๊ฒฐ๊ณผ๋กœ ๋ฎ์–ด์”Œ์›Œ์ง€๋Š” ๊ฐ€์žฅ ์œ„ํ—˜ํ•œ Race Condition ๋ฐฉ์ง€)
      await queryClient.cancelQueries({ queryKey: ['posts'] });
 
      // 2. ์—๋Ÿฌ ๋‚ฌ์„ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•ด์„œ, ์—…๋ฐ์ดํŠธ ์ „ '์›๋ž˜ ๋ฐ์ดํ„ฐ'๋ฅผ ๋ณ€์ˆ˜์— ์ž ๊น ๋ณด๊ด€ (์Šค๋ƒ…์ƒท)
      const previousPosts = queryClient.getQueryData<Post[]>(['posts']);
 
      // 3. โœจ ๋“œ๋””์–ด ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๋ฐœ๋™! ํ™”๋ฉด์— ๋ณด์ผ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ˆ˜์ •ํ•œ๋‹ค.
      queryClient.setQueryData<Post[]>(['posts'], (old) => {
        if (!old) return [];
        return old.map(post =>
          // ๋‚ด๊ฐ€ ์ง€๊ธˆ ๋ˆ„๋ฅธ ๊ฒŒ์‹œ๋ฌผ์˜ ์ข‹์•„์š” ์ˆ˜๋งŒ ๊ฐ€์งœ๋กœ +1 ์‹œ์ผœ๋ฒ„๋ฆผ (ํ™”๋ฉด ์ฆ‰์‹œ ๋ฐ˜์˜)
          post.id === postId ? { ...post, likes: post.likes + 1 } : post
        );
      });
 
      // 4. ์›๋ž˜ ๋ณด๊ด€ํ•ด๋‘” ๋ฐ์ดํ„ฐ๋ฅผ Context(ํ”ผ๋‚œ์ฒ˜)์— ๋‹ด์•„์„œ ๋ฆฌํ„ด! -> ์ด๊ฑด onError๋กœ ์ „๋‹ฌ๋จ
      return { previousPosts };
    },
 
    // 3๏ธโƒฃ ๐Ÿ’ฃ ๋งŒ์•ฝ ์„œ๋ฒ„์—์„œ 500 ์—๋Ÿฌ๊ฐ€ ๋‚ฌ๋‹ค๋ฉด? (๋กค๋ฐฑ)
    onError: (err, postId, context) => {
      // context์— ์•„๊นŒ onMutate์—์„œ ๋ฆฌํ„ดํ–ˆ๋˜ ์›๋ž˜ ์Šค๋ƒ…์ƒท์ด ๋“ค์–ด์žˆ๋‹ค.
      if (context?.previousPosts) {
        // ์›๋ž˜ ๋ฐ์ดํ„ฐ๋กœ ๋‹ค์‹œ ์บ์‹œ๋ฅผ ๋ณต๊ตฌ์‹œ์ผœ๋ฒ„๋ฆฐ๋‹ค! (Undo)
        queryClient.setQueryData(['posts'], context.previousPosts);
      }
    },
 
    // 4๏ธโƒฃ โœ… ์„ฑ๊ณต์ด๋“  ์‹คํŒจ๋“  ๋ชจ๋“  ๊ฒŒ ๋๋‚ฌ๋‹ค๋ฉด? (๊ฒ€์ฆ)
    onSettled: () => {
      // ํ˜น์‹œ ๋กœ์ปฌ ์กฐ์ž‘ ์ค‘ ๊ผฌ์˜€์„์ง€ ๋ชจ๋ฅด๋‹ˆ ์„œ๋ฒ„์—์„œ ์ฐ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ๊น”๋”ํ•˜๊ฒŒ ๋ถˆ์–ด์˜จ๋‹ค.
      queryClient.invalidateQueries({ queryKey: ['posts'] });
    },
  });
}

์ด ํ๋ฆ„์€ ๊ฑฐ์˜ ๊ณต์‹(Boilerplate)์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. ์™ธ์žฅํ•˜๋“œ์ฒ˜๋Ÿผ ๋–ผ๋‹ค ๋ถ™์ด์…”๋„ ๋  ์ •๋„๋กœ ์•ˆ์ •์ ์ธ ์•ˆ์ •์„ฑ์„ ์ž๋ž‘ํ•ฉ๋‹ˆ๋‹ค.


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

Q. ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” onMutate ๋ธ”๋ก์˜ ์ตœ์ƒ๋‹จ์—์„œ ๊ฐ€์žฅ ๋จผ์ € await queryClient.cancelQueries({ queryKey: [...] }) ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ™œ์„ฑํ™”๋œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ทจ์†Œ(Cancel)ํ•˜๋Š” ๊ทผ๋ณธ์ ์ธ ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ์ •๋‹ต: ์ด์ „์— ์ง„ํ–‰ ์ค‘์ด๋˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํŽ˜์น˜(Refetch)๊ฐ€ ํ•œ๋ฐœ ๋Šฆ๊ฒŒ ๋„์ฐฉํ•˜์—ฌ, ์šฐ๋ฆฌ๊ฐ€ ์ˆ˜๋™์œผ๋กœ ๊น”์•„๋‘” ๋‚™๊ด€์  ์บ์‹œ(๊ฐ€์งœ ์ƒˆ ๋ฐ์ดํ„ฐ)๋ฅผ ๊ณผ๊ฑฐ์˜ ๋‚ก์€ ๋ฐ์ดํ„ฐ๋กœ ๋ฎ์–ด์”Œ์›Œ๋ฒ„๋ฆฌ๋Š” ์ฐธ์‚ฌ(Race Condition)๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

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

  • ์›๋ฆฌ ์„ค๋ช…: ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ์ข‹์•„์š”๋ฅผ ๋ˆ„๋ฅด๊ธฐ 0.1์ดˆ ์ „์— ๋‹ค๋ฅธ ์ด์œ (์˜ˆ: ํƒญ ์ด๋™)๋กœ ['posts'] ์ฟผ๋ฆฌ๊ฐ€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํŒจ์น˜๋ฅผ ๋ณด๋‚ด๋‘” ์ƒํƒœ์˜€๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ์‹œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ํ•˜ํŠธ ๊ฐœ์ˆ˜๋ฅผ ๊ฐ€์งœ๋กœ +1 ํ•ด๋‘์—ˆ๋Š”๋ฐ, ๊ทธ ์งํ›„์— ์•„๊นŒ ๋ณด๋‚ด๋‘” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํŒจ์น˜์˜ ์‘๋‹ต์ด ๋„์ฐฉํ•ด๋ฒ„๋ฆฌ๋ฉด ์บ์‹œ๋Š” ๋‹ค์‹œ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ(ํ•˜ํŠธ ๊ฐœ์ˆ˜ -1) ์ƒํƒœ๋กœ ๋ฎ์–ด์”Œ์›Œ์ ธ ๋ฒ„๋ฆฌ๋ฉฐ ํ™”๋ฉด์˜ ํ•˜ํŠธ๊ฐ€ ๊ปŒ๋ป‘๊ปŒ๋ป‘์ด๋Š” ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, '์–ด์ฐจํ”ผ ๋‚™๊ด€์ ์ธ๋ฐ ๋’ท๋‹จ์—์„œ ๋ญฃํ•˜๋Ÿฌ ๋„๋Š” ๊ฑฐ ์ทจ์†Œํ•ด์š”?' ๋ผ๋‡จ! ์ˆ˜๋™ ์กฐ์ž‘(setQueryData)์„ ํ•  ๋•Œ ์บ์‹œ์˜ ์†Œ์œ ๊ถŒ์€ ํ˜„์žฌ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ณผ๊ฑฐ์˜ ๋ง๋ น(์ด์ „ ํŽ˜์นญ ๊ฒฐ๊ณผ)์ด ํ•จ๋ถ€๋กœ ์นจ๋ฒ”ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ฌธ์„ ์ž ๊ทธ๋Š”(Cancel) ํ–‰์œ„๋Š” ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ์˜ ์ œ1 ์›์น™์ž…๋‹ˆ๋‹ค!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๋‚™๊ด€์  ์ˆ˜๋™ ๋ฎ์–ด์“ฐ๊ธฐ ์ „์—” ๋ฐ˜๋“œ์‹œ ๋‚จ์€ ์ž”์—ฌ ์ฟผ๋ฆฌ๋“ค Cancel ๋น” ์š”์ฒญ ์ทจ์†Œ!

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ์—์„œ onMutate๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ํ•ด์•ผ ํ•  ์ผ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ์ •๋‹ต: ๊ด€๋ จ ์ฟผ๋ฆฌ๋ฅผ ์ทจ์†Œํ•˜๊ณ , ํ˜„์žฌ ์บ์‹œ ๊ฐ’์„ ๋ฐฑ์—…ํ•œ ๋’ค, ํ™”๋ฉด์— ๋จผ์ € ๋ณด์—ฌ์ค„ ์ž„์‹œ ๊ฐ’์„ setQueryData๋กœ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:
๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋Š” โ€œ์„œ๋ฒ„๊ฐ€ ์„ฑ๊ณตํ•  ๊ฒƒโ€์ด๋ผ๊ณ  ๋ณด๊ณ  UI๋ฅผ ๋จผ์ € ์›€์ง์ด๋Š” ์ „๋žต์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‹คํŒจ ์‹œ ๋˜๋Œ๋ฆด ์ด์ „ ๊ฐ’์ด ๊ผญ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฐฑ์—… ์—†์ด ํ™”๋ฉด๋งŒ ๋ฐ”๊พธ๋ฉด ์‹คํŒจํ–ˆ์„ ๋•Œ ์–ด๋–ค ์ƒํƒœ๋กœ ๋Œ์•„๊ฐ€์•ผ ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

Q2. cancelQueries๋ฅผ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ์ „์— ํ˜ธ์ถœํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ์ •๋‹ต: ์ง„ํ–‰ ์ค‘์ด๋˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‘๋‹ต์ด ๋’ค๋Šฆ๊ฒŒ ๋„์ฐฉํ•ด ๋ฐฉ๊ธˆ ์‹ฌ์€ ๋‚™๊ด€์  ์บ์‹œ๋ฅผ ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋กœ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:
๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋Š” ์บ์‹œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋งŒ์ง€๋Š” ์ˆœ๊ฐ„์ž…๋‹ˆ๋‹ค. ์ด๋•Œ ์ด์ „ ์š”์ฒญ์˜ ์‘๋‹ต์ด ์‚ด์•„ ์žˆ์œผ๋ฉด ์˜์ฒ ์ด ์˜ฌ๋ฆฐ ์ข‹์•„์š” +1์ด ๋‹ค์‹œ ์›๋ž˜ ๊ฐ’์œผ๋กœ ํŠ•๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์š”์ฒญ ์ทจ์†Œ๋Š” UX ๊ผผ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฒฝํ•ฉ ์ƒํƒœ ๋ฐฉ์–ด์ž…๋‹ˆ๋‹ค.

Q3. ์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ์ข‹์•„์š” ์š”์ฒญ์ด ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์ฝœ๋ฐฑ์—์„œ ์–ด๋–ค ๊ฐ’์„ ์‚ฌ์šฉํ•ด ๋กค๋ฐฑํ•ด์•ผ ํ•˜๋‚˜์š”?

โœ… ์ •๋‹ต: onMutate๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ์ด์ „ ์บ์‹œ context๋ฅผ onError์—์„œ ์ฝ์–ด setQueryData๋กœ ๋ณต๊ตฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:
์„ฑ๊ณตํ•˜๋ฉด ์ตœ์ข… ๋ฌดํšจํ™”๋กœ ์„œ๋ฒ„์™€ ๋‹ค์‹œ ๋งž์ถ”๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์‹คํŒจํ•˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๋ณธ ์ž„์‹œ UI๊ฐ€ ๊ฑฐ์ง“์ด์—ˆ์œผ๋ฏ€๋กœ ์ •ํ™•ํžˆ ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜์ฒ ์ด ์ด์ œ โ€œ๋น ๋ฅธ UIโ€์™€ โ€œ์ •ํ™•ํ•œ ๋ณต๊ตฌโ€๋ฅผ ํ•œ ์Œ์œผ๋กœ ํŒ๋‹จํ•˜๊ฒŒ ๋œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

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

์™€, ์†”์งํžˆ ์ฝ”๋“œ ๋ณผ ๋• onMutate, cancelQueries, setQueryData ์ฃ„๋‹ค ์„ž์—ฌ ์žˆ์–ด์„œ ๋ณต์žกํ•ด ๋ณด์˜€๋Š”๋ฐ ์ง์ ‘ ํƒ€์ž ์ณ๋ณด๋‹ˆ๊นŒ ์›๋ฆฌ๊ฐ€ ๊ธฐ๊ฐ€ ๋ง‰ํžŒ๋‹ค.
"1๋ฒˆ ๋ฉˆ์ถ”๊ณ  -> 2๋ฒˆ ๊ณผ๊ฑฐ ๋ฐฑ์—…ํ•˜๊ณ  -> 3๋ฒˆ ์ตœ์‹ ์ธ ์ฒ™ ์œ„์žฅํ•˜๊ณ  -> 4๋ฒˆ ๋งํ•˜๋ฉด ๋ณต๊ตฌ / ์„ฑ๊ณตํ•˜๋ฉด ๋ฆฌ๋‰ด์–ผ". ์ด ๋„ค ๋ฐ•์ž๊ฐ€ ์™„์ „ ์˜ˆ์ˆ ์ด๋„ค.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "์„œ๋ฒ„๊ฐ€ ๋А๋ฆฌ๋‹ค๊ณ  ๋‚ด ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ ๋А๋ฆด ํ•„์š”๋Š” ์—†๋‹ค! ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ๋ฏฟ๊ณ  (Optimistic) ํ™”๋ฉด๋ถ€ํ„ฐ ๋ณด์—ฌ์ค€ ๋’ค, ๋’ท๋‹จ์—์„œ ์‚ฌ๊ณ  ๋‚˜๋ฉด ์กฐ์šฉํžˆ ๋กค๋ฐฑ(Rollback)ํ•ด์ฃผ๋Š” ์šฐ์•„ํ•œ ์‚ฌ๊ธฐ(?)๋ฅผ ์น˜์ž."

์˜์ˆ™ ๋””์ž์ด๋„ˆ ๋‹˜ํ•œํ…Œ ์ด๊ฑฐ ์ ์šฉํ•˜๊ณ  ์‹œ์—ฐ ๋ณด์—ฌ๋“œ๋ ธ๋”๋‹ˆ ๋ˆˆ์„ ํฌ๊ฒŒ ๋œจ๊ณ  ์ปคํ”ผ ์‚ฌ์ฃผ์…จ๋‹ค . ๋‚ด์ผ์€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์•„์ดํ…œ ๋‹ด๋Š” ๊ฒƒ๋„ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋กœ ๋ฐ”๊ฟ”๋†”์•ผ์ง€. (๋‹จ, ๊ฒฐ์ œ ๊ฐ™์€ ์น˜๋ช…์ ์ธ ๊ฑด ๋‚™๊ด€์ ์œผ๋กœ ํ•˜๋ฉด ํฐ์ผ๋‚œ๋‹ค๊ณ  ์˜ํ˜ธ ๋‹˜์ด ๊ฒฝ๊ณ ํ•˜์…จ๋‹ค... ํ†ต์žฅ ์ž”๊ณ  0์›์ธ๋ฐ ๊ฒฐ์ œ ์™„๋ฃŒ ๋„์šธ ๋ป” )