네비게이션 바의 login 부분을 로그인한 계정의 정보를 표기하는 기능과 로그아웃 기능을 추가하고자 한다. 기존 HomePage.js에서 로그인한 정보를 표기하고 있는데, 이를 네비게이션 바에서 표기하도록 하는 것이다.
로그인 이후에는 로그아웃 전에는 로그인페이지로 돌아갈 수 없게하는 기능도 구현해보려 한다.
기존의 HomePage.js에서는 다음과 같은 코드를 통해 로그인 정보를 불러왔다.
// 사용자 정보를 가져오는 함수
const fetchUserInfo = async () => {
try {
const response = await axios.get('http://localhost:8080/oauth/user-info');
setUserInfo(response.data);
} catch (error) {
console.error("사용자 정보 조회 중 오류 발생:", error);
}
};
NavBar.js에 이 부분을 옮기면 그대로 사용할 수는 있지만, 그렇게 하면 NavBar.js가 너무 복잡해진다. 네비게이션 바는 네비게이션 바의 기능만을 담당하는 것이 옳다고 생각해 이 부분을 따로 분리하기로 했다.
이를 위해 Context API를 사용하기로 했다.
AuthContext.js
// src/main/frontend/src/context/AuthContext.js
import React, { createContext, useContext, useEffect, useState } from 'react';
import axios from 'axios';
const AuthContext = createContext(undefined);
export const useAuth = () => useContext(AuthContext);
export const AuthProvider = ({ children }) => {
const [userInfo, setUserInfo] = useState(null);
// 새로고침 없이 바로 로그인 정보 반영하기 위해 추가
useEffect(() => {
fetchUserInfo().then(() => {}); // 페이지 로드 시 사용자 정보 가져오기
}, []);
// 사용자 정보를 가져오는 함수
const fetchUserInfo = async () => {
try {
const response = await axios.get('http://localhost:8080/oauth/user-info');
setUserInfo(response.data);
console.log("UserInfo updated: ", response.data); // 디버깅 로그 추가
} catch (error) {
console.error("사용자 정보 조회 중 오류 발생:", error);
}
};
return (
<AuthContext.Provider value={{ userInfo, fetchUserInfo }}>
{children}
</AuthContext.Provider>
);
};
HomePage.js에서도
const { userInfo } = useAuth();
를 호출해 로그인 정보를 받아오도록 수정한다.
HomePage.js
// src/main/frontend/src/pages/HomePage.js
import React, { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import axios from 'axios';
import { useAuth } from '../context/AuthContext';
function HomePage() {
const [hello, setHello] = useState('');
const { userInfo, fetchUserInfo } = useAuth();
const location = useLocation();
const navigate = useNavigate();
console.log("HomePage UserInfo: ", userInfo); // 디버깅 로그 추가
// OAuth 리디렉션 처리
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
const code = searchParams.get('code');
const oauthProvider = location.pathname.split('/').pop(); // URL 경로에서 OAuth 제공자 정보 추출
if (code) {
handleOAuthLogin(oauthProvider, code).then(() => {
fetchUserInfo(); // 로그인 후 사용자 정보 업데이트
});
}
}, [location]);
const handleOAuthLogin = async (oauthProvider, code) => {
try {
const response = await axios.get(`http://localhost:8080/oauth/login/${oauthProvider}?code=${code}`);
const userId = response.data;
alert("로그인 성공: " + userId);
navigate("/home", { replace: true }); // URL을 /home으로 변경
} catch (error) {
console.error("로그인 또는 사용자 정보 조회 중 오류 발생:", error);
alert("로그인 실패");
navigate("/fail", { replace: true });
}
};
// 백엔드의 /api/hello 엔드포인트로부터 데이터를 가져오는 비동기 요청
useEffect(() => {
axios.get('/api/hello')
.then(response => {
setHello(response.data);
})
.catch(error => {
console.log(error);
});
}, []);
return (
<div>
<h1>Welcome to the Task Helper</h1>
<p>백엔드에서 가져온 데이터입니다: {hello}</p>
{userInfo && (
<div>
<h2>사용자 정보</h2>
<p>ID: {userInfo.id}</p>
<p>닉네임: {userInfo.nickname}</p>
</div>
)}
</div>
);
}
export default HomePage;
NavBar.js에서도 마찬가지로
const { userInfo } = useAuth();
를 호출해 로그인 정보를 받아오고 다음과 같이 login 링크를 수정한다.
<li className="navbar-item">
{userInfo ? (
<span className="navbar-link">
<FaUser className="navbar-icon" /> {userInfo.nickname} 님
</span>
) : (
<Link to="/login" className="navbar-link">
<FaUser className="navbar-icon"/> login
</Link>
)}
</li>
로그인 정보가 있으면 "이름 + 님"으로 표기되고 로그인 정보가 없을 때는 login링크가 된다.
App.js에서 AuthProvider로 태그를 감싸 애플리케이션 전체에서 AuthContext를 사용할 수 있도록 한다.
<AuthProvider>
function App() {
return (
<div className="App">
<AuthProvider>
{/* 내부 라우팅 */}
</AuthProvider>
</div>
);
}
위와 같이 로그인 정보로 업데이트 되었다. 하지만 현재 로그아웃이 구현되지 않았기 때문에 세션이 만료되어야만 로그아웃이 된다.
이전에 OauthController.java에서 다음과 같이 로그아웃 엔드포인트도 미리 만들어 두었다.
// 로그아웃 엔드포인트: 세션 무효화
@GetMapping("/logout")
public ResponseEntity<String> logout(HttpSession session) {
session.invalidate(); // 세션 무효화
return ResponseEntity.ok("로그아웃 성공");
}
AuthContext에 로그아웃을 추가한다.
const logout = async () => {
try {
await axios.get('http://localhost:8080/oauth/logout');
setUserInfo(null);
alert("로그아웃되었습니다.");
} catch (error) {
console.error('로그아웃 중 오류 발생:', error);
}
};
return (
<AuthContext.Provider value={{userInfo, fetchUserInfo, logout}}>
{children}
</AuthContext.Provider>
);
반환값에 logout 추가
NavBar에 로그아웃 추가
<li className="navbar-item">
<span className="navbar-link" onClick={logout}>
<RiLogoutBoxRLine className="navbar-icon"/> Logout
</span>
</li>
이렇게 간단하게 로그아웃이 추가되었다.
추가로, 현재 HomePage.js에서는 useAuth로 userInfo를 함께 받아온다.
const { userInfo, fetchUserInfo } = useAuth();
그런데 로그인이 되지 않은 상태에서는 userInfo가 설정되지 않았음에도 불러오기 때문에 401 에러가 발생한다. 따라서 다음과 같이 401에러에 대한 예외를 추가해주었다.
// 사용자 정보를 가져오는 함수
const fetchUserInfo = async () => {
try {
const response = await axios.get('http://localhost:8080/oauth/user-info');
setUserInfo(response.data);
console.log("UserInfo updated: ", response.data); // 디버깅 로그 추가
} catch (error) {
if (error.response && error.response.status === 401) {
// 401 에러일 경우 로그아웃 상태로 처리
console.log("사용자가 로그인되어 있지 않습니다.");
} else {
console.error("사용자 정보 조회 중 오류 발생:", error);
}
setUserInfo(null); // 로그아웃 상태로 설정
}
};
이제 드디어 로그인 기능 구현이 전부 끝났다. 다음 포스트에서는 다시 시간표 기능을 구현하도록 하겠다. 수강신청 D-5.....