-
Notifications
You must be signed in to change notification settings - Fork 2
[Feat/service/#78] 카카오로그인 #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6f556d8
cecd630
ab44970
87bb014
d5e0fff
6f72f15
3b05903
237b7aa
e55dcb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,39 @@ | ||
| 'use client'; | ||
| import { Middleware } from 'openapi-fetch'; | ||
|
|
||
| import { getAccessToken, setAccessToken } from '@utils'; | ||
| import { getAccessToken, setAccessToken, setName, setRefreshToken } from '@utils'; | ||
| import { postRefreshToken } from '@/apis/controller/auth'; | ||
|
|
||
| const UNPROTECTED_ROUTES = ['/api/v1/auth/admin/login', '/api/v1/auth/oauth/social-login']; | ||
| const UNPROTECTED_ROUTES = ['/api/student/auth/social/login', '/api/common/auth/refresh']; | ||
|
|
||
| const reissueToken = async () => { | ||
| try { | ||
| const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/v1/auth/reissue`, { | ||
| method: 'GET', | ||
| credentials: 'include', | ||
| }); | ||
| let accessToken = getAccessToken(); | ||
|
|
||
| if (!response.ok) throw new Error('Token reissue failed'); | ||
|
|
||
| const data = await response.json(); | ||
| const accessToken = data.data.accessToken; | ||
| setAccessToken(accessToken); | ||
| if (accessToken) { | ||
| return accessToken; | ||
| } catch (error) { | ||
| console.error('Reissue failed:', error); | ||
| } | ||
|
|
||
| const result = await postRefreshToken(); | ||
|
|
||
| if (!result.isSuccess || !result.data) { | ||
| console.error('액세스토큰 갱신 실패:', result.error); | ||
|
||
| localStorage.removeItem('accessToken'); | ||
| localStorage.removeItem('refreshToken'); | ||
| window.location.href = '/login'; | ||
| return null; | ||
| } | ||
|
|
||
| if (result.data?.token.accessToken) { | ||
| setAccessToken(result.data.token.accessToken); | ||
| accessToken = result.data.token.accessToken; | ||
| } | ||
| if (result.data?.token.refreshToken) { | ||
| setRefreshToken(result.data.token.refreshToken); | ||
| } | ||
| if (result.data?.name) { | ||
| setName(result.data.name); | ||
| } | ||
| return accessToken; | ||
| }; | ||
|
|
||
| const authMiddleware: Middleware = { | ||
|
|
@@ -32,18 +42,12 @@ const authMiddleware: Middleware = { | |
| return undefined; | ||
| } | ||
|
|
||
| let accessToken = getAccessToken(); | ||
| const accessToken = await reissueToken(); | ||
|
|
||
| if (!accessToken) { | ||
| accessToken = await reissueToken(); | ||
|
|
||
| if (!accessToken) { | ||
| console.error('Access token reissue failed. Logging out...'); | ||
| return request; | ||
| } | ||
| if (accessToken) { | ||
| request.headers.set('Authorization', `Bearer ${accessToken}`); | ||
| } | ||
|
|
||
| request.headers.set('Authorization', `Bearer ${accessToken}`); | ||
| return request; | ||
| }, | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import postLogin from './postLogin'; | ||
| import postKakaoLogin from './postKakaoLogin'; | ||
| import postUserInfo from './postUserInfo'; | ||
| import postRefreshToken from './postRefreshToken'; | ||
|
|
||
| export { postLogin, postKakaoLogin }; | ||
| export { postKakaoLogin, postUserInfo, postRefreshToken }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,54 +1,24 @@ | ||
| 'use client'; | ||
|
|
||
| import { setAccessToken, setName } from '@utils'; | ||
| import { client } from '@/apis/client'; | ||
|
|
||
| const postKakaoAccessToken = async (code: string) => { | ||
| const response = await fetch(`https://kauth.kakao.com/oauth/token`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', | ||
| }, | ||
| body: `grant_type=authorization_code&client_id=${process.env.NEXT_PUBLIC_REST_API_KEY}&redirect_uri=${process.env.NEXT_PUBLIC_REDIRECT_URI}&code=${code}`, | ||
| }); | ||
| const jsonData = await response.json(); | ||
| return jsonData.access_token; | ||
| }; | ||
|
|
||
| const postKakaoLogin = async (code: string) => { | ||
| const accessToken = await postKakaoAccessToken(code); | ||
| const response = await client.POST('/api/v1/auth/oauth/social-login', { | ||
| params: { | ||
| header: { | ||
| social_access_token: accessToken, | ||
| }, | ||
| query: { | ||
| provider: 'KAKAO', | ||
| }, | ||
| const postKakaoLogin = async () => { | ||
| const response = await client.POST('/api/student/auth/login/social', { | ||
| body: { | ||
| provider: 'KAKAO', | ||
| redirectUri: process.env.NEXT_PUBLIC_REDIRECT_URI ?? '', | ||
| }, | ||
| }); | ||
|
|
||
| try { | ||
| if ( | ||
| response && | ||
| response.data && | ||
| response.data.data && | ||
| response.data.data.name && | ||
| response.data.data.accessToken | ||
| ) { | ||
| const { accessToken, name } = response.data.data; | ||
| setAccessToken(accessToken); | ||
| setName(name); | ||
|
|
||
| window.location.href = '/'; | ||
| if (response && response.data) { | ||
| return { isSuccess: true, loginUrl: response.data.loginUrl }; | ||
| } else { | ||
| console.error('accessToken을 찾을 수 없습니다:', response); | ||
| return { isSuccess: false, error: '데이터를 찾을 수 없습니다.' }; | ||
| } | ||
| } catch (error) { | ||
| console.error('소셜 로그인 요청 오류:', error); | ||
| return { isSuccess: false, error: error }; | ||
| } | ||
|
|
||
| return response; | ||
| }; | ||
|
|
||
| export default postKakaoLogin; |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| 'use client'; | ||
|
|
||
| const postRefreshToken = async () => { | ||
| try { | ||
| const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/common/auth/refresh`, { | ||
| method: 'GET', | ||
| }); | ||
| if (!res.ok) throw new Error('Refresh failed'); | ||
|
|
||
| const data = await res.json(); | ||
| return { isSuccess: true, data }; | ||
| } catch (e) { | ||
| return { isSuccess: false, error: e }; | ||
| } | ||
| }; | ||
|
|
||
| export default postRefreshToken; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { client } from '@apis'; | ||
|
|
||
| const postUserInfo = async (name: string, grade: number) => { | ||
| try { | ||
| const response = await client.POST('/api/student/auth/register/social', { | ||
| body: { | ||
| name: name, | ||
| grade: grade, | ||
| }, | ||
| }); | ||
| return { isSuccess: true, data: response.data }; | ||
| } catch (error) { | ||
| return { isSuccess: false, error: error }; | ||
| } | ||
| }; | ||
|
|
||
| export default postUserInfo; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,18 +3,21 @@ import { useEffect } from 'react'; | |
| import { useRouter } from 'next/navigation'; | ||
|
|
||
| import { getAccessToken, trackEvent } from '@utils'; | ||
| import { postKakaoLogin } from '@apis'; | ||
| import { LogoLogin } from '@/assets/svg/logo'; | ||
| import { KakaoButton } from '@/components/login'; | ||
|
|
||
| const Page = () => { | ||
| const router = useRouter(); | ||
| const kakaoLoginUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${ | ||
| process.env.NEXT_PUBLIC_REST_API_KEY | ||
| }&redirect_uri=${process.env.NEXT_PUBLIC_REDIRECT_URI}&response_type=code`; | ||
|
|
||
| const handleLoginClick = () => { | ||
| const handleLoginClick = async () => { | ||
| trackEvent('kakao_login_click'); | ||
| window.location.replace(kakaoLoginUrl); | ||
| const result = await postKakaoLogin(); | ||
| if (result.isSuccess && result.loginUrl) { | ||
| router.push(result.loginUrl); | ||
| } else { | ||
| console.error('로그인 URL을 가져오는 데 실패했습니다.'); | ||
|
||
| } | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,75 @@ | ||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import { useForm, FormProvider } from 'react-hook-form'; | ||||||||||||||||||||||
| import { useRouter } from 'next/navigation'; | ||||||||||||||||||||||
| import { useEffect, useState } from 'react'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import { Button, Header } from '@components'; | ||||||||||||||||||||||
| import { postUserInfo } from '@/apis/controller/auth'; | ||||||||||||||||||||||
| import { setName } from '@utils'; | ||||||||||||||||||||||
| import UserInfoForm from '@/components/onboarding/UserInfoForm'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const Page = () => { | ||||||||||||||||||||||
| type FormValues = { name: string; grade: string }; | ||||||||||||||||||||||
| const router = useRouter(); | ||||||||||||||||||||||
| const [isFormFilled, setIsFormFilled] = useState(false); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const methods = useForm<FormValues>({ | ||||||||||||||||||||||
|
Comment on lines
+13
to
+17
|
||||||||||||||||||||||
| type FormValues = { name: string; grade: string }; | |
| const router = useRouter(); | |
| const [isFormFilled, setIsFormFilled] = useState(false); | |
| const methods = useForm<FormValues>({ | |
| import { UserInfoFormData } from '@/types'; | |
| const router = useRouter(); | |
| const [isFormFilled, setIsFormFilled] = useState(false); | |
| const methods = useForm<UserInfoFormData>({ |
Copilot
AI
Jul 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.error should be removed as indicated in the PR checklist. Consider using proper error handling or logging service instead.
Copilot
AI
Jul 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including the entire 'methods' object in the dependency array will cause unnecessary re-renders. Only include 'getFieldState' which is the specific method being used.
| }, [formState, methods]); | |
| }, [formState, getFieldState]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The route '/api/student/auth/social/login' doesn't match the actual API endpoint '/api/student/auth/login/social' used in postKakaoLogin.ts. This could cause authentication issues.