
리덕스에서 dispatch를 하면 action이 reducer로 전달이 되고 reducer는 새로운 state를 반환한다. 미들웨어를 사용해 이 과정 사이에 추가하고 싶은 작업을 넣을 수 있다.
보통 비동기 작업을 하기 위해 미들웨어를 사용한다. 리덕스 미들웨어 라이브러리를 설치해 사용한다. 비동기 작업에 관련된 미들웨어 라이브러리는 redux-thunk, redux-saga, redux-observable, redux-promise-middleware 등이 있다.
thunk는 지연된 작업을 수항하는 코드 조각을 의미하는 프로그래밍 용어이다. 바로 일부 논리를 실행하는 대신 나중에 작업을 수행하는 데 사용할 수 있는 함수 본문이나 코드를 작성할 수 있다.
Redux에서 thunk는 redux store 및 메서드와 상화작용 할 수 있는 내부 논리가 있는 함수를 작성하는 패턴이다. redux에서 thunk를 사용하려면 redux-thunk 미들웨어 라이브러리를 추가해 사용할 수 있다. 동기 및 비동기 논리를 모두 포함하는 방법이다.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
export const fetchTodos = createAsyncThunk(
"getTodos",
(payload, thunkAPI) => {
}
);
thunk로 서버에서 값을 가져오기
axios.get() 함수는 promise를 반환한다. 반환된 promise의 fulfilled 또는 rejected된 것을 처리하기 위해 비동기인 async/await를 추가하였다.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
export const fetchTodos = createAsyncThunk(
"getTodos",
async (payload, thunkAPI) => {
try {
const response = await axios.get('http://localhost:3001/todos');
return response.todos;
} catch (err) {
return err;
}
}
);
redux-toolkit으로 redux를 구현하였을때 createSlice() 내부의 reducer에서 액션 함수를 생성해야 하는데 redux-thunk를 사용한다면 extraRuducers 안에 액션 함수를 구현해야 한다.
pending: 네트워크 요청 시작 후 로딩 상태
fulfilled: 네트워크 요청 완료
rejected: 네트워크 요청 실패, 에러
export const todosSlice = createSlice({
name: "todos",
initialState,
reducers: {},
extraReducers: {
[fetTodos.pending]: (state) => {
state.isLoading = true;
},
[fetTodos.fulfilled]: (state, action) => {
state.isLoading = false;
state.todos = action.payload;
},
[fetTodos.rejected]: (state, action) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {},
extraReducers: builder => {
builder
.addCase(fetchTodos.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchTodos.fulfilled, (state, action) => {
state.isLoading = false; // 네트워크 요청이 끝났으니, false로 변경합니다.
state.todos = action.payload;
})
}
});
dispatch로 store의 값을 조회한다.
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { fetchTodos } from "./redux/modules/todosSlice";
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetTodos());
}, [dispatch]);
return <div>App</div>;
};
export default App;
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
export const fetchTodos = createAsyncThunk(
"getTodos",
async (payload, thunkAPI) => {
try {
const response = await axios.get('http://localhost:3001/api/todos');
return response.todos;
} catch (err) {
return err;
}
}
);
export const todosSlice = createSlice({
name: "todos",
initialState,
reducers: {},
extraReducers: {
[fetTodos.pending]: (state) => {
state.isLoading = true;
},
[fetTodos.fulfilled]: (state, action) => {
state.isLoading = false;
state.todos = action.payload;
},
[fetTodos.rejected]: (state, action) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
export const {} = todosSlice.actions;
export default todosSlice.reducer;
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { fetchTodos } from "./redux/modules/todosSlice";
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetTodos());
}, [dispatch]);
return <div>App</div>;
};
export default App;
출처
리덕스 미들웨어 - 벨로퍼트와 함께하는 모던 리액트
Writing Logic with Thunks - Redux Docs
Redux Essentials, Part 5: Async Logic and Data Fetching - Redux Docs
redux toolkit-thunk를 이용해서 비동기 작업을 처리하는 방법 - 생활코딩