From ac47b7e1fd2882505cdb68e81afb527321aea27a Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Sun, 30 Mar 2025 19:32:53 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat(service):=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/api/auth/callback/kakao/page.tsx | 5 ++ apps/service/src/app/login/page.tsx | 70 ++++--------------- .../src/components/login/KakaoButton.tsx | 10 ++- 3 files changed, 26 insertions(+), 59 deletions(-) create mode 100644 apps/service/src/app/api/auth/callback/kakao/page.tsx diff --git a/apps/service/src/app/api/auth/callback/kakao/page.tsx b/apps/service/src/app/api/auth/callback/kakao/page.tsx new file mode 100644 index 00000000..668b304a --- /dev/null +++ b/apps/service/src/app/api/auth/callback/kakao/page.tsx @@ -0,0 +1,5 @@ +const Page = () => { + return
Page
; +}; + +export default Page; diff --git a/apps/service/src/app/login/page.tsx b/apps/service/src/app/login/page.tsx index 0ac3336f..23f070e1 100644 --- a/apps/service/src/app/login/page.tsx +++ b/apps/service/src/app/login/page.tsx @@ -1,33 +1,19 @@ 'use client'; -import { postLogin } from '@apis'; -import { Button, Input } from '@components'; -import { SubmitHandler, useForm } from 'react-hook-form'; -import { useRouter } from 'next/navigation'; +import { useTrackEvent } from '@hooks'; import { LogoLogin } from '@/assets/svg/logo'; -import { setAccessToken } from '@/contexts/AuthContext'; - -interface LoginType { - email: string; - password: string; -} +import { KakaoButton } from '@/components/login'; const Page = () => { - const router = useRouter(); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm(); + const { trackEvent } = useTrackEvent(); - const onSubmitLogin: SubmitHandler = async (formData) => { - const { data } = await postLogin(formData.email, formData.password); + 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 { accessToken } = data?.data || {}; - if (accessToken) { - setAccessToken(accessToken); - router.push('/'); - } + const handleLoginClick = () => { + trackEvent('kakao_login_click'); + window.location.replace(kakaoLoginUrl); }; return ( @@ -41,41 +27,11 @@ const Page = () => {

포인터

- {/*
+

포인터는 태블릿의 스플릿뷰를 권장해요

- - -
*/} -
- - - {errors.password && ( -

- {errors.password.message} -

- )} - -
+ + {/* */} +
); }; diff --git a/apps/service/src/components/login/KakaoButton.tsx b/apps/service/src/components/login/KakaoButton.tsx index d7893912..d958922c 100644 --- a/apps/service/src/components/login/KakaoButton.tsx +++ b/apps/service/src/components/login/KakaoButton.tsx @@ -1,8 +1,14 @@ import { IcKakao } from '@svg'; -const KakaoButton = () => { +interface KakaoButtonProps { + onClick: () => void; +} + +const KakaoButton = ({ onClick }: KakaoButtonProps) => { return ( - From 0cfb8d73794c2af417129f446f895a33cefffa33 Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Sun, 30 Mar 2025 23:35:18 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat(service):=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/src/apis/controller/auth/index.ts | 4 ++- .../controller/auth/postKakaoAccessToken.ts | 13 +++++++ .../apis/controller/auth/postKakaoLogin.ts | 17 +++++++++ .../src/app/api/auth/callback/kakao/page.tsx | 36 +++++++++++++++++-- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 apps/service/src/apis/controller/auth/postKakaoAccessToken.ts create mode 100644 apps/service/src/apis/controller/auth/postKakaoLogin.ts diff --git a/apps/service/src/apis/controller/auth/index.ts b/apps/service/src/apis/controller/auth/index.ts index 38107e44..21ddb6fa 100644 --- a/apps/service/src/apis/controller/auth/index.ts +++ b/apps/service/src/apis/controller/auth/index.ts @@ -1,3 +1,5 @@ import postLogin from './postLogin'; +import postKakaoLogin from './postKakaoLogin'; +import postKakaoAccessToken from './postKakaoAccessToken'; -export { postLogin }; +export { postLogin, postKakaoLogin, postKakaoAccessToken }; diff --git a/apps/service/src/apis/controller/auth/postKakaoAccessToken.ts b/apps/service/src/apis/controller/auth/postKakaoAccessToken.ts new file mode 100644 index 00000000..e26da3b2 --- /dev/null +++ b/apps/service/src/apis/controller/auth/postKakaoAccessToken.ts @@ -0,0 +1,13 @@ +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; +}; + +export default postKakaoAccessToken; diff --git a/apps/service/src/apis/controller/auth/postKakaoLogin.ts b/apps/service/src/apis/controller/auth/postKakaoLogin.ts new file mode 100644 index 00000000..cc55df0e --- /dev/null +++ b/apps/service/src/apis/controller/auth/postKakaoLogin.ts @@ -0,0 +1,17 @@ +import { client } from '@/apis/client'; + +const postKakaoLogin = async (accessToken: string) => { + const response = await client.POST('/api/v1/auth/oauth/social-login', { + params: { + header: { + social_access_token: accessToken, + }, + query: { + provider: 'KAKAO', + }, + }, + }); + return response; +}; + +export default postKakaoLogin; diff --git a/apps/service/src/app/api/auth/callback/kakao/page.tsx b/apps/service/src/app/api/auth/callback/kakao/page.tsx index 668b304a..5e73634c 100644 --- a/apps/service/src/app/api/auth/callback/kakao/page.tsx +++ b/apps/service/src/app/api/auth/callback/kakao/page.tsx @@ -1,5 +1,37 @@ -const Page = () => { - return
Page
; +import { postKakaoAccessToken, postKakaoLogin } from '@apis'; +import { redirect } from 'next/navigation'; + +import { setAccessToken } from '@/contexts/AuthContext'; + +const Page = async ({ searchParams }: { searchParams: Promise<{ code: string }> }) => { + const { code } = await searchParams; + + try { + if (code) { + const kakaoAccessToken = await postKakaoAccessToken(code); + console.log('kakaoAccessToken', kakaoAccessToken); + + if (kakaoAccessToken) { + try { + const response = await postKakaoLogin(kakaoAccessToken); + console.log('응답 데이터:', response); + + if (response && response.data && response.data.data && response.data.data.accessToken) { + setAccessToken(response.data.data.accessToken); + redirect('/'); + } else { + console.error('accessToken을 찾을 수 없습니다:', response); + } + } catch (error) { + console.error('소셜 로그인 요청 오류:', error); + } + } + } + } catch (error) { + console.error('카카오 토큰 요청 오류:', error); + } + + return
code: {code}
; }; export default Page; From ddd37b2afaa5a0b84a02b546f0e4c160fcae6257 Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Mon, 31 Mar 2025 00:34:32 +0900 Subject: [PATCH 3/8] =?UTF-8?q?docs(service):=20openapi=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88=20=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/service/src/types/api/schema.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/service/src/types/api/schema.d.ts b/apps/service/src/types/api/schema.d.ts index f73b20bd..0f76b3e6 100644 --- a/apps/service/src/types/api/schema.d.ts +++ b/apps/service/src/types/api/schema.d.ts @@ -876,6 +876,7 @@ export interface components { LoginResponse: { /** Format: int64 */ memberId?: number; + name?: string; email?: string; accessToken?: string; refreshToken?: string; From d0590244e0f47475dc60abef05a74f2f8807990e Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Mon, 31 Mar 2025 00:36:33 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat(service):=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/controller/auth/postKakaoLogin.ts | 41 ++++++++++++++++++- .../src/app/api/auth/callback/kakao/page.tsx | 39 +++++------------- apps/service/src/contexts/AuthContext.tsx | 6 +++ 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/apps/service/src/apis/controller/auth/postKakaoLogin.ts b/apps/service/src/apis/controller/auth/postKakaoLogin.ts index cc55df0e..2c21c295 100644 --- a/apps/service/src/apis/controller/auth/postKakaoLogin.ts +++ b/apps/service/src/apis/controller/auth/postKakaoLogin.ts @@ -1,6 +1,24 @@ +'use client'; + +import { redirect } from 'next/navigation'; + import { client } from '@/apis/client'; +import { setAccessToken, setName } from '@/contexts/AuthContext'; -const postKakaoLogin = async (accessToken: string) => { +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: { @@ -11,6 +29,27 @@ const postKakaoLogin = async (accessToken: string) => { }, }, }); + + 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 = '/'; + } else { + console.error('accessToken을 찾을 수 없습니다:', response); + } + } catch (error) { + console.error('소셜 로그인 요청 오류:', error); + } + return response; }; diff --git a/apps/service/src/app/api/auth/callback/kakao/page.tsx b/apps/service/src/app/api/auth/callback/kakao/page.tsx index 5e73634c..b38e51e3 100644 --- a/apps/service/src/app/api/auth/callback/kakao/page.tsx +++ b/apps/service/src/app/api/auth/callback/kakao/page.tsx @@ -1,37 +1,20 @@ -import { postKakaoAccessToken, postKakaoLogin } from '@apis'; -import { redirect } from 'next/navigation'; +'use client'; -import { setAccessToken } from '@/contexts/AuthContext'; +import { postKakaoLogin } from '@apis'; +import { useSearchParams } from 'next/navigation'; +import { useEffect } from 'react'; -const Page = async ({ searchParams }: { searchParams: Promise<{ code: string }> }) => { - const { code } = await searchParams; +const Page = () => { + const searchParams = useSearchParams(); + const code = searchParams.get('code'); - try { + useEffect(() => { if (code) { - const kakaoAccessToken = await postKakaoAccessToken(code); - console.log('kakaoAccessToken', kakaoAccessToken); - - if (kakaoAccessToken) { - try { - const response = await postKakaoLogin(kakaoAccessToken); - console.log('응답 데이터:', response); - - if (response && response.data && response.data.data && response.data.data.accessToken) { - setAccessToken(response.data.data.accessToken); - redirect('/'); - } else { - console.error('accessToken을 찾을 수 없습니다:', response); - } - } catch (error) { - console.error('소셜 로그인 요청 오류:', error); - } - } + postKakaoLogin(code); } - } catch (error) { - console.error('카카오 토큰 요청 오류:', error); - } + }, []); - return
code: {code}
; + return <>; }; export default Page; diff --git a/apps/service/src/contexts/AuthContext.tsx b/apps/service/src/contexts/AuthContext.tsx index f36450b6..a7034806 100644 --- a/apps/service/src/contexts/AuthContext.tsx +++ b/apps/service/src/contexts/AuthContext.tsx @@ -11,6 +11,8 @@ export interface AuthContextType { const tokenStore = { accessToken: null as string | null, setAccessToken: (_: string | null) => {}, + name: '', + setName: (_: string) => {}, }; export const AuthContext = createContext(undefined); @@ -21,6 +23,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { tokenStore.accessToken = accessToken; tokenStore.setAccessToken = setAccessTokenState; + tokenStore.name = name; + tokenStore.setName = setNameState; const contextValue = { accessToken, @@ -34,3 +38,5 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { export const getAccessToken = () => tokenStore.accessToken; export const setAccessToken = (token: string | null) => tokenStore.setAccessToken(token); +export const getName = () => tokenStore.name; +export const setName = (name: string) => tokenStore.setName(name); From b4169795d144ae2c90729c7ca3cffe3ce6f8433f Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Mon, 31 Mar 2025 00:37:17 +0900 Subject: [PATCH 5/8] =?UTF-8?q?chore(service):=20=EC=95=88=EC=93=B0?= =?UTF-8?q?=EB=8A=94=20import=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/service/src/apis/controller/auth/postKakaoLogin.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/service/src/apis/controller/auth/postKakaoLogin.ts b/apps/service/src/apis/controller/auth/postKakaoLogin.ts index 2c21c295..6381f5fe 100644 --- a/apps/service/src/apis/controller/auth/postKakaoLogin.ts +++ b/apps/service/src/apis/controller/auth/postKakaoLogin.ts @@ -1,7 +1,5 @@ 'use client'; -import { redirect } from 'next/navigation'; - import { client } from '@/apis/client'; import { setAccessToken, setName } from '@/contexts/AuthContext'; From d38b07a79cb8db565a8a8c9dbcd33642da6dc0f8 Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Mon, 31 Mar 2025 00:52:45 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix(service):=20Suspense=20=EB=B9=8C?= =?UTF-8?q?=EB=93=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/service/src/app/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/service/src/app/layout.tsx b/apps/service/src/app/layout.tsx index c172743d..2e43092d 100644 --- a/apps/service/src/app/layout.tsx +++ b/apps/service/src/app/layout.tsx @@ -52,8 +52,8 @@ export default function RootLayout({ -
{children}
}> +
{children}
{modal}
From ceaa2e92566db3ffbdaba081c8999a2eaba56ccf Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Mon, 31 Mar 2025 00:53:17 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat(service):=20=ED=99=88=ED=97=A4?= =?UTF-8?q?=EB=8D=94=20=EC=9D=B4=EB=A6=84=20=EC=B6=9C=EB=A0=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/service/src/app/(home)/page.tsx | 4 ++-- .../src/components/home/HomeHeader.tsx | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/service/src/app/(home)/page.tsx b/apps/service/src/app/(home)/page.tsx index 829743e9..ee8f383d 100644 --- a/apps/service/src/app/(home)/page.tsx +++ b/apps/service/src/app/(home)/page.tsx @@ -1,11 +1,11 @@ 'use client'; +import { useRouter } from 'next/navigation'; import { Button } from '@components'; import { IcCalendar } from '@svg'; import { getHomeFeed } from '@apis'; import dayjs from 'dayjs'; import { DailyProgress } from '@types'; import { useTrackEvent } from '@hooks'; -import { useRouter } from 'next/navigation'; import { GuideButton, @@ -36,7 +36,7 @@ const Page = () => { return ( <> - +

아직은 고등학교 2학년 대상으로만 서비스를 하고 있어요! diff --git a/apps/service/src/components/home/HomeHeader.tsx b/apps/service/src/components/home/HomeHeader.tsx index 024a590a..1807ab13 100644 --- a/apps/service/src/components/home/HomeHeader.tsx +++ b/apps/service/src/components/home/HomeHeader.tsx @@ -1,14 +1,18 @@ +'use client'; + import Link from 'next/link'; import { IcSetting } from '@svg'; +import { useEffect, useState } from 'react'; import { LogoHeader } from '@/assets/svg/logo'; -interface HomeHeaderProps { - grade: number; - name: string; -} +const HomeHeader = () => { + const [name, setName] = useState(null); + + useEffect(() => { + setName(localStorage.getItem('name')); + }, []); -const HomeHeader = ({ name }: HomeHeaderProps) => { return (

@@ -19,7 +23,11 @@ const HomeHeader = ({ name }: HomeHeaderProps) => { {grade}학년 */}
- {name}님 + {name && ( + <> + {name}님 + + )}
From 10cee35753e3d59c3bee454844394dff7a57e9c4 Mon Sep 17 00:00:00 2001 From: Yoo TaeSeung Date: Mon, 31 Mar 2025 00:53:41 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor(service):=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20=EB=A7=88=EB=AC=B4=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/service/src/apis/controller/auth/index.ts | 3 +-- .../apis/controller/auth/postKakaoAccessToken.ts | 13 ------------- .../src/apis/controller/auth/postKakaoLogin.ts | 6 +++--- 3 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 apps/service/src/apis/controller/auth/postKakaoAccessToken.ts diff --git a/apps/service/src/apis/controller/auth/index.ts b/apps/service/src/apis/controller/auth/index.ts index 21ddb6fa..bdae8e16 100644 --- a/apps/service/src/apis/controller/auth/index.ts +++ b/apps/service/src/apis/controller/auth/index.ts @@ -1,5 +1,4 @@ import postLogin from './postLogin'; import postKakaoLogin from './postKakaoLogin'; -import postKakaoAccessToken from './postKakaoAccessToken'; -export { postLogin, postKakaoLogin, postKakaoAccessToken }; +export { postLogin, postKakaoLogin }; diff --git a/apps/service/src/apis/controller/auth/postKakaoAccessToken.ts b/apps/service/src/apis/controller/auth/postKakaoAccessToken.ts deleted file mode 100644 index e26da3b2..00000000 --- a/apps/service/src/apis/controller/auth/postKakaoAccessToken.ts +++ /dev/null @@ -1,13 +0,0 @@ -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; -}; - -export default postKakaoAccessToken; diff --git a/apps/service/src/apis/controller/auth/postKakaoLogin.ts b/apps/service/src/apis/controller/auth/postKakaoLogin.ts index 6381f5fe..e048dc49 100644 --- a/apps/service/src/apis/controller/auth/postKakaoLogin.ts +++ b/apps/service/src/apis/controller/auth/postKakaoLogin.ts @@ -1,7 +1,7 @@ 'use client'; import { client } from '@/apis/client'; -import { setAccessToken, setName } from '@/contexts/AuthContext'; +import { setAccessToken } from '@/contexts/AuthContext'; const postKakaoAccessToken = async (code: string) => { const response = await fetch(`https://kauth.kakao.com/oauth/token`, { @@ -33,12 +33,12 @@ const postKakaoLogin = async (code: string) => { response && response.data && response.data.data && - // response.data.data.name && + response.data.data.name && response.data.data.accessToken ) { const { accessToken, name } = response.data.data; setAccessToken(accessToken); - setName(name || ''); + localStorage.setItem('name', name); window.location.href = '/'; } else {