학습 목표
- 미들웨어의 개념에 대한 이해
- Redux 구조에서 비동기를 다루는 기술(미들웨어)인 Thunk 에 대해 알아보고, 사용합니다.
Redux 미들웨어
미들웨어란?
리덕스에서 dispatch 를 하면, action 이 리듀서로 전달이 되고, 리듀서는 새로운 state 를 반환합니다. 근데 미들웨어를 사용하면 이 과정 사이에 우리가 하고 싶은 작업들을 넣어서 할 수 있습니다.
만약 counter 프로그램에서 더하기 버튼을 클릭했을 때, 바로 +1 을 하지 않고, 3초를 기다렸다가 +1이 되도록 구현하려면 미들웨어를 사용하지 않고서는 구현할 수 없습니다. 왜냐하면 dispatch가 되자마자 바로 action 이 리듀서로 달려가서 새로운 state 를 반환해버리기 때문이죠. 즉, 여기서 "3초를 기다리는 작업" 이 미들웨어가 해주는 것입니다.
보통 리덕스 미들웨어를 사용하는 이유는 서버와의 통신을 위해서 사용하는 것이 대부분이고, 그 중에서도 많이 사용되고 있는 리덕스 미들웨어는 Redux-thunk 라는 것이 있습니다.
Thunk
리덕스 thunk 란?
리덕스에서 많이 사용하고 있는 미들웨어 중 하나입니다. thunk 를 사용하면 우리가 dispatch 를 할 때 객체가 아닌 함수를 dispatch 를 할 수 있게 해줍니다. 즉, dispatch(객체) 가 아니라 dispatch(함수) 를 할 수 있게 되는 것이죠.
그래서 중간에 하고자 하는 작업을 함수를 통해 넣을 수 있고, 그것이 중간에 실행이 되는 것입니다. 그래서 아래 흐름과 같이 실행이 되는거죠. 그리고 이 함수를 thunk 함수라고 부릅니다.
dispatch(함수) → 함수실행 → 함수안에서 dispatch(객체)
thunk 사용하기
Redux Toolkit 에서 사용했던 프로젝트를 이용하거나 또는 새로운 프로젝트를 생성해, Redux 설정을 모두 마치고, 아래 예시코드를 작성합니다.
- thunk 함수 만들기
- extraReducer 에 함수 등록하기
- dispatch(thunk 함수) 하기
- 테스트
이제 본격적으로 thunk 함수를 만들어보겠습니다.
thunk 함수의 역할은 "3초를 기다리는 것" 입니다. 그리고 3초가 지나면 원래 하려고 했던 ADD_NUMBER 를 해주는 것까지가 thunk 함수가 해야 할 일입니다.
툴킷에서는 createAysncThunk 라는 API 를 사용해서 thunk 함수를 생성할 수 있습니다. 이 API 는 함수인데, 첫번째 인자에는 Action Value, 두 번재 인자에는 함수가 들어갑니다. 이 함수에 하고 싶은 작업들을 구현하면 됩니다.
두 번째 들어가는 함수에서도 이자를 꺼낼 수 있는데, 첫 번째 인자는 이 thunk 함수가 외부에서 사용되었을 때 넣은 값을 여기에서 조회할 수 있고, 두 번째 인자에서는 thunk 가 제공하느 여러가지 API 기능들이 담긴 객체를 꺼낼 수 있습니다. 일단 이 부분은 이해가 되지 않아 넘어갔습니다.
export const __addNumber = createAsyncThunk(
"ADD_NUMBER_WAIT",
(arg, thunkAPI)=>{},
);
thunk 함수는 createAsncThunk 라는 툴킷 API 를 사용해 생성합니다.
'__' 가 함수 이름에 붙는 이유는 이 함수가 thunk 함수라는 것을 표기하기 위함입니다. 개인의 convention 이기 때문에 함수의 이름은 본인이 편한 이름으로 명명해도 됩니다.
원래 하려고 했던 3초를 기다리는 thunk 함수를 아래 코드를 참고해 만들어보겠습니다. 첫 번째 자리에는 action value 를 넣었습니다. 두 번째에는 함수를 넣었죠.
함수 안에는 setTimeout 이라는 Web API 를 이용해서 3초를 기다리게 했고, 이후에 thunkAPI 안에 있는 dispatch 를 통해서 addNumber 라는 action creator 를 넣었습니다.
src/redux/modules/counterSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
export const __addNumber = createAsyncThunk(
// 첫번째 인자 : action value
"addNumber",
// 두번째 인자 : 콜백함수
(payload, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addNumber(payload));
}, 3000);
}
);
const initialState = {
number: 0,
};
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
addNumber: (state, action) => {
state.number = state.number + action.payload;
},
minusNumber: (state, action) => {
state.number = state.number - action.payload;
},
},
});
export const { addNumber, minusNumber } = counterSlice.actions;
export default counterSlice.reducer;
인자는 컴포넌트에서 사용자가 input 에 입력한 더하고자 하는 값입니다 예전에 구현했던 것인데, 이런식으로 payload 값을 받아올 수 있습니다.
컴포넌트에서의 코드는 아래와 같습니다. 기존에는 addNumber 라는 action creator 를 dispatch 했다면, 이제는 __addNumber 라는 thunk 함수를 dispatch 해줍니다.
src/App.jsx
import React from "react";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { minusNumber, __addNumber } from "./redux/modules/counterSlice";
const App = () => {
const dispatch = useDispatch();
const [number, setNumber] = useState(0);
const globalNumber = useSelector((state) => state.counter.number);
const onChangeHandler = (evnet) => {
const { value } = evnet.target;
setNumber(+value);
};
// thunk 함수를 디스패치한다. payload는 thunk함수에 넣어주면,
// 리덕스 모듈에서 payload로 받을 수 있다.
const onClickAddNumberHandler = () => {
dispatch(__addNumber(number));
};
const onClickMinusNumberHandler = () => {
dispatch(minusNumber(number));
};
return (
<div>
<div>{globalNumber}</div>
<input type="number" onChange={onChangeHandler} />
<button onClick={onClickAddNumberHandler}>더하기</button>
<button onClick={onClickMinusNumberHandler}>빼기</button>
</div>
);
};
export default App;
정리해서
- 리덕스 미들웨어를 사용하면, 액션이 리듀서로 전달되기 전에 중간에 어떤 작업을 더 할 수 있습니다.
- Thunk 를 사용하면, 객체가 아닌 함수를 dispatch 할 수 있게 해줍니다.(thunk 핵심)
- 리덕스 툴킷에서 Thunk 함수를 생성할 때는 createAsyncThunk 를 이용합니다.
- 두 번째로 들어가는 함수에서 2개의 인자를 꺼내 사용할 수 있는데, 첫 번째 인자는 컴포넌트에서 보내준 payload이고, 두 번째 인자는 thunk 에서 제공하는 여러가지 기능입니다.
- dispatch : thunk 함수 안에서 dispatch 할 때 사용.
- getState : thunk 함수 안에서 현재 리덕스 모듈의 state 값을 사용하고 싶을 때 사용
'항해 16기 > Today I Learned' 카테고리의 다른 글
[항해 33일차] TIL_React 심화주차: Custom Hooks (0) | 2023.09.12 |
---|---|
[항해 32일차] TIL_React 심화주차: Thunk 2 (0) | 2023.09.11 |
[항해 30일차] TIL_React 심화주차: axios 심화 - instance & interceptor (0) | 2023.09.07 |
[항해 29일차] TIL_React 심화주차: 비동기 통신 - axios, fetch (0) | 2023.09.07 |
[항해 27일차] TIL_React 심화주차: HTTP (0) | 2023.09.07 |