redux-persist로 먼저 새로고침하더라도 redux의 상태들이 초기화되지 않도록 하는 과정을 먼저 거쳐야 한다.
npm으로 설치하기
npm install redux-persist
rootReducer.js
import { combineReducers } from "redux";
import loginSlice from './loginSlice';
import userInfoSlice from './userInfoSlice';
import modalSlice from './modalSlice';
import boardSlice from './boardSlice';
const rootReducer = combineReducers({
login: loginSlice,
userInfo: userInfoSlice,
modal: modalSlice,
})
export default rootReducer;
store.js
import { configureStore } from '@reduxjs/toolkit'
import {
persistReducer,
PERSIST,
PURGE
} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import rootReducer from '../reducers/rootReducer'
import logger from 'redux-logger';
const persistConfig = {
key: 'root',
version: 1,
storage,//localStorage에 저장해 store를 관리
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
//미들웨어 작성시 에러 주의
getDefaultMiddleware(
{
serializableCheck: {
ignoredActions: [PERSIST, PURGE],
},
}
).concat(logger)
})
export default store;
주의: 발생할 수 있는 에러
when using a middleware builder function, an array of middleware must be returned
=> 에러가 발생했던 코드middleware: (getDefaultMiddleware) => { /*에러나는 코드*/ getDefaultMiddleware().concat(logger); getDefaultMiddleware( { serializableCheck: { ignoredActions: [PERSIST, PURGE], }, } ) }
이처럼 애초에 middleware는 콜백함수 형태로 작성해야하는데 그게 아니라 함수를 실행?하는 것처럼 작성하여서 자꾸 에러를 뿜어내며 리액트를 렌더조차 못하고 있었던 것이었다. 따라서 한번만
getDefaultMiddleware
를 써서 불러오도록 해야한다.
index.js
import App from './App';
import store from './store/store'
import { persistStore } from 'redux-persist';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react';
const root = ReactDOM.createRoot(document.getElementById('root'));
export let persistor = persistStore(store);
root.render(
<BrowserRouter>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</BrowserRouter>
);
이렇게 설정해둘 경우 새로고침하면 더이상 store의 state들이 초기화되지 않는다.
유저정보를 삭제하기 위해서는 store를 purge하는 작업을 거쳐야 한다. persistConfig
에서 localStorage에 저장한 state들을 전부 초기화 시켜주기 위해서 필요한 과정이다.
공식문서에서는 다음과 같이 extraReducers를 사용해 적용하라고 한다.
리덕스 툴킷 사용 공식문서
import { PURGE } from "redux-persist";
...
extraReducers: (builder) => {
builder.addCase(PURGE, (state) => {
customEntityAdapter.removeAll(state);
});
}
이를 적용해 logout이 실행되는 시점에서 만들어둔 extraReducer로 store에 접근 가능하도록 해야한다.
loginSlice.js
export const loginSlice = createSlice({
name: actionName,
initialState,
reducers: {
login: (state, action) => {
state.isLogin = true;
state.accessToken = action.payload;
},
logout: (state) => {
state.isLogin = false;
}
},
//초기화하고 싶은 state가 있는 slice마다 아래를 추가해야한다.
extraReducers: (builder) => {
builder.addCase(PURGE, () => initialState);
}
})
로그인 상태를 관리하는 loginSlice.js말고, userSlice.js같은 리듀서가 또 있다면 해당 리듀서에서도 추가하여 initialState로 초기화시킬 수 있도록 한다. extraReducers를 추가해주지 않으면 state를 초기화시키지 못하고 있는 것을 볼 수 있을 것이다.
로그아웃이 실행되는 컴포넌트.js
import { persistor } from '../../index';
cons LogoutComponent = () => {
//초기화 하는 함수
const purge = async () => {
await persistor.purge();
}
}
return (
<NavButton onClick={async () => {
await handleLogout()
await setTimeout(() => purge(), 200)
}}>Logout</NavButton>
)
onClick 이벤트가 발생했을 때 먼저 로그아웃 관리하는 함수를 전부 완료한 후에 purge()를 진행하도록 비동기처리했다.
purge()함수를 만들어 처리하는 것에 대한 힌트는 redux-persist github 이슈: purge() doesn't work에 대한 답변중 하나를 참고했다.
주의할점:로그아웃 함수(handleLogout
)가 실행되기 전에 purge()해버리지 않도록 비동기처리와 setTimeout()
처리를 함으로써 로그아웃 처리를 완료 후에 초기화하도록 해야한다. 초기화가 먼저 실행되버리면 로그아웃할 유저에 대한 정보가 먼저 사라져 버릴수도 있는 위험을 방지하기 위해서이다.
마지막으로 localStorage나 redux의 store가 잘 초기화되는지 확인만 해보면 된다.
: 크롬브라우저에서 개발자도구를 켜 application탭을 누르면 확인 가능하다.
로그인 시 localStorage의 상태
로그아웃 시 localStorage의 상태
이건 확인할 수 있는 방법이 여러가지가 있다. getDefaultMiddleware().concat(logger)
로 찍히는 콘솔에서 직접 state를 확인해도 되고,
(action을 기준으로 상태값이 어떻게 달라지는지 확인 가능)
마찬가지로 크롬 개발자도구에서 redux 확장프로그램을 설치해 redux 상태 변화를 확인해 볼수도 있다.
두 방법으로 확인한 결과 모두 로그아웃시 상태값을 잘 초기화하는 것을 확인할 수 있다.