본문 바로가기

프로젝트 회고 🌊

리액트 기능구현 스터디-4. 카카오, 네이버, 구글 소셜로그인 리팩토링 회고 ☀️

1. 주제 소개

1) 완성 조건

STEP 1 : 소셜로그인 규격 로그인 버튼을 넣고 인가코드까지 받아오기. 

STEP 2 : 백엔드 서버를 통하여 accessToken 받고 로그인 완료. 로그아웃 완료 

STEP 3 : 자동로그인 , token 삭제시 라우팅처리

2) 시간

- 07.12 오후 3시~ 오후 9시 (리팩토링: 07.13)

3) 사용기술과 라이브러리

- React, styled-components, react-router-dom v6, axios, react-redux, redux-toolkit

 

4) 전체 회고

개인프로젝트로 하고 있던 블로그 프로젝트에 기존 로그인 말고도 소셜로그인을 추가했다. 기존로그인은 항해에서 올려준거라 언제 서버 꺼질지 몰라서... 일단 소셜로그인은 백엔드를 가지고 있으니 나중엔 이것만 살려둬야겠다.

 

 

 

 

카카오로 예를 들면 전체 흐름은 이렇다.

 

클라이언트에서 로그인 요청을 통해 인가 코드를 요청하고, 설정한 Redirect uri 로 인가 코드를 받는다. 클라이언트에서는 이 코드를 이용해 직접 backend에 http post 요청을 하고, 서버에서 토큰을 발급받는다. 이 토큰으로 로그인 또는 로그아웃처리를 해준다.

 

 

 

 

 

 

 

5) Trouble Shooting 💫

 

- Redirect URI

const OauthPage = () => {
    //인가 코드 추출
    const params = new URL(window.location.href).searchParams.get("code");

    const kakaoLogin = async () => {
        const response = await kakaoLogin(params);
        console.log(response);
    };
    
    useEffect(() => {
    	kakaoLogin();
    }, []);

    return <div>OauthPage</div>;
    };

export default OauthPage;

각각 서버, 클라이언트, 3사 로그인 설정 이 세 군데에서 모두 redirect_uri를 통일해야했었다. 간단하지만 3사가 각각 설정 방식도 다르고 처음 서버 코드를 보기 전까지 서버에도 통일해줘야하는지 몰라서 당황했었다 ... 

 

문제 해결은 3사 각각 auth 폴더에 페이지들을 만들어서 redirect_uri를 통일시켜줬다. docs를 더 꼼꼼하게 읽어보자

 

redirect page를 만들어 각각 구현해줬다. 구글을 예로 들어보면

const GoogleRedirect = () => {
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const params = new URL(window.location.href).searchParams.get("code");

  const handleGoogle = async () => {
    const response = await googleLogin(params);
    console.log(response);
    if (response) {
      //redux에 저장
      dispatch(googleSuccess(response.data.token));
      localStorage.setItem("accessToken", response.data.token);
      dispatch(loginSuccess());
      navigate("/home");
    }
  };

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

  return (
    <div>
      <div>google redirect</div>
    </div>
  );
};

export default GoogleRedirect;

 

 

 

 

 

 

- 로그인 유지 -> Redux

각 소셜 로그인용 토큰을 받아오면 redux에 저장했다. 저장하기 전에는 클라이언트에서 로그인 상태 관리가 안되서 로그인 유지가 어려웠었다. 이제 이 tokens module을 통해 useSelector로 token이 있는지 검사한 후, Router.js에서 isAuth를 로컬로 상태관리를 해준다. 

 

이 isAuth의 변화에 따라 <Outlet />으로 보여주고 싶은 부분만 보여준다.

 

예를 들면 login, register page는 isAuth가 false일때만 보여줘야한다.

그리고 home, write, mypage 등은 isAuth가 true일때만 보여줘야한다.

 

해당 토큰이 존재하고 유효한지 검사해야겠지?

localStorage.getItem을 통해 access token의 유무를 먼저 검사하는 checkLoginStatus 함수를 정의하고 Router의 useEffect에서 검사한다.

 

리팩토링 이후라 로그인상태 (isLoggedIn)을 리덕스로 관리해주고 있지만, 사실 useState을 통해 로컬로 관리해줘도 큰 문제는 없어보인다.

 

 

아래와 같이 구현한다.

// ./shared/Router.js

const checkLoginStatus = async () => {
  const token = localStorage.getItem("accessToken");
  if (!token) return false;

  return await verifyUser();
};

const Router = () => {
  const isLoggedIn = useSelector(({ user }) => user.isLoggedIn);
  const dispatch = useDispatch();

  useEffect(() => {
    const verifyAuth = async () => {
      const result = await checkLoginStatus();
      if (result) {
        dispatch(verifySuccess());
      } else {
        dispatch(verifyFailure());
      }
    };

    verifyAuth();
  }, [dispatch, isLoggedIn]);

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/kakao" element={<KakaoRedirect />} />
        <Route path="/naver" element={<NaverRedirect />} />
        <Route path="/google" element={<GoogleRedirect />} />

        <Route element={<AuthRoutes isAuth={isLoggedIn} />}>
          <Route path="/login" element={<LoginPage />} />
          <Route path="/register" element={<RegisterPage />} />
        </Route>

        <Route element={<ProtectedRoutes isAuth={isLoggedIn} />}>
          <Route path="/home" element={<PostListPage />} />
          <Route path="/write" element={<WritePage />} />
          <Route path=":postId" element={<PostPage />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};

export default Router;

const AuthRoutes = ({ isAuth }) => {
  return !isAuth ? <Outlet /> : <Navigate to={"/home"} />;
};

const ProtectedRoutes = ({ isAuth }) => {
  return isAuth ? <Outlet /> : <Navigate to={"/login"} />;
};

 

 

 

 

 

 

 

react/devlog-react