본문 바로가기
개발/React

리액트 - 새 창으로 로그인해서 현재 상태 유지하기

by 피로물든딸기 2023. 8. 19.
반응형

깃허브 데스크탑으로 프로젝트 관리하기 강의 오픈!! (인프런 바로가기)

 

리액트 전체 링크

 

참고

- 깃허브 OAuth Access 토큰 발급 받기

- Toast UI 에디터로 깃허브 마크다운 저장하기

 

GitHub OAuth Project Settings
Material UI로 깃허브 로그인 프로필 만들기
깃허브 OAuth 콜백 처리하기
인증 토큰 획득 서버 구현하기
인증 토큰으로 로그인 상태 관리하기
로그인 정보를 활용하여 Commit Message 남기기
새 창으로 로그인해서 현재 상태 유지하기

 

로그인이 되지 않은 상태에서 편집을 하다가,

로그인 버튼을 누르면  callback으로 인해 편집된 내용이 사라지게 된다.

 

현재 상태를 유지한 채로 로그인을 하려면 window.open을 이용해 새 창을 열면 된다.

  const handleSave = async () => {
    let status = await gh.getLoginStatus();

    if (status) {
      let loginID = localStorage.getItem("LOGIN_ID");
      let markDownContent = editorRef.current.getInstance().getMarkdown();

      fileWrite(markDownContent, `submitted by ${loginID}`);
    } else {
      Swal.fire({
        icon: "error",
        title: "Oops...",
        text: "로그인 해주세요!!",
        showCancelButton: true,
      }).then((result) => {
        if (result.isConfirmed) {
          const clientID = process.env.REACT_APP_CLIENT_ID;
          const callbackURL = "http://localhost:3000/callback";
          const loginURL = `https://github.com/login/oauth/authorize?client_id=${clientID}&scope=repo:status read:repo_hook user:email&redirect_uri=${callbackURL}`;
          window.open(loginURL);
        }
      });
    }
  };

 

이제 새 창에서 로그인을 하였다.

따라서 편집기의 저장 버튼이 정상적으로 동작하게 된다.


전체 코드는 다음과 같다.

 

App.js

import React, { useEffect, useState } from "react";
import { Route, Link, Routes } from "react-router-dom";

import Home from "./page/Home";
import SimpleToastEditor from "./page/SimpleToastEditor";

import "./App.css";
import GitHubLoginButton from "./page/GitHubLoginButton";
import GitHubLoginCallback from "./page/GitHubLoginCallback";

import * as gh from "./githublibrary.js";

const App = () => {
  const [loginStatus, setLoginStatus] = useState(false);

  useEffect(() => {
    gh.loginCheck(setLoginStatus);
  }, []);

  return (
    <div>
      <div className="router">
        <GitHubLoginButton
          loginStatus={loginStatus}
          setLoginStatus={setLoginStatus}
        />
        <span>
          <Link to="/">Home</Link>
        </span>
        <span>
          <Link to="/editor">Toast UI Editor</Link>
        </span>
      </div>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/editor" element={<SimpleToastEditor />} />
        <Route
          path="/callback"
          element={
            <GitHubLoginCallback
              loginStatus={loginStatus}
              setLoginStatus={setLoginStatus}
            />
          }
        />
      </Routes>
    </div>
  );
};

export default App;

 

GitHubLoginButton.js

import React from "react";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import Avatar from "@mui/material/Avatar";

const clientID = process.env.REACT_APP_CLIENT_ID;
const callbackURL = "http://localhost:3000/callback";
const loginURL = `https://github.com/login/oauth/authorize?client_id=${clientID}&scope=repo:status read:repo_hook user:email&redirect_uri=${callbackURL}`;

const GitHubLoginButton = ({ loginStatus, setLoginStatus }) => {
  const logout = () => {
    setLoginStatus(false);
    
    localStorage.removeItem("GITHUB_TOKEN");
    localStorage.removeItem("AVATAR_URL");
    localStorage.removeItem("LOGIN_ID");
  };

  return (
    <div>
      <Box sx={{ m: 2 }}>
        {loginStatus === false && (
          <div>
            <Stack direction="row" spacing={2}>
              <Avatar alt="GitHub Login" src="/static/images/avatar/1.jpg" />
              <Button variant="outlined" color="secondary" href={loginURL}>
                로그인
              </Button>
            </Stack>
          </div>
        )}
        {loginStatus === true && (
          <div>
            <Stack direction="row" spacing={2}>
              <Avatar
                alt="GitHub Login"
                src={localStorage.getItem("AVATAR_URL")}
              />
              <Button variant="outlined" color="error" onClick={logout}>
                로그아웃
              </Button>
            </Stack>
          </div>
        )}
      </Box>
    </div>
  );
};

export default GitHubLoginButton;

 

GitHubLoginCallback.js

import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";

import axios from "axios";
import queryString from "query-string";

const clientID = process.env.REACT_APP_CLIENT_ID;

const GitHubLoginCallback = ({ loginStatus, setLoginStatus }) => {
  const navigate = useNavigate();

  const getAccessToken = async (code) => {
    try {
      let server = `http://192.168.55.120:3002`;
      let accessInfo = await axios.get(
        `${server}/githubLogin?code=${code}&clientID=${clientID}`
      );

      let token = accessInfo.data.token;

      const userResponse = await axios.get("https://api.github.com/user", {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });

      const userData = userResponse.data;
      const avatarUrl = userData.avatar_url;
      const loginID = userData.login;

      console.log(userData);
      console.log(avatarUrl);
      console.log(loginID);

      localStorage.setItem("GITHUB_TOKEN", token);
      localStorage.setItem("AVATAR_URL", avatarUrl);
      localStorage.setItem("LOGIN_ID", loginID);
      
      setLoginStatus(true);

      navigate('/',  { replace: true });
    } catch (e) {
      console.log(e);
      setLoginStatus(false);
    }
  };

  const getCode = () => {
    let qs = queryString.parse(window.location.search);
    let code = qs.code;
    getAccessToken(code);
  };

  useEffect(() => getCode(), []);

  return <div>{loginStatus ? "로그인 성공!" : "로그인 실패..."}</div>;
};

export default GitHubLoginCallback;

 

githublibrary.js

import axios from "axios";

export const loginCheck = async (setLoginStatus) => {
  let token = localStorage.getItem("GITHUB_TOKEN");
  try {
    const response = await axios.get("https://api.github.com/user", {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    console.log(response);
    setLoginStatus(true);
  } catch (error) {
    console.error("Error fetching user data:", error);
    setLoginStatus(false);
  }
};

export const getLoginStatus = async () => {
  let token = localStorage.getItem("GITHUB_TOKEN");
  try {
    const response = await axios.get("https://api.github.com/user", {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    console.log(response);
    return true;
  } catch (error) {
    console.error("Error fetching user data:", error);
    return false;
  }
};
반응형

댓글