Skip to content

Commit e3c3e38

Browse files
authored
Merge pull request #85 from GDGoCINHA/develop
Push to Production
2 parents 98c07b7 + 1820ed2 commit e3c3e38

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+9119
-1369
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ yarn-error.log*
3838
# typescript
3939
*.tsbuildinfo
4040
next-env.d.ts
41+
42+
# ide files
43+
.idea/*

package-lock.json

Lines changed: 4827 additions & 1203 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/ui/pencil.png

7.89 KB
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use client';
2+
import axios from 'axios';
3+
4+
const CUSTOM_AUTH_URL = 'https://gdgocinha.site/auth';
5+
6+
export const login = async (email, password) => {
7+
try {
8+
const response = await axios.post(
9+
`${CUSTOM_AUTH_URL}/login`,
10+
{ email, password },
11+
{
12+
headers: { 'Content-Type': 'application/json' },
13+
withCredentials: true,
14+
}
15+
);
16+
return response;
17+
} catch (error) {
18+
console.warn('네트워크 오류 또는 서버에 연결할 수 없습니다.');
19+
alert('네트워크 오류 또는 서버에 연결할 수 없습니다.');
20+
}
21+
};

src/app/auth/signin/custom/CustomRegister.js

Whitespace-only changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client'
2+
import axios from 'axios';
3+
4+
const GOOGLE_AUTH_URL = 'https://gdgocinha.site/auth/oauth2/google';
5+
6+
// Google 로그인 코드 교환 함수
7+
export const exchangeGoogleToken = async (code) => {
8+
try {
9+
const response = await axios.get(`${GOOGLE_AUTH_URL}/callback?code=${code}`, {
10+
headers: { 'Content-Type': 'application/json' },
11+
withCredentials: true
12+
});
13+
return response;
14+
} catch (error) {
15+
console.error('Google 토큰 교환 중 오류 발생:', error);
16+
throw error;
17+
}
18+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const clientId = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_CLIENT_ID;
2+
const redirectUri = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI;
3+
const googleLoginUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=email profile`;
4+
5+
export const GoogleLogin = () => {
6+
const handleGoogleLogin = () => {
7+
window.location.href = googleLoginUrl;
8+
};
9+
10+
return {handleGoogleLogin};
11+
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use client'
2+
3+
import { useEffect, useState, Suspense } from 'react';
4+
import { useSearchParams, useRouter } from 'next/navigation';
5+
import { exchangeGoogleToken } from '../GoogleAuthApi';
6+
import { useAuth } from '@/hooks/useAuth';
7+
import Loader from '@/components/ui/Loader'
8+
9+
export const GoogleAuthComponent = () => {
10+
const { setAccessToken } = useAuth();
11+
const searchParams = useSearchParams();
12+
const router = useRouter();
13+
const [isLoading, setIsLoading] = useState(true);
14+
useEffect(() => {
15+
const decodedCode = searchParams.get('code');
16+
if (!decodedCode) {
17+
setIsLoading(false);
18+
return;
19+
}
20+
21+
const code = encodeURIComponent(decodedCode);
22+
23+
exchangeGoogleToken(code)
24+
.then((res) => {
25+
const { exists, access_token, email, name } = res.data.data;
26+
if (exists) {
27+
setAccessToken(access_token);
28+
router.push('/main');
29+
} else {
30+
alert('회원 정보가 없습니다. 회원가입을 완료해주세요.');
31+
sessionStorage.setItem('signup_email', email);
32+
sessionStorage.setItem('signup_name', name);
33+
router.push('/signup');
34+
}
35+
})
36+
.catch(() => {
37+
alert('구글 로그인 실패! 다시 시도해주세요.');
38+
router.push('/auth/signin');
39+
})
40+
.finally(() => setIsLoading(false));
41+
}, [searchParams, router, setAccessToken]);
42+
43+
if (isLoading) {
44+
return <Loader isLoading={true} />;
45+
}
46+
return null;
47+
};
48+
49+
export const GoogleAuth = () => (
50+
<Suspense fallback={<Loader isLoading={true} />}>
51+
<GoogleAuthComponent />
52+
</Suspense>
53+
);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use client';
2+
3+
import { GoogleAuth } from './GoogleAuth';
4+
5+
export default function Page() {
6+
return <GoogleAuth />;
7+
}

src/app/auth/signin/page.jsx

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import { useRouter } from 'next/navigation';
5+
import Image from 'next/image';
6+
7+
import loginBg from '@public/src/images/bgimg.png';
8+
import Header from './screen/Header';
9+
import AuthLogin from './screen/AuthLogin';
10+
import AuthFindId from './screen/AuthFindId';
11+
import AuthResetPassword from './screen/AuthResetPassword';
12+
import AuthResetRequest from './screen/AuthResetRequest';
13+
import Loader from '@/components/ui/Loader.jsx';
14+
15+
import { GoogleLogin } from './google/GoogleLogin';
16+
import { login } from './custom/CustomAuthApi';
17+
import { useAuth } from '@/hooks/useAuth';
18+
19+
export default function Page() {
20+
const router = useRouter();
21+
const [password, setPassword] = useState('');
22+
const [errors, setErrors] = useState([]);
23+
const { handleGoogleLogin } = GoogleLogin();
24+
const [isRendering, setIsRendering] = useState(0);
25+
const { setAccessToken } = useAuth();
26+
const [loading, setLoading] = useState(false);
27+
28+
const handleBackToLogin = () => setIsRendering(0);
29+
const handleFindIdClick = () => setIsRendering(1);
30+
const handleResetPasswordClick = () => setIsRendering(2);
31+
const handleBackToResetRequest = () => setIsRendering(2);
32+
const handleResetPasswordNext = () => setIsRendering(3);
33+
34+
const validatePassword = (password) => {
35+
const newErrors = [];
36+
if (password.length <= 0) {
37+
newErrors.push('비밀번호를 입력해주세요.');
38+
}
39+
return newErrors;
40+
};
41+
42+
const onSubmit = async (e) => {
43+
e.preventDefault();
44+
45+
const formData = Object.fromEntries(new FormData(e.currentTarget));
46+
const passwordErrors = validatePassword(password);
47+
48+
if (passwordErrors.length > 0) {
49+
setErrors(passwordErrors);
50+
} else {
51+
setErrors([]);
52+
53+
try {
54+
const { email, password } = formData;
55+
setLoading(true);
56+
const res = await login(email, password);
57+
const { exists, access_token } = res.data.data;
58+
59+
if (!exists) {
60+
alert('아이디 혹은 비밀번호가 올바르지 않습니다.');
61+
setLoading(false);
62+
return;
63+
}
64+
65+
setAccessToken(access_token);
66+
router.push('/main');
67+
} catch (error) {
68+
console.error('로그인 실패:', error);
69+
alert('로그인 중 오류가 발생했습니다.');
70+
setLoading(false);
71+
}
72+
}
73+
};
74+
75+
return (
76+
<div className='min-h-screen flex flex-col overflow-hidden relative'>
77+
<Loader isLoading={loading} />
78+
<Header />
79+
<Image src={loginBg} alt='loginBg' fill className='absolute top-0 left-0 -z-10 object-cover opacity-70 blur-sm' />
80+
<div className='flex justify-center items-center flex-1 relative'>
81+
{/* 로그인 화면 */}
82+
<div
83+
key='screen1'
84+
className={`absolute w-full transition-all duration-500 ease-in-out transform ${
85+
isRendering === 0 ? 'translate-x-0 opacity-100' : '-translate-x-full opacity-0'
86+
} flex justify-center items-center`}
87+
>
88+
<AuthLogin
89+
router={router}
90+
onSubmit={onSubmit}
91+
errors={errors}
92+
password={password}
93+
setPassword={setPassword}
94+
setErrors={setErrors}
95+
handleGoogleLogin={handleGoogleLogin}
96+
handleFindIdClick={handleFindIdClick}
97+
handleResetPasswordClick={handleResetPasswordClick}
98+
/>
99+
</div>
100+
101+
{/* 아이디 찾기 화면 */}
102+
<div
103+
key='screen2'
104+
className={`absolute w-full transition-all duration-500 ease-in-out transform ${
105+
isRendering === 1 ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'
106+
} flex justify-center items-center mt-[-30px]`}
107+
>
108+
<AuthFindId handleBackToLogin={handleBackToLogin} />
109+
</div>
110+
111+
{/* 비밀번호 재설정 화면 1 */}
112+
<div
113+
key='screen3'
114+
className={`absolute w-full transition-all duration-500 ease-in-out transform ${
115+
isRendering === 2
116+
? 'translate-x-0 opacity-100'
117+
: `${isRendering === 3 ? '-translate-x-full' : 'translate-x-full'} opacity-0`
118+
} flex justify-center items-center`}
119+
>
120+
<AuthResetRequest
121+
handleNextStep={handleResetPasswordNext}
122+
handleBackToLogin={handleBackToLogin}
123+
setLoading={setLoading}
124+
/>
125+
</div>
126+
127+
{/* 비밀번호 재설정 화면 2 */}
128+
<div
129+
key='screen4'
130+
className={`absolute w-full transition-all duration-500 ease-in-out transform ${
131+
isRendering === 3 ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'
132+
} flex justify-center items-center`}
133+
>
134+
<AuthResetPassword
135+
handleBackToLogin={handleBackToLogin}
136+
handleBackToResetRequest={handleBackToResetRequest}
137+
setLoading={setLoading}
138+
/>
139+
</div>
140+
</div>
141+
</div>
142+
);
143+
}

0 commit comments

Comments
 (0)