리덕스에서 미들웨어란 액션을 보내는 순간부터 스토어에 도착하는 순간까지 사이에 서드파티 확장을 사용할 수 있는 지점을 제공한다.
이를 통해서 로깅, 충돌 보고, 비동기 API 통신, 라우팅 기타 등등을 할 수 있다.
기본적인 Redux Store를 사용하면 액션을 디스패치하여 간단한 동기식 업데이트만 수행할 수 있다. 이때 미들웨어를 이용하면 스토어의 기능을 확장하고 스토어와 상호작용하는 비동기 로직을 작성할 수 있게 도와준다.
그러한 미들웨어 서드파티 중 redux-thunk는 스토어에 액세스해야 하는 복잡한 동기식 로직이나 AJAX 요청과 같은 간단한 비동기 로직을 포함한 기본적인 Redux 부수 효과 로직에 권장되는 미들웨어이다.
thunk의 다른 말은 함수로 다른 함수가 반환하는 함수에 대한 특별한 이름이다.
function wrapperFunction() {
return function thunk() {
console.log('I"m thunk');
}
}
리덕스에서 액션은 type 속성이 필수적으로 있고 그 외의 작업을 수행하기 위한 필요한 정보들(payload)을 포함하고 있는 일반 객체로 평범하고 단순하며 비활성 상태이다.
그런데 만약 이렇게 필요한 정보들을 바로바로 만들어서 보내주지 못할 때, 예를 들어 API를 호출하거나 다른 동작들을 dispatch 하는 행위들을 하기 위해서는 리듀서는 순수해야 하기 때문에 리듀서 내부에서는 API를 호출하거나 작업을 디스패치할 수 없다.
중요한 것은 액션이 비활성화 상태인데 이러한 액션으로 어떤 작업을 수행하려면 해당 코드가 함수 안에 있어야 하고 이 함수가 바로 수행해야 할 작업의 묶음이다.
function getPosts() {
return function(dispatch,getState) {
return axios.get('/posts').then(function(res) {
dispatch(setPost(res.data));
})
}
}
위 코드와 같이 함수를 넘겨주어서 처리를 한 이후에 dispatch를 실행하는 방법이다. 위를 추상화 하면 아래처럼 작성할 수 있다.
dispatch(func)
function func {
dispatch(actionCreator());
}
리덕스 toolkit에는 createAsyncThunk라는 메서드를 이용하여 thunk 함수를 만들 수 있다.
const addNumberThunk = createAsyncThunk(
"ADD_NUMBER_WAIT",
(payload, thunkAPI) => {
},
)
const addNumberThunk = createAsyncThunk(
"ADD_NUMBER_WAIT",
(payload, thunkAPI) => {
// addNumber는 액션 생성자 함수
thunkAPI.dispatch(addNumber(payload));
},
)
Thunk 자체의 개념은 비동기만을 위한 개념은 아니지만 아무래도 비동기를 위해서 많이 사용함으로 비동기에 대한 처리를 쉽게 할 수 있도록 createSlice에서 로직을 구현해놓은 것 같다.
Thunk함수 내에서 fulfillWithValue와 rejectWithValue은 툴킷에서 제공하는 API로 resolve, reject의 경우에 따라서 dispatch를 도와주는 API이다.
... createAsyncThunk
async (payload,thunkAPI) => {
try {
const data = await axios.get(`/posts/${paylaod.id}`);
return tnunkAPI.fulfillWithValue(data.data);
} catch(error) {
return thunkAPI.rejectWithValue(error);
}
}
위 처럼 반환한 각각의 API를 이용하여 Slice에 적용할 수 있는데
createSlice(
name: 'post',
initialState,
reducers: {},
extraReducers: {
[getPostThunk.pending]: (state,action) => {},
[getPostThunk.fulfilled]: (state,action) => {},
[getPostThunk.rejected]: (state,action) => {}
}
);
위 와 같이 각 경우에 대해서 로직을 조작할 수 있다.