관리 메뉴

거니의 velog

(36) 리덕스 툴킷 5 본문

SpringBoot_React 풀스택 프로젝트

(36) 리덕스 툴킷 5

Unlimited00 2024. 3. 6. 20:06

4. 쿠키를 이용한 애플리케이션 상태 저장

* 작성된 예제들은 로그인을 유지하거나 애플리케이션의 상태가 변경되는 상황에 대해서는 처리가 되지만, SPA의 근본적인 문제점인 '새로고침' 문제는 여전히 남는다. 로그인된 상황에서 브라우저의 '새로고침'을 호출하면 애플리케이션 자체가 다시 로딩되기 때문에 기존 애플리케이션의 상태 역시 초기화되는 문제가 발생한다.

* 이를 해결하기 위해서는 브라우저 내에 애플리케이션의 상태 데이터를 보관하고 애플리케이션이 로딩될 때 저장된 정보들을 로딩해서 사용하도록 구성해야만 하는데, 이를 위해서 주로 LocalStorage나 쿠키를 사용할 때가 많다. 예제에서는 유효시간을 가지는 쿠키를 사용해서 로그인한 상태를 저장한다.

* 리액트에서 쿠키를 사용하는 가장 간단한 방법은 react-cookie 라이브러리를 활용하면 된다. 그러면 쿠키를 저장하거나, 조회, 삭제 작업을 간단히 처리할 수 있다. 

* 아래의 CLI를 통해 react-cookie를 추가하자.

$ npm install react-cookie

* 쿠키를 사용하는 작업을 편하게 하기 위해서 util 폴더를 추가하고 cookieUtil.js 파일을 추가한다. cookieUtil에는 쿠키의 저장/조회/삭제를 위한 함수들을 정의한다.

import { Cookies } from "react-cookie";

const cookies = new Cookies();

export const setCookie = (name, value, days) => {
  const expires = new Date();
  expires.setUTCDate(expires.getUTCDate() + days); // 보관기한
  return cookies.set(name, value, { path: "/", expires: expires });
};

export const getCookie = (name) => {
  return cookies.get(name);
};

export const removeCookie = (name, path = "/") => {
  cookies.remove(name, { path });
};

(1) 로그인 결과의 쿠키 보관

* 로그인에 성공하면 결과를 쿠키로 보관하는 처리를 추가한다. 주의할 점은 로그인 결과는 객체지만 쿠키에는 문자열만이 들어갈 수 있기 때문에 JSON.stringify() 를 사용해서 문자열로 구성해야만 한다.

* loginSlice에서 cookieUtil을 이용하도록 수정한다.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { loginPost } from "../api/memberApi";
import { setCookie } from "../util/cookieUtil";

(...)

const loginSlice = createSlice({
  (...)
  extraReducers: (builder) => {
    builder
      .addCase(loginPostAsync.fulfilled, (state, action) => {
        console.log("fulfilled");

        const payload = action.payload;

        // 정상적인 로그인시에만 쿠키를 저장
        if (!payload.error) {
          setCookie("member", JSON.stringify(payload), 1); // 1일
        }

        return payload;
      })
      .addCase(loginPostAsync.pending, (state, action) => {
        console.log("pending");
      })
      .addCase(loginPostAsync.rejected, (state, action) => {
        console.log("rejected");
      });
  },
});

export const { login, logout } = loginSlice.actions;

export default loginSlice.reducer;

* 프로젝트를 실행해서 로그인이 정상적으로 실행되면 아래와 같이 member 이름의 쿠키가 생성된 것을 확인할 수 있다. 보관되는 쿠키는 Refresh Token과 동일하게 1일간 유지되도록 설정한다.


[애플리케이션 로딩 시 쿠키 활용]

* 쿠키로 저장된 로그인 결과는 애플리케이션의 실행 시에 사용되어야 한다. 이를 위해 loginSlice에서 초기 상태를 함수로 처리해서 member라는 이름의 쿠키가 있는지 확인하도록 변경한다.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { loginPost } from "../api/memberApi";
import { setCookie, getCookie } from "../util/cookieUtil";

const initState = {
  email: "",
};

export const loginPostAsync = createAsyncThunk("loginPostAsync", (param) => {
  return loginPost(param);
});

const loadMemberCookie = () => {
  //쿠키에서 로그인 정보 로딩
  const memberInfo = getCookie("member");

  //닉네임 처리
  if (memberInfo && memberInfo.nickname) {
    memberInfo.nickname = decodeURIComponent(memberInfo.nickname);
  }

  return memberInfo;
};

const loginSlice = createSlice({
  name: "LoginSlice",
  initialState: loadMemberCookie() || initState, // 쿠키가 없다면 초깃값사용
  (...)
});

export const { login, logout } = loginSlice.actions;

export default loginSlice.reducer;

* 변경된 loginSlice는 실행될 때 member 쿠키를 먼저 찾아보고 없는 경우에는 기본값을 가지도록 구성된다. 애플리케이션이 초기화될 때 loginSlice로 초기화되므로 member 쿠키가 있는 상태에서는 '새로고침'을 해도 로그인 상태가 유지된다.

새로고침해도 유지됨

* 브라우저에서 보관되는 member 쿠키를 삭제한 후에 '새로고침'을 하면 기존과 같이 로그인이 되지 않은 상태로 초기화 된다.

쿠키를 삭제하고 새로고침을 하면 로그인으로 바뀜


[로그아웃의 쿠키 처리]

* loginSlice에서 로그아웃은 member 쿠키를 삭제하도록 수정한다.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { loginPost } from "../api/memberApi";
import { setCookie, getCookie, removeCookie } from "../util/cookieUtil";

(...)

const loginSlice = createSlice({
  name: "LoginSlice",
  initialState: loadMemberCookie() || initState, // 쿠키가 없다면 초깃값사용
  reducers: {
    login: (state, action) => {
      console.log("login.....");

      // {email, pw} 로 구성
      const data = action.payload;

      // 새로운 상태
      return { email: data.email };
    },
    logout: (state, action) => {
      console.log("logout....");

      // 쿠키를 삭제
      removeCookie("member");

      // 값을 초기화
      return { ...initState };
    },
  },
  
(...)

* 쿠키의 삭제는 생성과 달리 브라우저에서 바로 반영되지 않으므로 로그아웃 후에 다른 메뉴를 클릭했다가 다시 확인해야만 한다.


 

'SpringBoot_React 풀스택 프로젝트' 카테고리의 다른 글

(38) 리액트 소셜 로그인 1  (0) 2024.03.06
(37) 리덕스 툴킷 6  (1) 2024.03.06
(35) 리덕스 툴킷 4  (0) 2024.03.06
(34) 리덕스 툴킷 3  (0) 2024.03.06
(33) 리덕스 툴킷 2  (0) 2024.03.06