From 170fce7b4b236a354624218f58a8c780df3ced1c Mon Sep 17 00:00:00 2001 From: ChoiWonkeun Date: Sun, 30 Nov 2025 22:46:33 +0900 Subject: [PATCH] Front: implement OAuth social login integration --- .env | 1 + src/features/auth/GithubCallback.jsx | 18 ++++++++---- src/features/home/Home.jsx | 41 ++++++++++++++++++++------ src/features/review/CodeReview.jsx | 44 ++++++++++++++++++++++------ 4 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..0583bfc --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://localhost:8080 \ No newline at end of file diff --git a/src/features/auth/GithubCallback.jsx b/src/features/auth/GithubCallback.jsx index 3ba758a..36c5c96 100644 --- a/src/features/auth/GithubCallback.jsx +++ b/src/features/auth/GithubCallback.jsx @@ -8,13 +8,21 @@ export default function GithubCallback() { useEffect(() => { const params = new URLSearchParams(location.search); - const token = params.get("token"); // ?token=... 이라고 온다고 가정 - if (token) { - localStorage.setItem("accessToken", token); - navigate("/", { replace: true }); + const accessToken = params.get("accessToken"); + const refreshToken = params.get("refreshToken"); + const email = params.get("email"); + const username = params.get("username"); + + if (accessToken && refreshToken) { + localStorage.setItem("accessToken", accessToken); + localStorage.setItem("refreshToken", refreshToken); + if (email) localStorage.setItem("userEmail", email); + if (username) localStorage.setItem("username", username); + + navigate("/", { replace: true }); // 로그인 후 홈으로 } else { - navigate("/login", { replace: true }); + navigate("/login", { replace: true }); // 실패 시 로그인 페이지로 } }, [location, navigate]); diff --git a/src/features/home/Home.jsx b/src/features/home/Home.jsx index b0364df..b336b03 100644 --- a/src/features/home/Home.jsx +++ b/src/features/home/Home.jsx @@ -17,16 +17,39 @@ export default function Home() { if (!init) return null; + const username = localStorage.getItem("username"); + const hasToken = !!localStorage.getItem("accessToken"); + return (
+ + {/* --- 상단 헤더 (로그인 / 사용자명 / 로그아웃) --- */}
- - Login - + {hasToken ? ( +
+ + {username || "User"} + + +
+ ) : ( + + Login + + )}
+ - {/* --- 헤더 --- */} + {/* --- 브랜드 타이틀 --- */}

- {/* --- 메인 콘텐츠 --- */} + {/* --- 메인 메뉴 (3개 버튼) --- */}
{items.map(({ title, href, icon }) => { - const Icon = icon; // 🔥 ESLint가 확실히 인식하게 명시적으로 지정 + const Icon = icon; return ( ""); + throw new Error( + `코드 리뷰 요청 실패 (status: ${response.status}) ${text || ""}`.trim() + ); + } + + const data = await response.json(); const reviewText = typeof data?.review === "string" @@ -102,12 +126,14 @@ export default function Review() { ? data : ""; - if (!reviewText) throw new Error("AI 응답이 비어 있습니다."); + if (!reviewText) { + throw new Error("AI 응답이 비어 있습니다."); + } setReview(reviewText); setQuestions(Array.isArray(data.questions) ? data.questions : []); } catch (err) { - setError(err.message); + setError(err.message || "코드 리뷰 요청 중 오류가 발생했습니다."); } finally { setIsLoading(false); }