Redux Thunk ํบ์๋ณด๊ธฐ! ๐ฒ
Redux Thunk๋ Redux์์ ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ฃผ๋ ๋ฏธ๋ค์จ์ด์ ๋๋ค. “Thunk”๋ผ๋ ์ด๋ฆ์ ๋ค์ ์ ๋น๋กญ๊ฒ ๋ค๋ฆด ์ ์์ง๋ง, ๊ฐ๋จํ ๋งํด ์ก์ ์ ๋ฆฌํดํ๋ ํจ์๋ฅผ ๋์คํจ์นํ ์ ์๊ฒ ๋ง๋ค์ด์ฃผ๋ ๋๊ตฌ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ์.
์ด ๊ธ์์๋ Redux Thunk๊ฐ ์ ํ์ํ์ง, ์ด๋ป๊ฒ ์๋ํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๋ค๋ฃฐ๊ฒ์. ์ค๋น๋์ จ๋์? ์ถ๋ฐ~! ๐
๐ Redux์์ ๋น๋๊ธฐ ์์ ์ด ์ ํ์ํ๊ฐ?
Redux๋ ์์ํ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ ์งํ๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค.
์ฆ, ์ก์ (Action)์ด ๋ฐ์ํ๋ฉด ๋ฆฌ๋์(Reducer)๊ฐ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํ๊ณ , ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด UI๊ฐ ์ ๋ฐ์ดํธ๋์ฃ .
ํ์ง๋ง… ์ฌ๊ธฐ์ ๋ฌธ์ ๊ฐ ํ๋ ์์ด์:
Redux๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋๊ธฐ ์์ ๋ง ์ฒ๋ฆฌํ ์ ์๋ค๋ ์ ์ ๋๋ค.
ํ์ง๋ง ์ฐ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด API ํธ์ถ ๊ฐ์ ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์์? ์ฌ๊ธฐ์ Redux Thunk๊ฐ ๋ฑ์ฅํฉ๋๋ค.
๐ก Redux Thunk๋?
Thunk๋ ์๋ ํจ์๋ฅผ ๋ํํด์ ๋์ค์ ์คํํ ์ ์๋๋ก ๋ง๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. Redux Thunk๋ ์ด ๊ฐ๋ ์ Redux์ ์ ์ฉํ์ฌ ์ก์ ์์ฑ์๊ฐ ํจ์๋ฅผ ๋ฐํํ ์ ์๋๋ก ๋ง๋ค์ด์ค๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก Redux๋ ๊ฐ์ฒด๋ง ๋ฐํํ ์ ์์ง๋ง, Redux Thunk๋ฅผ ์ฌ์ฉํ๋ฉด ํจ์๋ฅผ ๋ฐํํ ์ ์์ต๋๋ค.
ํจ์๋ฅผ ๋ฐํํ๋ฉด ๊ทธ ํจ์ ์์์ ๋น๋๊ธฐ ์์ (API ํธ์ถ ๋ฑ)์ ์ํํ๊ณ , ์์ ์ด ๋๋ ํ์ ์ก์ ์ ๋์คํจ์นํ ์ ์์ด์.
โ๏ธ ์ค์น ๋ฐ ์ค์
Redux Thunk๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋ฏธ๋ค์จ์ด๋ก ์ถ๊ฐํด์ผ ํฉ๋๋ค.
npm install redux-thunk
store๋ฅผ ์์ฑํ ๋ applyMiddleware๋ฅผ ํตํด Redux Thunk๋ฅผ ์ถ๊ฐํ์ธ์:
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
์ด์ Redux Thunk๋ฅผ ์ฌ์ฉํ ์ค๋น ์๋ฃ! ๐
๐ Redux Thunk ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
Thunk์ ๊ธฐ๋ณธ ์์ด๋์ด๋ ์ก์ ๋์ ํจ์๋ฅผ ๋ฐํํ๋ ์ก์ ์์ฑ์๋ฅผ ๋ง๋๋ ๊ฒ์ ๋๋ค.
// ์ผ๋ฐ ์ก์
์์ฑ์
const increment = () => ({ type: "INCREMENT" });
// Thunk ์ก์
์์ฑ์
const fetchData = () => {
return async (dispatch) => {
dispatch({ type: "FETCH_DATA_REQUEST" });
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
dispatch({ type: "FETCH_DATA_SUCCESS", payload: data });
} catch (error) {
dispatch({ type: "FETCH_DATA_FAILURE", error });
}
};
};
๐ ์ฌ๊ธฐ์ fetchData๋ ํจ์๋ฅผ ๋ฐํํ๋ ์ก์ ์์ฑ์์ ๋๋ค.
์ด ํจ์๋ dispatch๋ฅผ ๋ฐ์์ ๋น๋๊ธฐ ์์ ์ ์คํํ ํ, ์ฑ๊ณต ๋๋ ์คํจ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ก์ ์ ๋์คํจ์นํฉ๋๋ค.
๐ ๋น๋๊ธฐ ์์ ๋จ๊ณ๋ณ ์ฒ๋ฆฌ
๋น๋๊ธฐ ์์ ์ Redux Thunk๋ก ์ฒ๋ฆฌํ ๋๋ ๋ณดํต ์ธ ๊ฐ์ง ๋จ๊ณ๋ก ๋๋ฉ๋๋ค:
- ์์ฒญ ์์: ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์์ํ์์ ์๋ฆฌ๋ ์ก์ ๋์คํจ์น
- ์์ฒญ ์ฑ๊ณต: ๋ฐ์ดํฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์์์ ์๋ฆฌ๋ ์ก์ ๋์คํจ์น
- ์์ฒญ ์คํจ: ์์ฒญ ์ค ์๋ฌ๊ฐ ๋ฐ์ํ์์ ์๋ฆฌ๋ ์ก์ ๋์คํจ์น
// ๋ฆฌ๋์ ์์
const initialState = {
loading: false,
data: null,
error: null,
};
function dataReducer(state = initialState, action) {
switch (action.type) {
case "FETCH_DATA_REQUEST":
return { ...state, loading: true, error: null };
case "FETCH_DATA_SUCCESS":
return { ...state, loading: false, data: action.payload };
case "FETCH_DATA_FAILURE":
return { ...state, loading: false, error: action.error };
default:
return state;
}
}
๐ฉ๐ป๐ป ์ค์ ์์ : ์ฌ์ฉ์์ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
์ก์ ์์ฑ์
export const fetchUsers = () => {
return async (dispatch) => {
dispatch({ type: "FETCH_USERS_REQUEST" });
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
dispatch({ type: "FETCH_USERS_SUCCESS", payload: users });
} catch (error) {
dispatch({ type: "FETCH_USERS_FAILURE", error: error.message });
}
};
};
๋ฆฌ๋์
const initialState = {
loading: false,
users: [],
error: null,
};
function usersReducer(state = initialState, action) {
switch (action.type) {
case "FETCH_USERS_REQUEST":
return { ...state, loading: true, error: null };
case "FETCH_USERS_SUCCESS":
return { ...state, loading: false, users: action.payload };
case "FETCH_USERS_FAILURE":
return { ...state, loading: false, error: action.error };
default:
return state;
}
}
์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๊ธฐ
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchUsers } from "./actions";
function UserList() {
const dispatch = useDispatch();
const { loading, users, error } = useSelector((state) => state.users);
useEffect(() => {
dispatch(fetchUsers());
}, [dispatch]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;
โ ๏ธ Redux Thunk ์ฌ์ฉ ์ ์ฃผ์์
1. ๋ฏธ๋ค์จ์ด๊ฐ ํ์ ์์ ๊ฒฝ์ฐ
Redux Thunk๋ ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋๊ตฌ์ ๋๋ค. ๋๊ธฐ ์์ ๋ง ์๋ค๋ฉด ๋ถํ์ํ๊ฒ ์ฌ์ฉํ ํ์๋ ์์ด์.
2. ๋๋ฌด ๋ง์ ๋น์ฆ๋์ค ๋ก์ง
Thunk ๋ด๋ถ์์ ๋๋ฌด ๋ง์ ๋ก์ง์ ์ฒ๋ฆฌํ๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง ์ ์์ต๋๋ค.
๋น์ฆ๋์ค ๋ก์ง์ ์ก์ ์์ฑ์๋ณด๋ค๋ ๋ฆฌ๋์์์ ๊ด๋ฆฌํ๋ ๊ฒ ์ข์ต๋๋ค.
3. Redux Toolkit ํ์ฉ
์ต๊ทผ์๋ Redux Toolkit์ด ๊ธฐ๋ณธ์ ์ผ๋ก Thunk๋ฅผ ํฌํจํ๊ณ ์์ด ์ค์ ์ด ๋ ๊ฐ๋จํฉ๋๋ค. Redux Toolkit๋ ํ๋ฒ ๊ฒํ ํด ๋ณด์ธ์.
๐ Redux Thunk vs Redux Saga
Redux Thunk์ ์์ฃผ ๋น๊ต๋๋ ๋๊ตฌ๋ก๋ Redux Saga๊ฐ ์์ต๋๋ค.
๋ ๋ค ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ๋์ง๋ง, ์ ๊ทผ ๋ฐฉ์์ด ๋ค๋ฆ ๋๋ค:
ํน์ง | Redux Thunk | Redux Saga |
์ค์น ๋ฐ ์ฌ์ฉ | ๊ฐ๋จํ๊ณ ์ง๊ด์ | ์ค์ ๊ณผ ์ฝ๋๊ฐ ๋ ๋ณต์กํจ |
๋น๋๊ธฐ ์ฒ๋ฆฌ | ํจ์ ๊ธฐ๋ฐ์ผ๋ก ๋น๋๊ธฐ ์์ ์ฒ๋ฆฌ | ์ ๋๋ ์ดํฐ ๊ธฐ๋ฐ์ผ๋ก ๋ณต์กํ ์์ ์ฒ๋ฆฌ |
ํ์ต ๊ณก์ | ๋ฎ์ | ๋์ |
์ ์ค ์ผ์ด์ค | ๊ฐ๋จํ ๋น๋๊ธฐ ๋ก์ง | ๋ณต์กํ ๋น๋๊ธฐ ํ๋ฆ |
๐ ๊ฒฐ๋ก : Redux Thunk๋ก ๊น๋ํ ๋น๋๊ธฐ ๊ด๋ฆฌ ๐
Redux Thunk๋ ๋น๋๊ธฐ ์์ ์ Redux์ ํตํฉํ๊ธฐ ์ํ ํ๋ฅญํ ๋๊ตฌ์ ๋๋ค.
๊ฐ๋จํ API ํธ์ถ๋ถํฐ ๋ณต์กํ ๋ฐ์ดํฐ ํ๋ฆ๊น์ง, Redux Thunk๋ก ๊น๋ํ๊ฒ ์ฒ๋ฆฌํด ๋ณด์ธ์.
๐ท์ ์ค์ ๊ฐ๋ฐ์๊ฐ ๋์ด๋ด ์๋น!๐ท