리덕스는 언제나 불변성을 유지해야 한다. 그러나 내가 기존에 작성한 코드는 localStorage
에 값을 저장하고 삭제하는 등의 동작을 위해 slice 내부에 해당 로직을 작성했다. 그러나, localStorage
는 사이드 이펙트를 발생시키기 때문에, 이는 리덕스의 원칙을 위배한다고 볼 수 있따.
그렇기 때문에 리덕스는 이러한 사이드 이펙트를 발생시키는 로직은 middleware
에 작성하도록 한다.
미들웨어는 Request(요청)을 받고, Respond(응답)을 생성하는 프레임워크 사이에서 동작하는 코드다. 리덕스의 미들웨어는 로깅, 비동기 API와의 통신 라우팅 등에 사용된다.
// src/middleware/localStorageMiddleware.ts
import { createListenerMiddleware } from "@reduxjs/toolkit";
// Best to define this in a separate file, to avoid importing
// from the store file into the rest of the codebase
export const listenerMiddleware = createListenerMiddleware();
export const { startListening, stopListening } = listenerMiddleware;
먼저, 미들웨어를 설정하기 위해, 인스턴스를 생성해 준다. 그리고 생성한 미들웨어는, configureStore에 추가해 준다.
// src/share/store.ts
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import gameReducer from "slices/gameSlice";
import modalReducer from "slices/modalSlice";
import { listenerMiddleware } from "src/middleware/localStorageMiddleware";
const rootReducer = combineReducers({
game: gameReducer,
modal: modalReducer,
});
export function setupStore(preloadedState?: Partial<RootState>) {
return configureStore({
reducer: rootReducer,
preloadedState,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().prepend(listenerMiddleware.middleware), // 추가!
});
}
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = AppStore["dispatch"];
startListening
는 미들웨어의 새로운 리스너를 생성한다.
옵션으로는 다음 4가지 중 하나를 제공해야 한다.
1. type (문자열로 정확히 일치하는 action)
2. actionCreator (action 생성자를 지정해, 정확히 일치하는 action 1개)
3. matcher?: Matcher (toolkit의 액션 중 하나와 일치하는 경우)
4. predicate?: ListenerPredicate (액션과 상태를 기반으로 조건문을 설정)
위의 옵션 중 하나를 리스너에 지정하면, 조건에 맞을 경우 effect가 실행된다.
// src/slices/gameSlice.ts
startListening({
matcher: isAnyOf(dropMarker, reset),
effect: (_action: Action, listenerApi) => {
const newData = (listenerApi.getState() as RootState).game;
localStorage.setItem("connect-four", JSON.stringify(newData));
},
});
위와 같은 방식으로 미들웨어를 통해 localStorage와 통신을 하면, 리덕스의 원칙을 준수하면서, 사이드 이펙트를 발생시킬 수 있다.