diff --git a/src/api/oauth.ts b/src/api/oauth.ts new file mode 100644 index 0000000..8c651b5 --- /dev/null +++ b/src/api/oauth.ts @@ -0,0 +1,58 @@ +// src/api/oauth.ts +import { apiFetch } from '@/config/client'; +import { OAuthAuthResponse } from '@/types/oauth'; + +type OAuthProvider = 'kakao'; + +type OAuthSignInParams = { + provider: OAuthProvider; + redirectUri: string; + token: string; +}; + +type OAuthSignUpParams = { + provider: OAuthProvider; + redirectUri: string; + token: string; + nickname: string; +}; + +/** + * OAuth 로그인 + * POST /oauth/sign-in/{provider} + */ +export function oauthSignIn({ + provider, + redirectUri, + token, +}: OAuthSignInParams) { + return apiFetch(`/oauth/sign-in/${provider}`, { + method: 'POST', + body: { + redirectUri, + token, + }, + skipAuthRefresh: true, + }); +} + +/** + * OAuth 회원가입 + * POST /oauth/sign-up/{provider} + */ +export function oauthSignUp({ + provider, + redirectUri, + token, + nickname, +}: OAuthSignUpParams) { + return apiFetch(`/oauth/sign-up/${provider}`, { + method: 'POST', + body: { + redirectUri, + token, + nickname, + }, + skipAuthRefresh: true, + }); +} diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index a4c32f0..6c6c7e9 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -9,6 +9,7 @@ import { login } from '@/api/auth'; import kakaoLogo from '@/assets/icons/auth/ic-kakao.svg'; import Button from '@/components/Button'; import { TextInput, PasswordInput } from '@/components/Input'; +import { KAKAO_REST_API_KEY, KAKAO_REDIRECT_URI } from '@/config/oauth'; import { useGuestOnly } from '@/hooks/useGuestOnly'; import { validateEmail, validatePassword } from '@/util/validations'; @@ -19,6 +20,18 @@ export default function LoginPage() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const handleKakaoLogin = () => { + const url = + 'https://kauth.kakao.com/oauth/authorize' + + `?client_id=${KAKAO_REST_API_KEY}` + + `&redirect_uri=${encodeURIComponent(KAKAO_REDIRECT_URI)}` + + '&response_type=code' + + '&state=login' + + '&prompt=login'; + + window.location.href = url; + }; + useEffect(() => { const savedEmail = sessionStorage.getItem('signupEmail'); @@ -126,7 +139,12 @@ export default function LoginPage() {
- diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 0f25ba1..3c827fc 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -9,6 +9,8 @@ import { signup } from '@/api/users'; import kakaoLogo from '@/assets/icons/auth/ic-kakao.svg'; import Button from '@/components/Button'; import { TextInput, PasswordInput } from '@/components/Input'; +import { useToast } from '@/components/toast/useToast'; +import { KAKAO_REST_API_KEY, KAKAO_REDIRECT_URI } from '@/config/oauth'; import { useGuestOnly } from '@/hooks/useGuestOnly'; import { validateEmail, @@ -35,6 +37,19 @@ export default function SignupPage() { }); const router = useRouter(); + const toast = useToast(); + + const handleKakaoSignup = () => { + const url = + 'https://kauth.kakao.com/oauth/authorize' + + `?client_id=${KAKAO_REST_API_KEY}` + + `&redirect_uri=${encodeURIComponent(KAKAO_REDIRECT_URI)}` + + '&response_type=code' + + '&state=signup' + + '&prompt=login'; + + window.location.href = url; + }; const handleChange = (key: keyof typeof form) => (value: string) => { setForm((prev) => ({ @@ -71,29 +86,29 @@ export default function SignupPage() { password: form.password, nickname: form.nickname, }); - //TODO toast 팝업으로 교체 - alert('회원가입이 완료되었습니다!'); + toast.success('회원가입이 완료되었습니다!'); sessionStorage.setItem('signupEmail', form.email); - router.push('/login'); } catch (error) { - //TODO 에러처리 if (error instanceof Error) { if (error.message.includes('이메일')) { setErrors((prev) => ({ ...prev, email: error.message, })); + toast.error(error.message); } else if (error.message.includes('닉네임')) { setErrors((prev) => ({ ...prev, nickname: error.message, })); + toast.error(error.message); } else { setErrors((prev) => ({ ...prev, password: error.message, })); + toast.error(error.message); } } else { // 예상치 못한 에러 @@ -101,6 +116,7 @@ export default function SignupPage() { ...prev, password: '알 수 없는 오류가 발생했습니다.', })); + toast.error('알 수 없는 오류가 발생했습니다.'); } } }; @@ -199,7 +215,12 @@ export default function SignupPage() {
- diff --git a/src/app/(common)/(mypage)/mypage/hooks/useMyPageForm.ts b/src/app/(common)/(mypage)/mypage/hooks/useMyPageForm.ts index 07d624a..516d57d 100644 --- a/src/app/(common)/(mypage)/mypage/hooks/useMyPageForm.ts +++ b/src/app/(common)/(mypage)/mypage/hooks/useMyPageForm.ts @@ -6,6 +6,7 @@ import { createUpdatePayload, isUnauthorizedError } from './useMypageFormUtils'; import { validateForm } from './useMyPageFormValidators'; import { useGetMyInfo, useUpdateMyInfo } from './useUser'; +import { useToast } from '@/components/toast/useToast'; import { getApiErrorMessage } from '@/util/error'; /** @@ -16,6 +17,7 @@ import { getApiErrorMessage } from '@/util/error'; */ export function useMyPageForm() { const router = useRouter(); + const toast = useToast(); // React Query: 사용자 정보 조회 const { @@ -59,8 +61,8 @@ export function useMyPageForm() { if (fetchError) { console.error('사용자 정보 로딩 실패:', fetchError); if (isUnauthorizedError(fetchError)) { - alert('로그인이 필요합니다.'); - router.push('/signin'); + toast.warning('로그인이 필요합니다.'); + router.push('/login'); } } }, [fetchError, router]); @@ -102,7 +104,7 @@ export function useMyPageForm() { try { const payload = createUpdatePayload(formData); await updateProfile(payload); - alert('저장되었습니다.'); + toast.success('저장되었습니다!'); // 비밀번호 필드 초기화 setFormData((prev) => ({ @@ -113,12 +115,12 @@ export function useMyPageForm() { } catch (error: unknown) { console.error('저장 실패:', error); if (isUnauthorizedError(error)) { - alert('로그인이 필요합니다.'); - router.push('/signin'); + toast.warning('로그인이 필요합니다.'); + router.push('/login'); return; } const errorMessage = getApiErrorMessage(error, '저장에 실패했습니다.'); - alert(errorMessage); + toast.error(errorMessage); } }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8bc7627..c068fd9 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -29,6 +29,13 @@ export default function RootLayout({ }) { return ( + + {/* kakao logout SDK */} +