middleware로 localStorage 관리하기

no-pla·2024년 8월 12일
0

TIL

목록 보기
14/14
post-thumbnail

리덕스는 언제나 불변성을 유지해야 한다. 그러나 내가 기존에 작성한 코드는 localStorage에 값을 저장하고 삭제하는 등의 동작을 위해 slice 내부에 해당 로직을 작성했다. 그러나, localStorage는 사이드 이펙트를 발생시키기 때문에, 이는 리덕스의 원칙을 위배한다고 볼 수 있따.

그렇기 때문에 리덕스는 이러한 사이드 이펙트를 발생시키는 로직은 middleware에 작성하도록 한다.

middleware [미들웨어]

미들웨어는 Request(요청)을 받고, Respond(응답)을 생성하는 프레임워크 사이에서 동작하는 코드다. 리덕스의 미들웨어는 로깅, 비동기 API와의 통신 라우팅 등에 사용된다.

createListenerMiddleware

// 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

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와 통신을 하면, 리덕스의 원칙을 준수하면서, 사이드 이펙트를 발생시킬 수 있다.

0개의 댓글