[React] Redux (feat. 로그인 세션 저장)

조수현·2025년 9월 7일

서론

저번에 분량 조절 실패로 막 잘라내느라 짧아졌는데 원래는 하나의 글이었음,,,

참고

기존 로그인

  • 로그인 시 session storage에 세션 저장
  • 세션이 필요한 컴포넌트 마다 useEffect를 사용하는 등 세션의 존재 여부를 확인하는 로직이 필요함
  • 아마 저번에 작성한 custom hook을 만들어 코드 단축이 가능할 것 같으나 이번엔 전역 상태 관리하는 리덕스를 사용해 편리하게 만들어 보겠다!

로그인 확인 로직

  • 로그인 여부를 확인할 때마다 해당 코드가 중복적으로 작성 됨
const checkSession = async () => {
    const session = sessionStorage.getItem('mayl-session')

    if (session) return;

    navigate("/sign-in");
  };

  useEffect(() => {
    checkSession();
  }, []);

리덕스 사용한 로그인

  • redux를 사용하면 세션이 생기고 사라지는 것을 상태값으로 관리가 가능해 페이지 새로 고침없이 업데이트가 가능하다
  • 기존 로그인은 페이지 진입 시에만 로그인 확인을 하고 실시간으로 로그인, 로그아웃을 감지하기 위해서는 추가 로직이 필요하다
  • 리덕스로만 사용하면 탭을 껐다 켰을 때 로그인 유지가 안 되기 때문에 redux-persist라는 라이브러리로 storage 관리를 해 준다

스토어

  • 리덕스의 전역 상태 관리할 스토어를 생성해 준다
  • auth라는 이름으로 세션을 관리할 예정
export const store = configureStore({
  reducer: {
    auth: null, // 리듀서를 생성해서 넣어야 함
  },
});

리듀서(슬라이스)

  • 리덕스도 휴스탄트와 마찬가지로 슬라이스 개념이 있다
  • 리듀서 하나로 스토어를 관리할 수도 있지만 스토어 하나를 생성해서 그 안에서 파티션처럼 쪼개서 사용하는 것 같다
  • 전에는 스토어 여러개로 만들면 되지 않나 했는데 Provider로 감싸서 사용 해야하는데 여러 Provider를 만드는 것보다 성능면에서 좋지 않을까 싶다
import { createSlice } from "@reduxjs/toolkit";

const authSlice = createSlice({
  name: "auth",
  initialState: {
    session: null,
  },
  reducers: {
    setSession: (state, action) => {
      console.log(action.payload);
      
      state.session = action.payload;
    },
    removeSession: (state) => {
      state.session = null;
    },
  },
});

export const { setSession, removeSession } = authSlice.actions;

export default authSlice.reducer;

액션

  • 전 글에서 액션이라는 이벤트를 정의 객체를 생성하고 dispatch시 사용한다고 했는데
  • 슬라이스 패턴을 사용할 때는 사용하지 않는 것 같다
  • 액션은 한 리듀서 안에서 여러 액션을 처리할 때 주로 사용한다
  • 슬라이스 패턴은 리듀서를 여러개로 나눠서 작성하기에 역으로 슬라이스 객체에서 액션을 꺼내 쓰는 형태를 보이고 있다
export const { setSession, removeSession } = authSlice.actions;

로그인 로직

  • 원래 로직은 sessionStorage.setItem(KEY, session) 형태로 사용 되었으나 dispatch 메서드로 변경 되었다
import { useDispatch } from "react-redux";
import { setSession } from "../store/auth";

const dispatch = useDispatch();


const handleSignIn =async()=>{
  //생략
  const response = await signIn();
  dispatch(setSession(response.data.session));
}

리덕스 상태 불러오기

  • 전역상태가 필요한 곳에서 상태를 불러오기 위해서는 useSelector를 사용한다
  • 슬라이스 패턴을 사용했기에 auth에서 session을 가져온다
const session = useSelector((state) => state.auth.session);

return (
  //.. 생략
	 <span>세션: {session.access_token}</span>
)

redux-persist

  • 리덕스로만 구현하면 앱을 새로고침하면 상태유지가 안 되기 때문에 storage 저장도 필요하다
  • 이 라이브러리는 리덕스에 저장하면 자동으로 스토리지도 업데이트 해주는 라이브리러다
npm install redux-persist
npm i --save-dev @types/redux-persist

코드 추가

  • 세션에 저장할 key값과 어떤 스토리지를 사용할 지 정의한 객체를 생성한다
  • 스토리지 저장하기 위한 리듀서를 persist 리듀서로 감싼다
  • 감싼 리듀서를 스토어에 등록하고 스토어 또한 persist 스토어로 감싼 스토어를 만든다
  • 기존 스토어, persist 스토어 둘 다 필요함
const persistConfig = {
  key: "mayl-session",
  storage: storageSession,
};

const persistedReducer = persistReducer(persistConfig, authReducer);

export const store = configureStore({
  reducer: {
    auth: persistedReducer,
  },
});

export const persistor = persistStore(store)

provider 적용

  • main.tsx에서 적용한 store provider는 유지한 채
  • 내부에 persist store를 적용할 PersistGate를 삽입한다
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Provider store={store}>
      <PersistGate persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  </StrictMode>
);

로그인 화면

  • 리덕스와 세션에 동시에 저장되는 모습을 볼 수 있음

마무리

이 글에서 사용한 기능 말고도 리덕스를 활용할 수 있는 방법은 많다 첨부한 공식 문서를 참고해 다들 리덕스를 사용해 보시길

profile
프론트엔드 개발 블로그

0개의 댓글