์ค๋ ๋๋์ด 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;
});
์์ฒญ ๋๋ ์๋ต์ ๊ฐ๋ก์ฑ์ ์ ์ฒ๋ฆฌ ๋๋ ํ์ฒ๋ฆฌํ ์ ์๋ค. ์ด๋ฅผ ํตํด ์ธ์ฆ ํ ํฐ์ ์๋์ผ๋ก ์ถ๊ฐํ๊ฑฐ๋ ์ค๋ฅ๋ฅผ ์ผ๊ด ์ฒ๋ฆฌํ ์ ์๋ค.
client์ server ์ฌ์ด์ HTTP์์ฒญ๊ณผ ์๋ต์ ๊ฐ๋ก์ฑ์ ํน์ ์์
์ ์ํํ ์ ์๊ฒ ํ๋ค.
์ธํฐ์ ํฐ ์ง์
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;
๋ฏธ๋ค์จ์ด๋ ์ก์ ์ด ๋ฆฌ๋์์ ๋๋ฌํ๊ธฐ ์ ์ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ ์ถ๊ฐ์ ์ธ ์์ ์ ์ํํ ์ ์๋๋ก ํจ:
dispatch(someSliceAction()) // slice reducer์์ ์ ์ํ ์ก์
์ ์ง์ ๋์คํจ์น
// createAsyncThunk๋ฅผ ์ฌ์ฉํ ๋น๋๊ธฐ ์ก์
์์ฑ
const asyncAction = createAsyncThunk(
"action/type",
async () => {
// ๋น๋๊ธฐ ๋ก์ง
}
)
createAsyncThunk("action type", ๋น๋๊ธฐํจ์)๋ ๋ค์๊ณผ ๊ฐ์ ์ํ๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌ:
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;
});
}
Action ๊ฐ์ฒด โ Reducer โ State ์ ๋ฐ์ดํธ
๊ทธ๋์!! ์๊ธด๊ฒ tanstack query!
react thunk๊ฐ useEffect๋ก ๊ฐ์ ธ์๋ ๋ฐ์ดํฐ๋ค์ ๋นํด์๋ ํธ๋ฆฌํ ๋๊ตฌ๋ฅผ ์ ๊ณตํ์ง๋ง ์ฌ๋ฌ๊ฐ์ง ์ด์ ๋ค๋๋ฌธ์react query๋ฅผ ์ฌ์ฉํ๋ค.
๊ทผ๋ฐ ์ด์ ์ง์ง ๋๋ผ์ด ๊ฑด TanStack Query(์๋ ์ด๋ฆ: React Query)์๋ค. ์ฒ์์๋ "๋ ๋ญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ..." ํ๋๋ฐ, ์ด๊ฑฐ ๋๋ฐ์ด๋ค.
๊ธฐ์กด์ useEffect๋ก ํ๋ ๋ฐฉ์์ด ์ผ๋ง๋ ๊ท์ฐฎ์๋๋ฉด:
1. ๋ก๋ฉ ์ํ ๊ด๋ฆฌ
2. ์๋ฌ ์ํ ๊ด๋ฆฌ
3. ๋ฐ์ดํฐ ์ํ ๊ด๋ฆฌ
4. ์ด๊ฑฐ ๋ค useState๋ก ๋ฐ๋ก ๊ด๋ฆฌ...
์๋ฒ ์ํ๋ฅผ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์บ์ฑ, ๋๊ธฐํ, ๋ฌดํจํ)
ํ์คํ ์ฟผ๋ฆฌ ์ฃผ์ ๊ธฐ๋ฅ 3๊ฐ์ง (์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๊ฐฑ์ ํ๋ ๊ณผ์ )
1. get (R) - useQuery
2. modify (CUD) - useMutation
3. refresh (๋ฐ์ดํฐ ๊ฐฑ์ ) - invalidateQueries
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: false, // ์ปดํฌ๋ํธ ๋ง์ดํธ ์ ์ฌ์์ฒญ ๋ฐฉ์ง
refetchOnWindowFocus: false, // ์๋์ฐ ํฌ์ปค์ค ์ ์ฌ์์ฒญ ๋ฐฉ์ง
refetchOnReconnect: false, // ์ฌ์ฐ๊ฒฐ ์ ์ฌ์์ฒญ ๋ฐฉ์ง
},
},
});
<QueryClientProvider client={client}>
<App />
</QueryClientProvider>
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>
);
const { mutate } = useMutation({
mutationFn: addTodo // ๋ณ๊ฒฝ ์์
์ ์ํํ ํจ์
});
// ์ฌ์ฉ ์์
const newTodoObj = {
title: todoItem,
isDone: false
};
mutate(newTodoObj);
const { mutate } = useMutation({
mutationFn: addTodo,
onSuccess: () => {
// ๋ฐ์ดํฐ ๊ฐฑ์
queryClient.invalidateQueries(["todos"]);
// ์ ํ์ : ์ฑ๊ณต ๋ฉ์์ง ํ์
// alert("๋ฐ์ดํฐ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ถ๊ฐ๋์์ต๋๋ค!");
}
});
false: ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ค์ง ์์true: ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ดfalse: ์๋์ฐ๊ฐ ํฌ์ปค์ค๋ฅผ ๋ฐ์ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ค์ง ์์true: ์ฌ์ฉ์๊ฐ ๋ธ๋ผ์ฐ์ ํญ์ผ๋ก ๋์์ฌ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ดfalse: ๋คํธ์ํฌ ์ฌ์ฐ๊ฒฐ ์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ค์ง ์์true: ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ๋ณต๊ตฌ๋ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ดqueryKey๋ ๋ฐฐ์ด ํํ๋ก, ์ฌ๋ฌ ๋ ๋ฒจ์ ํค๋ฅผ ๊ฐ์ง ์ ์์ต๋๋คinvalidateQueries๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ์ฟผ๋ฆฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ฌดํจํํ๊ณ ์๋ก ๋ถ๋ฌ์ฌ ์ ์์ต๋๋คQueryClient)๊ณผ ๊ฐ๋ณ ์ฟผ๋ฆฌ ๋จ์๋ก ๋ชจ๋ ๊ฐ๋ฅํฉ๋๋ค๋ด์ผ์ ์ด๊ฑฐ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๋ ค๋ ์ค์ค๋ก ์ ๋ฆฌํ๊ณ ํ๋์ฉ ์จ๋ณด๋ฉด์ ๋ฐฐ์๊ฐ๋ ๊ณผ์ ์ด ํ์ํ ๊ฒ ๊ฐ๋ค.
ํ๋ฃจ์ ๋ค์ํ๊ณ ๋ง์ ๋ด์ฉ๋ค์ ์ ํ๋ ค๋ ๋๋ฌด ์ง์น๋๊ฑด ์ฌ์ค์ธ์ง๋ผ ์ ๋๋ก ๊นจ์ฐ์น๊ณ ์ตํ๊ฒ์ด ๋ง๋์ถ์ง๋ง...
๊ทธ๋๋ TanStack Query์ useMutation์ด๋ invalidateQueries ๋ถ๋ถ์ด ๋๋ฌด ๊ธฐ๋๋๋ค. ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๋ฅผ ์ค์ผ ์ ์๋ค๋...