let isLogin : false -> true
리액트에서 말하는 상태(state)란 캐시나 메모리와 같은 역할을 하는 자바스크립트 변수입니다
하지만 자바스크립트 변수는 브라우저를 새로고침하면 초기화된다는 문제가 있는데요
그래서 오늘 포스팅의 관건은 로그인 상태(true
)를 새로고침해도 유지되도록 만드는 것
이를 위해서는 로그인 상태를 로컬 스토리지에 저장해야 합니다
+) 토큰을 쿠키에 저장하는 로직은 백엔드에서 처리하는 것이 좋습니다
(프론트에서 document.cookie
를 사용하는 방법도 가능합니다만
리액트에서는 document 객체를 잘 사용하지 않기도 하고, 또 보안적인 이유도 크다고 합니다)
↓ 토큰 저장 예제 코드입니다
res.setHeader('Set-Cookie', `token=${token}; expires=${date}; path='/'`);
res.status(200).send()
// express에서 cookie-parser를 사용중이라면
res.cookie("token", token, { maxAge : 600_000, path : "/" });
+) 요청시 CORS 처리에도 주의할 것!
request.post(
"/auths",
{
userid: userid.value,
userpw: userpw.value,
},
{ withCredentials: true }
);
const storageState = localStorage.getItem("state")
console.log(storageState) // null
localStorage.getItem
: 데이터가 없으면 기존의 initialState
를 사용하기 const storageState = localStorage.getItem("state");
console.log(storageState);
const initialState = {
isLogin: false,
user: {},
};
// 로컬스토리지의 데이터 타입은 스트링
const initial = !storageState ? initialState : JSON.parse(storageState);
const [state, dispatch] = useReducer(rootReducer, initial);
이제 로컬스토리지에 데이터가 존재한다면 앞으로는 로컬스토리지에 저장된 상태를 기본 상태로 사용하게 됩니다
localStorage.setItem
:dispach
함수 수정하기 const [state, dispatch] = useReducer(rootReducer, initial);
const globalState = {
state,
dispatch: (action) => {
dispatch(action);
// 로컬 스토리지에 관련된 코드 작성
// setItem을 할 때는 key, value 형태로 입력해야 합니다
localStorage.setItem('state', JSON.stringify(rootReducer(state, action)));
}
}
return (
<Context.Provider value={globalState}>{children}</Context.Provider>
);
};
{isLogin: true}
인 객체를 로컬스토리지에 저장합니다
[Logout.jsx]
import { useStore } from "../store"
import { useEffect } from "react"
import { useNavigate } from "react-router-dom"
export const Logout = () => {
const { dispatch } = useStore()
const navigate = useNavigate()
useEffect(()=> {
dispatch({ type: "LOGOUT"})
document.cookie = "token=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;";
navigate("/")
}, [])
}
[reduce.jsx]
export const rootReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return { ...state, isLogin: action.payload };
case "LOGOUT":
return { ...state, isLogin: false };
default:
return state;
}
};
쿠키 삭제는 백엔드 요청 없이 프론트 측에서 document.cookie
를 사용하기도 한다고 하네요
로컬스토리지의 상태 저장 로직을 커스텀 훅으로 만들어서 사용합니다
[usePersistedState.jsx]
import { useState, useEffect } from "react";
// localstorage의 key와 value(상태)
export const usePersistedState = (key, initialState) => {
const [state, setState] = useState(() => {
const storagedState = localStorage.getItem(key);
return !storagedState ? initialState : JSON.parse(storagedState);
});
// state가 바뀔 때 실행할 코드
useEffect(() => {
localStorage.setItem("state", JSON.stringify(state));
}, [key, state]);
return [state, setState];
};
[store/index.jsx]
import { createContext, useContext, useReducer } from "react";
import { rootReducer } from "./reducer";
import { usePersistedState } from "../hooks/usePersistedState";
export const Context = createContext();
export const useStore = () => useContext(Context);
export const StoreProvider = ({ children }) => {
// const storageState = localStorage.getItem("state");
const initialState = {
isLogin: false,
user: {},
};
// const initial = !storageState ? initialState : JSON.parse(storageState);
const [state, dispatch] = useReducer(rootReducer, initialState);
const [persistedState, setPersistedState] = usePersistedState("state", state)
const globalState = {
state : persistedState,
dispatch: (action) => {
dispatch(action);
// localStorage.setItem('state', JSON.stringify(rootReducer(state, action)));
setPersistedState(rootReducer(persistedState, action))
},
};
return <Context.Provider value={globalState}>{children}</Context.Provider>;
};
useState
와 usePersistedState
를 구분해서 globalState에서 동기화하는 이유_이유는 두 상태관리 함수의 용도가 다르기 때문입니다
useState
는 컴포넌트의 상태를 관리하는 데 사용되는 일반적인 상태관리 함수이며,
usePersistedState
는 localStorage에 데이터를 저장하는 용도로 사용합니다
(새로고침으로 컴포넌트가 리렌더링될 때에도 상태를 유지하기 위해서)
그리고 서로 다른 상태인 state
와 persistedState
를useEffect
를 이용해서 동기화하는 구조입니다
state
가 업데이트될 때마다 localStorage에 데이터를 저장하고,
key
가 변경될 때마다 localStorage에서 데이터를 가져와서 state
를 초기화합니다
주의할 점!
reducer
함수는 업데이트된 상태 객체를 리턴하는 함수이지, 상태를 바꾸는 함수가 아닙니다
실제로 상태를 바꾸는 역할은 그것을 전달받은 dispatch
(=setState
)가 담당합니다