[FIFAPulse] 개발기록 - 새로고침 시 context값 초기화 되는 현상 해결하기

조민호·2023년 4월 28일
1


문제 상황

현재 프로젝트에서 전역으로 context로 현재 이 웹에 로그인 된

구글계정의 정보를 저장해놓고 사용하고 있었다

이렇게 유저 정보 객체를 상태로 지정해주고 context로 사용하면서

로그인 창에서 로그인을 진행하게 되면 setUsetObj를 통해 usetObj상태값을 변경해 주고 있었다



그러던 도중 모든 페이지에서 새로고침을 했는데, 갑자기 context로 사용하는 값이 초기화 되는 현상을 발견했다

즉, 로그인을 하고 난 상황에서 새로고침을 하게 되면
context에서 로그인 정보가 죄다 사라지게 되는 것이다

  • 최초 상황에는 아래와 같이 현재 로그인이 되어있는지 알려주는 isLoggedIn , useObj 상태들이 제대로 찍힌다

  • 그렇지만 동일 페이지에서 새로고침을 하게 되면 isLoggedIn , useObj 상태들이 전부 초기값이 돼버리는 것이다

원인


처음에는 useMemo 훅으로 메모이제이션이 잘못 돼서 그런줄 알았다

근데 아무리 코드를 봐도 useMemo 훅 자체는 의존성 배열에 넣어준

값이 바뀔때만 함수의 리턴값을 반환해서 불필요한 리렌더링을 방지하는 역할일 뿐 이므로

개념상 갑자기 context 값을 초기화 하는데 관여할 리가 없었고

의존성 배열 역시 제대로 선언을 해 두었다

그래서 혹시 context로 사용하는 value들 또한 useState이므로


이 역시 마찬가지로 새로고침하면 자동으로 useState의 초깃값을 사용

하게 되는건가? 싶었는데 그게 맞았다


단지 , Props Drilling 의 비효율성을 해결하기 위해

전역에서 해당 값들을 호출할 수 있도록 하는 것일 뿐이지

기존 상태를 계속해서 유지하거나 알아서 업데이트 해주는

기능은 전혀 존재하지 않는다

그러므로 이러한 새로고침 이슈 발생 시 , 기존 useState특성대로

초깃값을 사용하게 되는 것이다

쉽게 말해 , props로 넘겨주지는 과정만 생략된 것일 뿐이지

전역으로 사용되는 useState와 완전 동일하다는 것이다




해결


이런 문제의 해결책으로는

  1. 로컬 스토리지 사용하기
  2. 쿠키 사용 or 서버측에서 관리하기

같은 해결책들이 있었다

현재 나는 1인 프로젝트로 서버를 운영하고 있지 않기에

어쩔 수 없이 로컬스토리지를 사용하기로 했다



UserObjContext.tsx

import React, { createContext, useState, useContext, useMemo } from 'react';
import { userObjType, UserObjContextValue } from '../../types/context';

export const UserObjContext = createContext<UserObjContextValue | null>(null);

export const UserObjProvider = ({ children }: { children: React.ReactNode }) => {
	
	// 로컬 스토리지 값 사용
  const localStorageUserObj = JSON.parse(localStorage.getItem('userObj')!);

	// 로컬 스토리지 값을 (새로고침 시)초깃값으로 사용 
  const [userObj, setUserObj] = useState<userObjType | null>(localStorageUserObj);

  const memoValue = useMemo(() => {
    return { userObj, setUserObj };
  }, [userObj]);

  return <UserObjContext.Provider value={memoValue}>{children}</UserObjContext.Provider>;
};

export function useUserObjAPI() {
  return useContext(UserObjContext);
}

ChooseModeAndLogin.tsx (로그인이 진행되는 페이지)

const { isLoggedIn, setIsLoggedIn } = useLoginAPI()!; // context로 관리하는 로그인이 되어 있는지 알려주는 상태
const { userObj, setUserObj } = useUserObjAPI()!; // context로 관리하는 현재 로그인 중인 유저의 정보

...

// 로그인 됐을 때
setUserObj(obj);
setIsLoggedIn(true);
localStorage.setItem('userObj', JSON.stringify(obj));
localStorage.setItem('isLoggedIn', JSON.stringify(true));

...

// 로그아웃 됐을 때
setIsLoggedIn(false);
setUserObj(null);
localStorage.setItem('userObj', JSON.stringify(null));
localStorage.setItem('isLoggedIn', JSON.stringify(false));

위처럼 로그인 or 로그아웃이 됐을 때 , 해당 context의 상태 업데이트를 진행하고

동시에 로컬스토리지에도 저장 or 초기화를 해주면 된다

그렇다면 새로고침을 해도 context에서 useState의 초깃값을

로컬스토리지에서 가져다 쓰기 때문에 새로고침을 해도 기존 상태 값이 유지가 된다


(혹시 헷갈릴까봐)

이럴꺼면 로컬스토리지만 사용하지 왜 굳이 context까지 사용하는거지?

로컬스토리지는 일반 값이 저장된 형태고 context에는 상태가 저장된 것이다
즉 , 리렌더링 작업이 필요한 컴포넌트에는 context를 사용하는 것이고 이때 새로고침이 되면 초기화가 돼버리니까 부수적으로 로컬스토리지의 힘을 잠시 빌리는 것이다

profile
할 수 있다

0개의 댓글