๐Ÿ“– TIL - fetch๊ฐ€ axios๋ฅผ ๊ฑฐ์ณ TanStack Query๊ฐ€ ๋˜๊ธฐ๊นŒ์ง€: ํ•˜๋ฃจ ๋งŒ์— ๊นจ๋‹ฌ์€ ๊ฒƒ๋“ค โšก

์Š˜ยท2025๋…„ 2์›” 19์ผ

๐Ÿ“– TIL

๋ชฉ๋ก ๋ณด๊ธฐ
60/90

๐Ÿค” axios vs fetch: "axios์•ผ... ๋ฏธ์•ˆํ•˜๋‹ค"

์˜ค๋Š˜ ๋“œ๋””์–ด axios์™€ fetch์˜ ์ฐจ์ด๋ฅผ ์ œ๋Œ€๋กœ ์•Œ๊ฒŒ ๋๋‹ค. ๊ทธ๋™์•ˆ ์‚ฌ์‹ค์€ axios์— ๋Œ€ํ•ด์„  ์–ด๋А์ •๋„ ์•Œ๊ณ ์žˆ์—ˆ๊ณ  ์‚ฌ์šฉํ•ด ๋ณธ์ ๋„ ๋งŽ์•˜์ง€๋งŒ.. ์ด๊ฑธ ์™œ ์‚ฌ์šฉํ•˜๋Š”์ง€, fetch์™€ axios์˜ ์ฐจ์ด๋Š” ๋ญ”์ง€ ๋น„๊ตํ•ด๋ณธ์ ์ด ์—†์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. axios์™€ fetch๋ฅผ ๋น„๊ตํ•ด์„œ ๋ณด๋‹ˆ '์•„... ์ด๋ž˜์„œ ๋‹ค๋“ค axios ์“ฐ๋Š”๊ตฌ๋‚˜' ์‹ถ๋”๋ผ.

axios๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ธฐ๋ณธ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค:

// axios๋กœ ์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ ์„ค์ •์ด ๋œ๋‹ค...!
const axiosInstance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' }
});

๊ทธ๋ฆฌ๊ณ  ์—๋Ÿฌ ์ฒ˜๋ฆฌ... fetch๋Š” ์ง„์งœ ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ๋งŒ ์žก๋Š”๋ฐ, axios๋Š” ์„œ๋ฒ„์—์„œ ์˜ค๋Š” 400, 500 ์—๋Ÿฌ๋„ ๋‹ค ์žก์•„์ค€๋‹ค. ์ด๊ฑฐ ํ•˜๋‚˜๋งŒ์œผ๋กœ๋„ ๊ฐˆ์•„ํƒˆ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

api.interceptors.request.use((config) => {
  // ์—ฌ๊ธฐ์„œ ํ† ํฐ ๋„ฃ์–ด์ฃผ๊ณ 
  console.log("์š”์ฒญ ๊ฐ€๋กœ์ฑ„๊ธฐ ์„ฑ๊ณต!");
  return config;
});

๐ŸซทAxios Interceptor

์š”์ฒญ ๋˜๋Š” ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„์„œ ์ „์ฒ˜๋ฆฌ ๋˜๋Š” ํ›„์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ธ์ฆ ํ† ํฐ์„ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๋ฅผ ์ผ๊ด„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
client์™€ server ์‚ฌ์ด์˜ HTTP์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„์„œ ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

์„œ๋ฒ„๋ž‘ ํด๋ผ์ด์–ธํŠธ๋ž‘ ์ฃผ๊ณ ๋ฐ›์„๋•Œ ์ค‘๊ฐ„ ์ž‘์—…์„ ๊ฐ€๋กœ์ฑ„์•ผํ•  ๊ฒฝ์šฐ์˜ ์—ญํ• 

  • request๋ฅผ ๋‚ ๋ฆด๋•Œ server์—๊ฒŒ ๊ฐ€์•ผ๋˜๋Š” header, baseUrl์„ ๋งค๋ฒˆ ์„ค์ •ํ•ด์•ผ ๋  ํ•„์š”๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์‘๋‹ต์ด ์™”์„๋•Œ ๋™์ผํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ๋  ์ˆ˜๋„ ์žˆ๋‹ค.

์ธํ„ฐ์…‰ํ„ฐ ์ง€์ •

import axios from "axios";

const api = axios.create({
  baseURL: "http://localhost:4000",
  timeout: 1, // 1ms timeout์€ axios ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ธฐ๋‹ค๋ฆด์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ์‹œ๊ฐ„ response reject ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•จ
});

api.interceptors.request.use((config) => { // api ์š”์ฒญ ์ธํ„ฐ์…‰ํŠธ
  // ๊ณตํ†ต header๋ฅผ ์„ค์ •ํ• ์ˆ˜๋„ ์žˆ๊ณ .. ๋กœ๊ทธ๋ฅผ ์ฐ์„ ์ˆ˜๋„ ์žˆ๊ณ ..
  console.log("์ธํ„ฐ์…‰ํŠธ ์š”์ฒญ ์„ฑ๊ณต");
  return config;
});

api.interceptors.response.use( // api ์‘๋‹ต ์ธํ„ฐ์…‰ํŠธ
  (res) => {
    console.log("์ธํ„ฐ์…‰ํŠธ ์‘๋‹ต ์„ฑ๊ณต");
    return res;
  },
  (err) => {
    console.log("์ธํ„ฐ์…‰ํŠธ ์‘๋‹ต ์‹คํŒจ");
    return Promise.reject(err); // promise๋กœ ๋„˜์–ด์˜ค๋Š” ๊ฐ์ฒด๋ฅผ ์˜ค๋ฅ˜์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปดํฌ๋„ŒํŠธ์— ์คŒ
  }
);

export default api;

๐Ÿ‘‘ TanStack query์— ๋“ค์–ด๊ฐ€๊ธฐ ์•ž์„œ ๋“ฑ์žฅ๋ฐฐ๊ฒฝ์„ ๊ฐ€๋ณ๊ฒŒ ์ •๋ฆฌํ•˜์ž๋ฉด..

1. ์ƒํƒœ๊ด€๋ฆฌ ๋ณต์žก์„ฑ

  • ์ปดํฌ๋„ŒํŠธ์—์„œ loading, error, ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ๋“ฑ๋“ฑ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผํ•จ
  • ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•˜๊ณ  ๋กœ์ง์ด ๋‚ด๋ถ€์— ๋ถ„์‚ฐ๋˜์–ด ์žˆ์Œ

2. ์ค‘๋ณต๋œ ์ฝ”๋“œ

  • ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์นญํ•  ๊ฒฝ์šฐ ๋ฐ˜๋ณต ์ž‘์„ฑํ•ด์•ผํ•จ

3. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌ

  • *๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง : ํ•ต์‹ฌ ๋™์ž‘ ์ •์˜ ์ฝ”๋“œ

๐Ÿ”„ ์ด์™ธ์— ์ถ”๊ฐ€์ ์ธ Redux ๋ฏธ๋“ค์›จ์–ด์˜ ์ดํ•ด

๋ฏธ๋“ค์›จ์–ด๋Š” ์•ก์…˜์ด ๋ฆฌ๋“€์„œ์— ๋„๋‹ฌํ•˜๊ธฐ ์ „์— ์ค‘๊ฐ„์— ๊ฐ€๋กœ์ฑ„์„œ ์ถ”๊ฐ€์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ:

  • ๋น„๋™๊ธฐ ์ž‘์—… ์ฒ˜๋ฆฌ
  • ๋กœ๊น…
  • ์—๋Ÿฌ ํ•ธ๋“ค๋ง

๐Ÿš€ Redux Thunk ๊ฐœ๋…

RTK vs Thunk ๋น„๊ต

RTK ๋ฐฉ์‹

dispatch(someSliceAction())  // slice reducer์—์„œ ์ •์˜ํ•œ ์•ก์…˜์„ ์ง์ ‘ ๋””์ŠคํŒจ์น˜

Thunk ๋ฐฉ์‹

// createAsyncThunk๋ฅผ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ์•ก์…˜ ์ƒ์„ฑ
const asyncAction = createAsyncThunk(
    "action/type",
    async () => {
        // ๋น„๋™๊ธฐ ๋กœ์ง
    }
)

โš™๏ธ createAsyncThunk ์ž‘๋™ ๋ฐฉ์‹

createAsyncThunk("action type", ๋น„๋™๊ธฐํ•จ์ˆ˜)๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ:

  • pending: ๋กœ๋”ฉ ์ƒํƒœ
  • fulfilled: ์„ฑ๊ณต ์ƒํƒœ
  • rejected: ์—๋Ÿฌ ์ƒํƒœ

extraReducers์—์„œ์˜ ์ฒ˜๋ฆฌ

extraReducers: (builder) => {
    builder
        .addCase(asyncAction.pending, (state) => {
            state.loading = true;
        })
        .addCase(asyncAction.fulfilled, (state, action) => {
            state.data = action.payload;
        })
        .addCase(asyncAction.rejected, (state, action) => {
            state.error = action.error;
        });
}

๐Ÿ”„ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ๋น„๊ต

RTK ๊ธฐ์กด ๋ฐฉ์‹

Action ๊ฐ์ฒด โ†’ Reducer โ†’ State ์—…๋ฐ์ดํŠธ

TanStack Query ๋ฐฉ์‹

  1. Dispatch(์ด๋ฒคํŠธ ๋ฐœ์ƒ)
  2. ๋ฏธ๋“ค์›จ์–ด ์ฒ˜๋ฆฌ
  3. API ๋น„๋™๊ธฐ ํ†ต์‹ (Thunk)
  4. Dispatch๋กœ ๊ฒฐ๊ณผ ์ „๋‹ฌ
  5. Reducer์—์„œ ์ฒ˜๋ฆฌ
  6. State ์—…๋ฐ์ดํŠธ
  7. UI ๋ Œ๋”๋ง

โš ๏ธ ๊ทธ๋ž˜๋„ ํ•œ๊ณ„์ 

  • ๋ณต์žกํ•œ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ
  • ์•ก์…˜ ์ƒ์„ฑ์ž์™€ ๋ฆฌ๋“€์„œ์˜ ๊ธธ์ด ์ฆ๊ฐ€
  • ๋กœ์ง ๋ถ„๋ฆฌ์˜ ์–ด๋ ค์›€

๊ทธ๋ž˜์„œ!! ์ƒ๊ธด๊ฒŒ tanstack query!
react thunk๊ฐ€ useEffect๋กœ ๊ฐ€์ ธ์™”๋˜ ๋ฐ์ดํ„ฐ๋“ค์— ๋น„ํ•ด์„œ๋Š” ํŽธ๋ฆฌํ•œ ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ด์œ ๋“ค๋•Œ๋ฌธ์—react query๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿ˜ฑ TanStack Query: "์–ด... ์ด๊ฒŒ ๋ญ์•ผ"

๊ทผ๋ฐ ์ด์ œ ์ง„์งœ ๋†€๋ผ์šด ๊ฑด TanStack Query(์˜›๋‚  ์ด๋ฆ„: React Query)์˜€๋‹ค. ์ฒ˜์Œ์—๋Š” "๋˜ ๋ญ” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ..." ํ–ˆ๋Š”๋ฐ, ์ด๊ฑฐ ๋Œ€๋ฐ•์ด๋‹ค.

๊ธฐ์กด์— useEffect๋กœ ํ•˜๋˜ ๋ฐฉ์‹์ด ์–ผ๋งˆ๋‚˜ ๊ท€์ฐฎ์•˜๋‚˜๋ฉด:
1. ๋กœ๋”ฉ ์ƒํƒœ ๊ด€๋ฆฌ
2. ์—๋Ÿฌ ์ƒํƒœ ๊ด€๋ฆฌ
3. ๋ฐ์ดํ„ฐ ์ƒํƒœ ๊ด€๋ฆฌ
4. ์ด๊ฑฐ ๋‹ค useState๋กœ ๋”ฐ๋กœ ๊ด€๋ฆฌ...

๐Ÿคฒ TanStack Query - ๋™์ž‘์›๋ฆฌ

์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (์บ์‹ฑ, ๋™๊ธฐํ™”, ๋ฌดํšจํ™”)

ํƒ„์Šคํƒ ์ฟผ๋ฆฌ ์ฃผ์š” ๊ธฐ๋Šฅ 3๊ฐ€์ง€ (์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๊ฐฑ์‹ ํ•˜๋Š” ๊ณผ์ •)
1. get (R) - useQuery
2. modify (CUD) - useMutation
3. refresh (๋ฐ์ดํ„ฐ ๊ฐฑ์‹ ) - invalidateQueries

๐Ÿš€ TanStack Query - ๊ธฐ๋ณธ ๊ฐ€์ด๋“œ

๐Ÿ“‹ ์ดˆ๊ธฐ ์„ค์ •

QueryClient ์„ค์ •

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnMount: false,      // ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ์‹œ ์žฌ์š”์ฒญ ๋ฐฉ์ง€
      refetchOnWindowFocus: false, // ์œˆ๋„์šฐ ํฌ์ปค์Šค ์‹œ ์žฌ์š”์ฒญ ๋ฐฉ์ง€
      refetchOnReconnect: false,   // ์žฌ์—ฐ๊ฒฐ ์‹œ ์žฌ์š”์ฒญ ๋ฐฉ์ง€
    },
  },
});

Provider ์„ค์ •

<QueryClientProvider client={client}>
  <App />
</QueryClientProvider>

๐Ÿ“ฅ ๋ฐ์ดํ„ฐ ์กฐํšŒ (useQuery)

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

const { 
  data: todos,      // ์กฐํšŒ๋œ ๋ฐ์ดํ„ฐ
  isPending,        // ๋กœ๋”ฉ ์ƒํƒœ
  isError          // ์—๋Ÿฌ ์ƒํƒœ
} = useQuery({ 
  queryKey: ["todos"],  // ์ฟผ๋ฆฌ ์‹๋ณ„์ž
  queryFn: fetchTodos   // ๋ฐ์ดํ„ฐ ์š”์ฒญ ํ•จ์ˆ˜
});

๋กœ๋”ฉ ๋ฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

if (isPending) {
  return <div>๋กœ๋”ฉ์ค‘...</div>;
}

if (isError) {
  return <div>์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค!</div>;
}

// ๋ฐ์ดํ„ฐ ๋งคํ•‘
return (
  <div>
    {todos.map((todo) => (
      <TodoItem key={todo.id} {...todo} />
    ))}
  </div>
);

๐Ÿ“ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ (useMutation)

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

const { mutate } = useMutation({ 
  mutationFn: addTodo  // ๋ณ€๊ฒฝ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ํ•จ์ˆ˜
});

// ์‚ฌ์šฉ ์˜ˆ์‹œ
const newTodoObj = { 
  title: todoItem, 
  isDone: false 
};
mutate(newTodoObj);

์„ฑ๊ณต ์ฒ˜๋ฆฌ ๋ฐ ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ 

const { mutate } = useMutation({ 
  mutationFn: addTodo,
  onSuccess: () => {
    // ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ 
    queryClient.invalidateQueries(["todos"]);
    
    // ์„ ํƒ์ : ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
    // alert("๋ฐ์ดํ„ฐ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!");
  }
});

๐Ÿ”„ ์ฃผ์š” ์˜ต์…˜ ์„ค๋ช…

refetchOnMount

  • false: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์Œ
  • true: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ด

refetchOnWindowFocus

  • false: ์œˆ๋„์šฐ๊ฐ€ ํฌ์ปค์Šค๋ฅผ ๋ฐ›์„ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์Œ
  • true: ์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ํƒญ์œผ๋กœ ๋Œ์•„์˜ฌ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ด

refetchOnReconnect

  • false: ๋„คํŠธ์›Œํฌ ์žฌ์—ฐ๊ฒฐ ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์Œ
  • true: ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ๋ณต๊ตฌ๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ด

๐Ÿ’ก Tips

  • queryKey๋Š” ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ, ์—ฌ๋Ÿฌ ๋ ˆ๋ฒจ์˜ ํ‚ค๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • invalidateQueries๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ์ฟผ๋ฆฌ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ƒˆ๋กœ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ์˜ต์…˜ ์„ค์ •์€ ์ „์—ญ(QueryClient)๊ณผ ๊ฐœ๋ณ„ ์ฟผ๋ฆฌ ๋‹จ์œ„๋กœ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

๐Ÿคฏ ์˜ค๋Š˜์˜ ๊นจ๋‹ฌ์Œ

  1. axios๋Š” ์ง„์งœ fetch์˜ ์ƒ์œ„ํ˜ธํ™˜
  2. ์ธํ„ฐ์…‰ํ„ฐ๋ผ๋Š” ๊ฐœ๋… ๋„ˆ๋ฌด ์ข‹๋‹ค
  3. TanStack Query๋Š”... ๊ทธ๋ƒฅ ๋ฏธ์ณค๋‹ค
  4. ......ํ•˜๋ฃจ์— ๋„ˆ๋ฌด ๋งŽ๊ณ  ๋”ฅํ•œ ๊ณต๋ถ€๋Š” ์ง€์–‘ํ•˜์ž (๋„ˆ๋ฌดํž˜๋“ค์–ด..)

๋‚ด์ผ์€ ์ด๊ฑฐ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๋ ค๋‹ˆ ์Šค์Šค๋กœ ์ •๋ฆฌํ•˜๊ณ  ํ•˜๋‚˜์”ฉ ์จ๋ณด๋ฉด์„œ ๋ฐฐ์›Œ๊ฐ€๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™๋‹ค.
ํ•˜๋ฃจ์— ๋‹ค์–‘ํ•˜๊ณ  ๋งŽ์€ ๋‚ด์šฉ๋“ค์„ ์ ‘ํ•˜๋ ค๋‹ˆ ๋„ˆ๋ฌด ์ง€์น˜๋Š”๊ฑด ์‚ฌ์‹ค์ธ์ง€๋ผ ์ œ๋Œ€๋กœ ๊นจ์šฐ์น˜๊ณ  ์ตํžŒ๊ฒƒ์ด ๋งž๋‚˜์‹ถ์ง€๋งŒ...
๊ทธ๋ž˜๋„ TanStack Query์˜ useMutation์ด๋ž‘ invalidateQueries ๋ถ€๋ถ„์ด ๋„ˆ๋ฌด ๊ธฐ๋Œ€๋œ๋‹ค. ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋‹ˆ...

profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ์žฅ๊ธฐ ๊ธฐ๋ก๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€