Skip to content

Commit 62109a1

Browse files
committed
Merge branch 'feature/terms-upadte' into develop
2 parents 2bc349c + 5cf6630 commit 62109a1

8 files changed

Lines changed: 349 additions & 508 deletions

File tree

nginx/react-frontpage/src/App.tsx

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React from 'react';
2-
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
1+
import React, { useEffect, useRef } from 'react';
2+
import { BrowserRouter as Router, Routes, Route, useLocation, useNavigate } from 'react-router-dom';
33
import Home from './Pages/Home';
44
import SideBar from './Component/SideBar/SideBar';
55
import Register from './Component/Register/Register';
@@ -10,32 +10,71 @@ import CharacterChatRoom from './Pages/CharacterChatRoom';
1010
import CharacterAdd from './Component/CharacterMain/CharacterAdd';
1111
import PrivacyConsent from './Component/Register/PrivacyConsentProps';
1212
import MainPage from './Pages/MainPage';
13-
import LoginMain from './Component/Login/LoginMain';
13+
14+
// JWT 토큰 체크용 커스텀 훅
15+
const useAuthRedirect = () => {
16+
const location = useLocation();
17+
const navigate = useNavigate();
18+
const redirected = useRef(false);
19+
20+
useEffect(() => {
21+
// 인증이 필요 없는 경로
22+
const publicPaths = ['/', '/login', '/register', '/privacy', '/loginMain'];
23+
const isPublic = publicPaths.some(path => location.pathname === path || location.pathname.startsWith(path + '/'));
24+
25+
// 쿠키에서 jwt-token 확인
26+
const getCookie = (name: string) => {
27+
const value = `; ${document.cookie}`;
28+
const parts = value.split(`; ${name}=`);
29+
if (parts.length === 2) return parts.pop()?.split(';').shift();
30+
};
31+
32+
const jwtToken = getCookie('jwt-token');
33+
34+
if (!jwtToken && !isPublic && !redirected.current) {
35+
redirected.current = true;
36+
alert('로그인 후 이용해주세요');
37+
navigate('/', { replace: true });
38+
} else if (jwtToken || isPublic) {
39+
redirected.current = false;
40+
}
41+
}, [location, navigate]);
42+
};
43+
44+
// Router 내부에서만 훅 사용
45+
const AppRoutes: React.FC = () => {
46+
useAuthRedirect();
47+
48+
return (
49+
<div className='flex h-screen'>
50+
<div className=" h-full">
51+
<SideBar />
52+
</div>
53+
<div className="flex-1 h-screen overflow-y-auto flex flex-col items-center bg-[#1a1918]">
54+
<Routes>
55+
<Route path="/home" element={<Home />} />
56+
<Route path="/CharacterChat" element={<CharacterChat />} />
57+
<Route path="/chat/:uuid" element={<CharacterChatRoom />} />
58+
<Route path="/profile" element={<Profile />} />
59+
<Route path='/characterAdd' element={<CharacterAdd />} />
60+
<Route path="/privacy" element={<PrivacyConsent />} />
61+
{/* MainPage에서 로그인/회원가입을 오른쪽에 띄움 */}
62+
<Route path="/" element={<MainPage />}>
63+
<Route index element={<Login />} /> {/* / 경로에서 Login */}
64+
<Route path="loginMain" element={<Login />} />
65+
<Route path="/register" element={<Register />} /> {/* 회원가입 단독 페이지 */}
66+
67+
</Route>
68+
</Routes>
69+
</div>
70+
</div>
71+
);
72+
};
1473

1574
const App: React.FC = () => {
1675
return (
1776
<Router>
18-
<div className='flex h-screen'>
19-
<div className=" h-full">
20-
<SideBar />
21-
</div>
22-
<div className="flex-1 h-screen overflow-y-auto flex flex-col items-center bg-[#1a1918]">
23-
<Routes>
24-
<Route path="/home" element={<Home />} />
25-
<Route path="/CharacterChat" element={<CharacterChat />} />
26-
<Route path="/chat/:uuid" element={<CharacterChatRoom />} />
27-
<Route path="/profile" element={<Profile />} />
28-
<Route path='/characterAdd' element={<CharacterAdd />} />
29-
<Route path="/privacy" element={<PrivacyConsent />} />
30-
{/* MainPage에서 로그인/회원가입을 오른쪽에 띄움 */}
31-
<Route path="/" element={<MainPage />}>
32-
<Route index element={<LoginMain />} /> {/* / 경로에서 Login */}
33-
<Route path="loginMain" element={<LoginMain />} />
34-
<Route path="register" element={<Register />} />
35-
</Route>
36-
</Routes>
37-
</div>
38-
</div>
77+
<AppRoutes />
3978
</Router>
4079
);
4180
};

nginx/react-frontpage/src/Component/Login/Login.tsx

Lines changed: 87 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import logo_kakao_kr from './logo/logo_kakao_kr.png';
55
import logo_google_kr from './logo/logo_google_kr.png';
66
import { useGoogleLogin } from '@react-oauth/google';
77
import { setCookie, getCookie, removeCookie, setObjectCookie } from '../../Cookies';
8+
import { useNavigate } from 'react-router-dom'; // 추가
89

910
// 인앱 브라우저 감지 함수
1011
const isInAppBrowser = () => {
@@ -41,24 +42,9 @@ const handleGoogleSocialLogin = async (code: string, setError: (msg: string) =>
4142
}
4243
);
4344
const data = await serverResponse.json();
44-
const { token, user_id, user_email, user_name } = data;
45+
const { token } = data;
4546

4647
setCookie('jwt-token', token);
47-
setCookie('user_id', user_id);
48-
setCookie('user_email', user_email);
49-
setCookie('user_name', user_name);
50-
51-
setObjectCookie('user_info', {
52-
id: user_id,
53-
name: user_name,
54-
email: user_email,
55-
picture: ''
56-
});
57-
58-
// 이전 사용자와 다르면 채팅방 초기화
59-
const previousUserId = getCookie('previous_user_id') || '';
60-
if (previousUserId !== user_id) removeCookie('mongo_chatroomid');
61-
setCookie('previous_user_id', user_id);
6248

6349
return true;
6450
} catch (error: any) {
@@ -86,6 +72,15 @@ const Login: React.FC = () => {
8672
const [error, setError] = useState('');
8773
const [success, setSuccess] = useState(false);
8874
const [isLoading, setIsLoading] = useState(false);
75+
const navigate = useNavigate(); // 추가
76+
77+
// jwt-token이 있으면 /home으로 이동
78+
useEffect(() => {
79+
const token = getCookie('jwt-token');
80+
if (token) {
81+
navigate('/home', { replace: true });
82+
}
83+
}, [navigate]);
8984

9085
const googleLogin = useGoogleLogin({
9186
flow: 'auth-code',
@@ -161,15 +156,8 @@ const Login: React.FC = () => {
161156
const response = await axios.post('/server/user/login', { id: Id, pw: password });
162157
if (response.status === 200) {
163158
setSuccess(true);
164-
const { token, name } = response.data;
159+
const { token } = response.data;
165160
setCookie('jwt-token', token);
166-
setCookie('user_id', Id);
167-
setCookie('user_name', name);
168-
169-
// chatlog_agree와 user_setting_agree를 true로 저장
170-
setCookie('chatlog_agree', 'true');
171-
setCookie('user_setting_agree', 'true');
172-
173161
window.location.href = '/';
174162
} else {
175163
setError('로그인 실패. 다시 시도해 주세요.');
@@ -182,76 +170,81 @@ const Login: React.FC = () => {
182170
};
183171

184172
return (
185-
<div className="flex flex-col items-center justify-center h-screen w-[90vw]">
186-
<form className="bg-white p-12 rounded-md text-left w-full max-w-sm" onSubmit={handleSubmit}>
187-
<div className="flex justify-center items-center">
188-
<h2 className="text-green-600 text-2xl mb-2 whitespace-nowrap">TreeNut</h2>
189-
</div>
190-
<div className="flex justify-center items-center">
191-
<h2 className="text-lg mb-3 whitespace-nowrap">AI 어시스턴트한테 도움을 받아보세요!</h2>
192-
</div>
193-
<h2 className="text-center text-sm mb-6 text-gray-600">
194-
TreeNut과 함께 편리한 AI 서비스를 이용해보세요
195-
</h2>
196-
<div className="flex flex-col items-center mb-5">
197-
<img
198-
src={logo_naver_kr}
199-
alt="Naver Logo"
200-
className="w-72 mb-4 transition-transform transform hover:translate-y-[-5px] cursor-pointer"
201-
onClick={handleNaverLogin}
202-
/>
203-
<img
204-
src={logo_kakao_kr}
205-
alt="Kakao Logo"
206-
className="w-72 mb-4 transition-transform transform hover:translate-y-[-5px] cursor-pointer"
207-
onClick={handleKakaoLogin}
208-
/>
209-
<img
210-
src={logo_google_kr}
211-
alt="Google Logo"
212-
className="w-72 mb-4 transition-transform transform hover:translate-y-[-5px] cursor-pointer"
213-
onClick={handleGoogleLogin}
214-
onTouchStart={handleGoogleLogin}
215-
/>
216-
</div>
217-
<div className="flex items-center mb-6">
218-
<div className="flex-1 h-[1px] bg-gray-300"></div>
219-
<span className="mx-4 text-gray-600 text-sm">또는</span>
220-
<div className="flex-1 h-[1px] bg-gray-300"></div>
221-
</div>
222-
<div className="mb-4">
223-
<input
224-
type="text"
225-
id="Id"
226-
value={Id}
227-
onChange={(e) => setId(e.target.value)}
228-
required
229-
className="w-full p-2 border text-gray-700 border-gray-300 rounded-md opacity-70 focus:outline-none focus:ring-2 focus:ring-green-500"
230-
placeholder="아이디"
231-
/>
232-
</div>
233-
<div className="mb-4">
234-
<input
235-
type="password"
236-
id="password"
237-
value={password}
238-
onChange={(e) => setPassword(e.target.value)}
239-
required
240-
className="w-full p-2 border text-gray-700 border-gray-300 rounded-md opacity-70 focus:outline-none focus:ring-2 focus:ring-green-500"
241-
placeholder="비밀번호"
242-
/>
243-
</div>
244-
<button
245-
type="submit"
246-
disabled={isLoading}
247-
className="w-full font-semibold tracking-wider py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition disabled:bg-green-400"
248-
>
249-
{isLoading ? '로그인 중...' : '로그인'}
250-
</button>
251-
</form>
252-
{error && <p className="text-red-600 mt-4 text-sm">{error}</p>}
253-
{success && <p className="text-green-600 mt-4 text-sm">로그인 성공!</p>}
254-
</div>
173+
<form className="bg-white p-8 rounded-lg text-left w-full max-w-md shadow-lg" onSubmit={handleSubmit}>
174+
<div className="flex justify-center items-center mb-1">
175+
<h2 className="text-green-600 text-2xl whitespace-nowrap">TreeNut</h2>
176+
</div>
177+
<div className="flex justify-center items-center mb-1">
178+
<h2 className="text-lg whitespace-nowrap">AI 어시스턴트한테 도움을 받아보세요!</h2>
179+
</div>
180+
<h2 className="text-center text-sm mb-3 text-gray-600">
181+
TreeNut과 함께 편리한 AI 서비스를 이용해보세요
182+
</h2>
183+
<div className="flex flex-col items-center mb-3">
184+
<img
185+
src={logo_naver_kr}
186+
alt="Naver Logo"
187+
className="w-25 mb-2 transition-transform transform hover:translate-y-[-5px] cursor-pointer"
188+
onClick={handleNaverLogin}
189+
/>
190+
<img
191+
src={logo_kakao_kr}
192+
alt="Kakao Logo"
193+
className="w-25 mb-2 transition-transform transform hover:translate-y-[-5px] cursor-pointer"
194+
onClick={handleKakaoLogin}
195+
/>
196+
<img
197+
src={logo_google_kr}
198+
alt="Google Logo"
199+
className="w-25 mb-2 transition-transform transform hover:translate-y-[-5px] cursor-pointer"
200+
onClick={handleGoogleLogin}
201+
onTouchStart={handleGoogleLogin}
202+
/>
203+
</div>
204+
<div className="flex items-center mb-4">
205+
<div className="flex-1 h-[1px] bg-gray-300"></div>
206+
<span className="mx-2 text-gray-600 text-sm">또는</span>
207+
<div className="flex-1 h-[1px] bg-gray-300"></div>
208+
</div>
209+
<div className="mb-2">
210+
<input
211+
type="text"
212+
id="Id"
213+
value={Id}
214+
onChange={(e) => setId(e.target.value)}
215+
required
216+
className="w-full p-2 border text-gray-700 border-gray-300 rounded-md opacity-70 focus:outline-none focus:ring-2 focus:ring-green-500"
217+
placeholder="아이디"
218+
/>
219+
</div>
220+
<div className="mb-2">
221+
<input
222+
type="password"
223+
id="password"
224+
value={password}
225+
onChange={(e) => setPassword(e.target.value)}
226+
required
227+
className="w-full p-2 border text-gray-700 border-gray-300 rounded-md opacity-70 focus:outline-none focus:ring-2 focus:ring-green-500"
228+
placeholder="비밀번호"
229+
/>
230+
</div>
231+
<button
232+
type="submit"
233+
disabled={isLoading}
234+
className="w-full font-semibold tracking-wider py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition disabled:bg-green-400"
235+
>
236+
{isLoading ? '로그인 중...' : '로그인'}
237+
</button>
238+
<button
239+
type="button"
240+
onClick={() => navigate('/register')}
241+
className="w-full mt-2 font-semibold tracking-wider py-2 bg-white text-green-600 border border-green-600 rounded-md hover:bg-green-50 transition"
242+
>
243+
회원가입
244+
</button>
245+
{error && <p className="text-red-600 mt-3 text-sm">{error}</p>}
246+
{success && <p className="text-green-600 mt-3 text-sm">로그인 성공!</p>}
247+
</form>
255248
);
256249
};
257250

0 commit comments

Comments
 (0)