Skip to content

Commit 18405c4

Browse files
committed
로그인 후 자동 리디렉션
1 parent 438f24e commit 18405c4

5 files changed

Lines changed: 114 additions & 28 deletions

File tree

src/App.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HashRouter, Routes, Route, useLocation, Navigate } from "react-router-dom";
1+
import { HashRouter, Routes, Route, useLocation, Navigate, useNavigate } from "react-router-dom";
22
import { useEffect, useState } from "react";
33

44
import Header from "./components/header/Header";
@@ -23,6 +23,7 @@ import CodecastLive from "./components/codecast/codecastlive/CodecastLive";
2323

2424
function AppContent() {
2525
const location = useLocation();
26+
const navigate = useNavigate();
2627
const [isDark, setIsDark] = useState(false);
2728
const [isLoggedIn, setIsLoggedIn] = useState(false);
2829
const [nickname, setNickname] = useState('');
@@ -44,15 +45,26 @@ function AppContent() {
4445
);
4546

4647
useEffect(() => {
47-
const token = localStorage.getItem('token');
48-
const storedUsername = localStorage.getItem('username');
49-
if (token && storedUsername) {
50-
setIsLoggedIn(true);
51-
setNickname(storedUsername);
52-
} else {
53-
setIsLoggedIn(false);
54-
setNickname('');
55-
}
48+
const syncAuthState = () => {
49+
const token = localStorage.getItem('token');
50+
const storedUsername = localStorage.getItem('username');
51+
if (token && storedUsername) {
52+
setIsLoggedIn(true);
53+
setNickname(storedUsername);
54+
} else {
55+
setIsLoggedIn(false);
56+
setNickname('');
57+
}
58+
};
59+
60+
syncAuthState();
61+
window.addEventListener('storage', syncAuthState);
62+
window.addEventListener('dv:auth-updated', syncAuthState);
63+
64+
return () => {
65+
window.removeEventListener('storage', syncAuthState);
66+
window.removeEventListener('dv:auth-updated', syncAuthState);
67+
};
5668
}, []);
5769

5870
useEffect(() => {
@@ -115,6 +127,13 @@ function AppContent() {
115127
onLoginSuccess={() => {
116128
setIsLoggedIn(true);
117129
setNickname(localStorage.getItem('username') || '');
130+
setIsLoginModalOpen(false);
131+
132+
const redirectTarget = sessionStorage.getItem('dv:postLoginRedirect');
133+
if (redirectTarget) {
134+
sessionStorage.removeItem('dv:postLoginRedirect');
135+
navigate(redirectTarget, { replace: true });
136+
}
118137
}}
119138
/>
120139
)}

src/components/community/CommunityWrite.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ export default function CommunityWrite() {
8787
const token = localStorage.getItem("token");
8888
if (!token && !loginPromptedRef.current) {
8989
loginPromptedRef.current = true;
90-
promptLogin();
90+
const redirectPath = `${location.pathname}${location.search || ""}`;
91+
promptLogin(undefined, { redirectTo: redirectPath });
9192
navigate("/community", { replace: true, state: { from: location.pathname } });
9293
}
93-
}, [navigate, location.pathname]);
94+
}, [navigate, location.pathname, location.search]);
9495

9596
const handleFocus = () => {
9697
if (content === defaultGuide) setContent("");
@@ -102,7 +103,8 @@ export default function CommunityWrite() {
102103
const handleSubmit = async () => {
103104
const token = localStorage.getItem("token");
104105
if (!token) {
105-
promptLogin();
106+
const redirectPath = `${location.pathname}${location.search || ""}`;
107+
promptLogin(undefined, { redirectTo: redirectPath });
106108
return;
107109
}
108110

src/components/community/PostDetail.jsx

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// src/pages/PostDetail.jsx
22
import React, { useEffect, useState, useMemo, useRef, useCallback } from "react";
3-
import { useParams, useNavigate } from "react-router-dom";
3+
import { useParams, useNavigate, useLocation } from "react-router-dom";
44
import "./PostDetail.css";
55
import config from "../../config";
66
import { promptLogin } from "../../utils/auth";
@@ -29,6 +29,14 @@ const deriveCommentCount = (resp, data) => {
2929
export default function PostDetail() {
3030
const { id } = useParams(); // /community/post/:id
3131
const navigate = useNavigate();
32+
const location = useLocation();
33+
34+
const [authState, setAuthState] = useState(() => ({
35+
token: localStorage.getItem("token"),
36+
userId: localStorage.getItem("userId"),
37+
username: localStorage.getItem("username"),
38+
role: localStorage.getItem("role"),
39+
}));
3240

3341
const [post, setPost] = useState(null);
3442
const [comments, setComments] = useState([]);
@@ -56,13 +64,15 @@ export default function PostDetail() {
5664
const [replyContent, setReplyContent] = useState("");
5765

5866
// 토큰 및 인증 헤더 (백틱 사용 수정 반영)
59-
const tokenRaw = useMemo(() => localStorage.getItem("token"), []);
60-
const authHeader = tokenRaw
61-
? tokenRaw.startsWith("Bearer ") ? tokenRaw : `Bearer ${tokenRaw}`
62-
: null;
63-
const currentUserId = useMemo(() => localStorage.getItem("userId") || "", []);
64-
const currentUsername = useMemo(() => localStorage.getItem("username") || "", []);
65-
const currentRole = useMemo(() => (localStorage.getItem("role") || "").toUpperCase(), []);
67+
const authHeader = useMemo(() => {
68+
const token = authState.token;
69+
if (!token) return null;
70+
return token.startsWith("Bearer ") ? token : `Bearer ${token}`;
71+
}, [authState.token]);
72+
73+
const currentUserId = useMemo(() => authState.userId || "", [authState.userId]);
74+
const currentUsername = useMemo(() => authState.username || "", [authState.username]);
75+
const currentRole = useMemo(() => (authState.role || "").toUpperCase(), [authState.role]);
6676
const hasManageRole = useMemo(() => ["ADMIN", "MANAGER", "ROLE_ADMIN", "ROLE_MANAGER"].includes(currentRole), [currentRole]);
6777
const matchesCurrentUser = useCallback((writerName, writerId) => {
6878
if (writerId && currentUserId) return String(writerId) === String(currentUserId);
@@ -74,6 +84,41 @@ export default function PostDetail() {
7484
return matchesCurrentUser(writerName, writerId);
7585
}, [hasManageRole, matchesCurrentUser]);
7686

87+
useEffect(() => {
88+
const syncAuth = () => {
89+
setAuthState((prev) => {
90+
const next = {
91+
token: localStorage.getItem("token"),
92+
userId: localStorage.getItem("userId"),
93+
username: localStorage.getItem("username"),
94+
role: localStorage.getItem("role"),
95+
};
96+
if (
97+
prev.token === next.token &&
98+
prev.userId === next.userId &&
99+
prev.username === next.username &&
100+
prev.role === next.role
101+
) {
102+
return prev;
103+
}
104+
return next;
105+
});
106+
};
107+
108+
window.addEventListener("storage", syncAuth);
109+
window.addEventListener("dv:auth-updated", syncAuth);
110+
111+
return () => {
112+
window.removeEventListener("storage", syncAuth);
113+
window.removeEventListener("dv:auth-updated", syncAuth);
114+
};
115+
}, []);
116+
117+
const redirectPath = useMemo(() => `${location.pathname}${location.search || ""}`, [location.pathname, location.search]);
118+
const requestLogin = useCallback(() => {
119+
promptLogin(undefined, { redirectTo: redirectPath });
120+
}, [redirectPath]);
121+
77122
// 좋아요 수 및 내 상태 재조회
78123
const refreshLikeStatus = async () => {
79124
try {
@@ -209,7 +254,7 @@ export default function PostDetail() {
209254
// 좋아요 토글
210255
const handleToggleLike = async () => {
211256
if (!authHeader) {
212-
promptLogin();
257+
requestLogin();
213258
return;
214259
}
215260
if (liking) return;
@@ -263,7 +308,7 @@ export default function PostDetail() {
263308
const handleCreateComment = async () => {
264309
if (!newComment.trim()) return;
265310
if (!authHeader) {
266-
promptLogin();
311+
requestLogin();
267312
return;
268313
}
269314

@@ -300,7 +345,7 @@ export default function PostDetail() {
300345
const handleCreateReply = async (parentId) => {
301346
if (!replyContent.trim()) return;
302347
if (!authHeader) {
303-
promptLogin();
348+
requestLogin();
304349
return;
305350
}
306351

@@ -335,7 +380,7 @@ export default function PostDetail() {
335380

336381
const handleDeletePost = async () => {
337382
if (!authHeader) {
338-
promptLogin();
383+
requestLogin();
339384
return;
340385
}
341386
if (deletingPost) return;
@@ -364,7 +409,7 @@ export default function PostDetail() {
364409

365410
const handleDeleteComment = async (commentId) => {
366411
if (!authHeader) {
367-
promptLogin();
412+
requestLogin();
368413
return;
369414
}
370415
if (!commentId || deletingCommentId === commentId) return;

src/components/login/Login.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ function Login({ onClose, onLoginSuccess }) {
3838
localStorage.setItem('userId', userId);
3939
localStorage.setItem('role', role);
4040

41+
window.dispatchEvent(new Event('dv:auth-updated'));
42+
4143
onLoginSuccess();
4244
onClose();
4345
} catch (error) {

src/utils/auth.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1-
export function promptLogin(message = "로그인이 필요합니다.") {
2-
alert(message);
1+
export function promptLogin(message = "로그인이 필요합니다.", options = {}) {
2+
let resolvedMessage = message;
3+
let resolvedOptions = options;
4+
5+
if (typeof message === "object" && message !== null) {
6+
resolvedOptions = message;
7+
resolvedMessage = message.message || "로그인이 필요합니다.";
8+
}
9+
10+
const { redirectTo } = resolvedOptions || {};
11+
if (redirectTo) {
12+
try {
13+
sessionStorage.setItem("dv:postLoginRedirect", redirectTo);
14+
} catch (error) {
15+
console.warn("Failed to persist post-login redirect path", error);
16+
}
17+
}
18+
19+
alert(resolvedMessage);
320
window.dispatchEvent(new CustomEvent("dv:open-login-modal"));
21+
window.dispatchEvent(new Event("dv:login-requested"));
422
}

0 commit comments

Comments
 (0)