yamoo9 / likelion-FEQA

질문/답변 — 프론트엔드 스쿨, 멋사
29 stars 9 forks source link

[LAB-8] onAuthStateChanged에서 일어나 상태변경이 다른 컴포넌트에서 적용되지 않습니다 #249

Closed rlawlsdn263 closed 1 year ago

rlawlsdn263 commented 1 year ago

질문 작성자

김진우

문제 상황

onAuthStateChanged(auth, (user) => {
  if (user) {
    const userId = user.uid;
   setCheckCurrentUserState(true);
    setUid(userId);
    console.log("사용자 로그인", checkCurrentUserState, uid);
  } else {
    setCheckCurrentUserState(false);
    setUid("");
    console.log("사용자 로그아웃", checkCurrentUserState, uid);
    }
  });

Header.jsx에서 onAuthStateChanged가 실시간으로 사용자의 로그인과 로그아웃 상태를 체크해주고 있습니다. 사용자가 로그인 했다면 checkCurrentUserState를 true로 바꿔주고 아니라면 false로 업데이트 해주고 있습니다.

{
          !checkCurrentUserState &&
          <>
            <Button type="button" aria-label="로그인" className="loginButton">
              로그인
            </Button>
            <Button type="button" aria-label="회원가입" className="registerButton">
              회원가입
            </Button>
          </>
        }
        {
          checkCurrentUserState && 
          <>
            <Button type="button" aria-label="로그아웃" className="loginButton" onClick={handleLogout}>
              로그아웃
            </Button>
          </>
        }

checkCurrentUserState의 state를 활용해 컴포넌트 UI를 조건부로 렌더링하는데 성공했습니다. checkCurrentUserState의 값이 정상적으로 업데이트된다는 걸 확인했습니다.

export function RequireAuth({children}) {

  const [checkCurrentUserState, setCheckCurrentUserState] = useRecoilState(checkCurrentUserStateAtom);
  const [uid, setUid] = useRecoilState(uidAtom);
  console.log(checkCurrentUserState, uid);

  return (
    checkCurrentUserState ? {children} : <Navigate to ="/login" />
  )
}

RequireAuth()라는 함수형 컴포넌트를 만들어줬습니다. 이 컴포넌트도 checkCurrentUserState를 확인하고 조건부로 동작하는 컴포넌트입니다.

로그인이 된 상태라면 checkCurrentUserState가 true이기 때문에 children으로 들어온 값이 보여져야 할테지만 로그인이 됐음에도 false가 나와 원하는 페이지로 이동을 하지 못하고 있습니다.

image

야무쌤께서 말씀하신 것처럼 비동기로 동작하는 리액트의 특성을 고려해야한다고 판단해 useEffect를 사용했지만 여전히 해결하지 못하고 있습니다.

useEffect(()=>{
    onAuthStateChanged(auth, (user) => {
      if (user) {
        const userId = user.uid;
        setCheckCurrentUserState(true);
        setUid(userId);
        console.log("사용자 로그인", checkCurrentUserState, uid);
      } else {
        setCheckCurrentUserState(false);
        setUid("");
        console.log("사용자 로그아웃", checkCurrentUserState, uid);
      }
    });
  }, [])

상태 업데이트로 계속 질문을 올리고 있는 거 같아 죄송합니다ㅠ

프로젝트 저장소 URL

https://github.com/likelion-frontendo/likelion-saja/tree/feature/jinwoo/register

yamoo9 commented 1 year ago

문제 원인

React Router는 클라이언트 측에서 라우팅 처리하는 도구이므로 직접 주소 창에 다른 입력하여 엔터(enter)를 누르는 방식으로 탐색하면 안됩니다.

문제 해결

결과적으로 로그인 인증 사용자 상태 유지는 잘 되고 있습니다. 😃 수정된 가이드 파일을 다운로드 받아 확인해보세요.

src.zip

Header.jsx

다만, 클라이언트 라우팅이 정상적으로 이뤄지도록 렌더링(JSX) 코드를 수정해야 합니다. Link 컴포넌트로 라우팅 할 수 있도록 구성해야 합니다.

<div className="buttonContainer">
  {!checkCurrentUserState && (
    <>
      <Link to="/login" className="loginButton">
        로그인
      </Link>
      <Button type="button" aria-label="회원가입" className="registerButton">
        회원가입
      </Button>
    </>
  )}
  {checkCurrentUserState && (
    <>
      <Link to="/mypage">마이 페이지</Link>
      <Button type="button" aria-label="로그아웃" className="loginButton" onClick={handleLogout}>
        로그아웃
      </Button>
    </>
  )}
</div>

그리고 useEffect 훅은 아래와 같이 수정해야 합니다. 이벤트 구독과 구독 해제가 순환될 수 있도록 설정해주세요.

useEffect(() => {
  // 종속성 배열에 포함된 항목이 변경될 때마다 이벤트 구독 시도 (먼저 클린업 함수로 이벤트 구독 해제 됨)
  const unsub = onAuthStateChanged(auth, (user) => {
    if (user) {
      const userId = user.uid;
      setCheckCurrentUserState(true);
      setUid(userId);
      console.log("사용자 로그인", checkCurrentUserState, uid);
    } else {
      setCheckCurrentUserState(false);
      setUid("");
      console.log("사용자 로그아웃", checkCurrentUserState, uid);
    }
  });

  // 리-렌더링 : "구독 해제 → 구독"을 위한 클린업 함수 반환
  return unsub;
}, [checkCurrentUserState, setCheckCurrentUserState, setUid, uid]);

라우트를 보호하는 컴포넌트 코드도 아래와 같이 수정해주세요.

RequiredAuth.jsx

import {Navigate} from "react-router-dom";
import {checkCurrentUserStateAtom} from "@/components/Header/Header";
import {useRecoilValue} from "recoil";
import {uidAtom} from "@/pages/Register/atoms/uidAtom";

export function RequireAuth({children}) {
  const checkCurrentUserState = useRecoilValue(checkCurrentUserStateAtom);
  const uid = useRecoilValue(uidAtom);

  console.log(checkCurrentUserState, uid);

  return checkCurrentUserState ? children : <Navigate to="/login" />;
}

아래 문제가 해결된 영상을 확인해보세요. 🙂

https://user-images.githubusercontent.com/1850554/227837186-98158299-7769-48ce-809f-514658485234.mp4

rlawlsdn263 commented 1 year ago

ㅠㅠㅠ야무쌤 감사합니다! 잘 동작하네요. 제가 계속 도메인 주소로 접근하려고 해서 그랬던 걸까요??

yamoo9 commented 1 year ago

ㅠㅠㅠ야무쌤 감사합니다! 잘 동작하네요. 제가 계속 도메인 주소로 접근하려고 해서 그랬던 걸까요??

네 맞아요. 😀 주소창에 URL을 입력하여 접근하는 방식은 React Router를 이용하지 않고 서버에 페이지를 요청하는 방식입니다. 그렇게 되면 상태는 초기화 됩니다.

rlawlsdn263 commented 1 year ago

아하 그래서였군요ㅠㅠ 감사합니다!!!!