깃허브 데스크탑으로 프로젝트 관리하기 강의 오픈!! (인프런 바로가기)
참고
- GitHub OAuth Project Settings
- Material UI로 깃허브 로그인 프로필 만들기
- 깃허브 OAuth 콜백 처리하기
- 인증 토큰 획득 서버 구현하기
- 인증 토큰으로 로그인 상태 관리하기
- 로그인 정보를 활용하여 Commit Message 남기기
- 새 창으로 로그인해서 현재 상태 유지하기
이제 로그인이 되면 버튼을 로그아웃으로 변경하고 프로필 사진도 변경해보자
로그인 체크 함수
이전 글에서 로그인 이후 얻은 정보를 로컬 스토리지에 저장하였다.
localStorage.setItem("GITHUB_TOKEN", token);
localStorage.setItem("AVATAR_URL", avatarUrl);
localStorage.setItem("LOGIN_ID", loginID);
이를 이용해서 현재 로그인 상태를 알 수 있다.
githublibrary.js를 만들어서 아래의 코드를 추가하자.
정상적으로 로그인된 상태라면 useState로 loginStatus를 true로 변경할 것이다.
에러가 발생하면 catch에서 false로 처리한다.
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);
}
};
Callback - 로그인 상태 변경하기
App.js에서 login 상태를 관리하기 위해 useState로 loginStatus를 추가한다.
그리고 GitHubLoginButton과 GitHubLoginCallback Component에 props로 넘겨준다.
import React, { useEffect, useState } from "react";
...
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}
/>
...
</div>
<Routes>
...
<Route
path="/callback"
element={
<GitHubLoginCallback
setLoginStatus={setLoginStatus}
/>
}
/>
</Routes>
</div>
);
};
export default App;
GitHubLoginCallback은 props를 받도록 수정한 후, getAccessToken에서 try catch로 login status를 변경한다.
import React, { useEffect } from "react";
import axios from "axios";
import queryString from "query-string";
const clientID = process.env.REACT_APP_CLIENT_ID;
const GitHubLoginCallback = ({ loginStatus, setLoginStatus }) => {
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);
} 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;
로그인이 실패한다면 아래와 같이 페이지가 변경된다.
로그인 버튼, 프로필 사진 변경하기
localStorage로 로그인 상태를 관리하기 때문에, 로그아웃은 아래와 같이 매우 간단하게 구현할 수 있다.
const logout = () => {
setLoginStatus(false);
localStorage.removeItem("GITHUB_TOKEN");
localStorage.removeItem("AVATAR_URL");
localStorage.removeItem("LOGIN_ID");
};
loginStatus가 callback에서 상태가 변경되므로, GitHubLoginButton도 아래와 같이 코드를 수정한다.
여기서는 단순하게 loginStatus에 따라 로그인 / 로그아웃 프로필과 버튼을 구분하였다.
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;
이제 로그인을 하면 프로필과 버튼이 변경되고, 로그아웃을 누르면 원래 상태로 돌아가게 된다.
useNavigate 리다이렉션
로그인이 정상적으로 완료된 후라면 callback 페이지에 남을 이유가 없다.
useNavigate를 이용해서 home으로 돌아가도록 코드를 수정하자.
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
const GitHubLoginCallback = ({ loginStatus, setLoginStatus }) => {
const navigate = useNavigate();
const getAccessToken = async (code) => {
try {
...
setLoginStatus(true);
navigate('/', { replace: true });
} catch (e) { ... }
};
...
로그인이 완료되면 첫페이지로 돌아가는 것을 알 수 있다. (URL 참고)
전체 코드는 다음과 같다.
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;
page/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;
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;
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);
}
};
'개발 > React' 카테고리의 다른 글
리액트 - 새 창으로 로그인해서 현재 상태 유지하기 (0) | 2023.08.19 |
---|---|
리액트 - GitHub OAuth 로그인 정보를 활용하여 Commit Message 남기기 (0) | 2023.08.19 |
리액트, Node JS - 인증 토큰 획득 서버 구현하기 (GitHub Access Token Server with Node JS) (0) | 2023.08.19 |
리액트 - 깃허브 OAuth 콜백 처리하기 (GitHub OAuth Callback) (0) | 2023.08.19 |
리액트 - Material UI로 깃허브 로그인 프로필 만들기 (0) | 2023.08.19 |
댓글