React에서 사용되는 State Management Library 중 하나이다. React 에서는 상태관리를 props, state로 하기 때문에 규모가 큰 프로젝트일 수록 상태관리 라이브러리를 필수로 사용해야 하는 것으로 알고있다. 단순한 글로벌 상태 관리를 위한 것이라면 이전 글에서 기록했던 Context API 로도 충분히 해결 가능하다. 따라서, 어떤 경우에 Redux가 사용되는지 찾아보았다.
프로젝트 규모가 클수록 Redux을 사용하는것을 추천한다고 한다. Context API와는 다르게 하나의 커다란 객체에 상태들을 넣어 관리하기 때문에 기능별로 Context을 생성할 필요가 없이 필요한 reducer을 생성해 관리하면 된다.
1번이유와 거의 유사하다고 생각한다. Redux에서 미들웨어도 thunk, saga로 나뉜다. 우리는 이 미들웨어를 통해 액션 객체가 리듀서에 처리되기 전에 우리가 원하는 작업을 할 수 있다.
Redux에서는 다양한 훅들을 제공한다. useSelector, useDispatch 등 컴포넌트에서 이 훅들을 통해 쉽게 값을 변경하고 가져올 수 있다.
이전에 redux-thunk을 사용해 본 경험이 있었다. 하지만 이번 프로젝트를 진행하면서 redux-toolkit을 알게되어 redux-toolkit을 통해 프로젝트의 redux을 구성해 보았다.
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: {
user: userSlice,
match: matchSlice,
},
});
index.tsx
import { Provider } from 'react-redux';
import store from './app/store';
root.render(
<Provider store={store}>
<App />
</Provider>,
);
2번까지는 이전에 redux을 사용하던 것과 같은 방법이었다. 이번 과정부터 redux-toolkit의 장점이 느껴졌다. redux-toolkit을 통해
match 정보를 저장하는 reducer 이다.
const matchSlice = createSlice({
name: 'match',
initialState,
reducers: {
getMatchInfo: (state, action: PayloadAction<[types]>) =>
action.payload,
},
});
createSlice을 통해 name, initialstate, reducers 기본적으로 3개의 값을 입력한다. 기존에는 action 함수를 따로 만들고 reducer에서 switch case을 통해 action.type값으로 어떤 state값에 변화를 줄 것인지 판단하여 변경했지만, redux-toolkit에서는 action생성 및 reducer 초기화 및 생성을 한번에 할 수 있다.
redux-toolkit은 추가적으로 미들웨어가 내장되어 있기 때문에 따로 미들웨어를 붙여주는 작업을 할 필요가 없다. 하지만 비동기 작업을 할 경우 createAsyncThunk 매소드를 사용해야한다.
redux-toolkit document에 정의된 파라미터 정의다.
createAsyncThunk(
1) type: 이 값에 따라 pending, fulfilled, rejected가 붙어 action creator을 반환한다.
2) payloadCreator: Promise을 반환하는 callback 함수이다. async await을 통해 처리하거나 에러를 반환할 경우RecjetWithValue(redux-toolkit 에서 제공)을 이용하거나 plain Error을 Promise.reject로 반환 해야한다.
3) options: 여기서 redux-saga의 기능을 이용할 수 있다. 비동기 함수 실행 전 실행 취소를 하거나 옵션을 통해 다른 기능을 수행한다.
)
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
const getUserInfoByToken = createAsyncThunk(
'users/getByToken',
async (token: string) => {
const response = await getUserAPI(token);
return response.data;
},
);
users/getByToken 값을 통해 [fulfilled, rejected, pending] action creator들을 생성해 준다.(ex) "users/getByToken/fullfilled" 하지만, reducer을 생성하는 기능은 이전에 createSlice로 생성한 곳에 extraReducers로 처리해야한다.
const userSlice = createSlice({
name: 'users',
initialState,
reducers: {
...
},
extraReducers: (builder) => {
builder.addCase(getUserInfoByToken.fulfilled, (state, action) => {
state.profile = action.payload;
state.isAuthenticaton = true;
});
builder.addCase(getUserInfoByToken.rejected, (state, action) => {...
});
builder.addCase(
createUserBySelf.fulfilled,
(state, action) => action.payload,
);
builder.addCase(createUserBySelf.rejected, (state, action) => {
console.error('error while operating createUserBySelf:');
});
},
});
builder.addCase을 통해 createAsyncThunk에서 정의한 콜백 함수에 따른 action을 실행한다.
builder.addCase(getUserInfoByToken.fulfilled, (state, action) => {
state.profile = action.payload;
state.isAuthenticaton = true;
});
이 코드 부분은 createAsyncThunk으로 생성한 함수인 getUserInfoByToken.fulfilled을 통해 콜백 함수가 값을 오류 없이 잘 반환하여 실행되었을 경우 아래 정의한 코드를 실행한다. return 값들은 action.payload에 담겨있다.
https://redux-toolkit.js.org/usage/usage-with-typescript
https://velog.io/@raejoonee/createAsyncThunk