diff --git a/next.config.ts b/next.config.ts index 189df27..6a574df 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,6 @@ import type { NextConfig } from 'next'; -const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL; +const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URI; const nextConfig: NextConfig = { reactStrictMode: true, diff --git a/package.json b/package.json index 90949a4..80a0928 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@svgr/cli": "^8.1.0", "@tanstack/react-query": "^5.75.5", "clsx": "^2.1.1", + "js-base64": "^3.7.7", "next": "15.2.4", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/src/app/(auth)/@content/login/page.tsx b/src/app/(auth)/@content/login/page.tsx index 9d549bb..8b57c81 100644 --- a/src/app/(auth)/@content/login/page.tsx +++ b/src/app/(auth)/@content/login/page.tsx @@ -1,3 +1,5 @@ +'use client'; + import { Spacing } from '@/components/ui/Spacing'; import Logo from '@/assets/logo.svg'; import IconKaKao from '@/assets/icons/icon-kakao.svg'; @@ -14,11 +16,21 @@ export default function LoginPage() { {/* TODO : api 연결할 때 onClick 연결 필요 */} - + { + window.location.href = `${process.env.NEXT_PUBLIC_API_BASE_URI}/oauth2/authorization/kakao`; + }} + className="flex h-[45px] w-[300px] items-center justify-center rounded-md bg-[#fee500] p-[14px]" + > - + { + window.location.href = `${process.env.NEXT_PUBLIC_API_BASE_URI}/oauth2/authorization/google`; + }} + className="border-layout-grey4 flex h-[45px] w-[300px] items-center justify-center rounded-md border p-[14px]" + > diff --git a/src/app/(auth)/@content/sign-up/step3/page.tsx b/src/app/(auth)/@content/sign-up/step3/page.tsx index a6f48d2..ed2cf1f 100644 --- a/src/app/(auth)/@content/sign-up/step3/page.tsx +++ b/src/app/(auth)/@content/sign-up/step3/page.tsx @@ -6,6 +6,7 @@ import { Spacing } from '@/components/ui/Spacing'; import { Button } from '@/components/ui/Button'; import { StepBar } from '@/components/ui/StepBar'; import { useSignupStore } from '@/stores/signUpStore'; +import { useSignUp } from '@/hooks/users/useSignUp'; import IconArrowDown from '@/assets/icons/icon-arrow-down.svg'; import IconArrowBack from '@/assets/icons/icon-arrow-back.svg'; @@ -35,7 +36,8 @@ const category = [ export default function Step1() { const router = useRouter(); - const { preferences, setPreferences, clearPreferences } = useSignupStore(); + const { username, role, preferences, setPreferences, clearPreferences } = useSignupStore(); + const { mutate: signUp } = useSignUp(); const [currentIndex, setCurrentIndex] = useState(0); @@ -52,6 +54,14 @@ export default function Step1() { router.push('/sign-up-complete'); }; + const handleSignUp = () => { + signUp({ + name: username, + job: role, + tags: preferences, + }); + }; + return ( @@ -111,11 +121,24 @@ export default function Step1() { - + { + handleSkipBtn(); + handleSignUp(); + }} + > 건너뛰기 {preferences.length >= 3 ? ( - + { + handleNextBtn(); + handleSignUp(); + }} + > 다음으로 ) : ( diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx index cb688f8..6c56eab 100644 --- a/src/app/(auth)/layout.tsx +++ b/src/app/(auth)/layout.tsx @@ -9,7 +9,6 @@ export default function AuthLayout({ content, image }: Props) { return ( {content} - {image} ); diff --git a/src/app/callback/page.tsx b/src/app/callback/page.tsx new file mode 100644 index 0000000..3f7fc4b --- /dev/null +++ b/src/app/callback/page.tsx @@ -0,0 +1,36 @@ +'use client'; + +import { useEffect, Suspense } from 'react'; +import { useSearchParams, useRouter } from 'next/navigation'; +import { handleLoginSuccess } from '@/utils/loginHandler'; + +function CallbackContent() { + const searchParams = useSearchParams(); + const router = useRouter(); + + useEffect(() => { + const type = searchParams.get('type'); + const accessToken = searchParams.get('token'); + + switch (type) { + case 'NEW_USER': + router.replace('/sign-up/step1'); + break; + case 'SUCCESS': + default: + handleLoginSuccess(accessToken); + router.replace('/advice'); + break; + } + }, [searchParams, router]); + + return Redirecting...; +} + +export default function Callback() { + return ( + Loading...}> + + + ); +} diff --git a/src/components/ui/Header/index.tsx b/src/components/ui/Header/index.tsx index 6b0a34f..734431d 100644 --- a/src/components/ui/Header/index.tsx +++ b/src/components/ui/Header/index.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useEffect, useState } from 'react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { cn } from '@/lib/utils'; @@ -8,6 +9,7 @@ import Logo from '@/assets/logo.svg'; export default function Header() { const pathname = usePathname(); + const [isLogin, setIsLogin] = useState(false); const menus = [ { label: '조언받기', href: '/advice' }, @@ -15,10 +17,11 @@ export default function Header() { { label: '보관함', href: '/archive' }, ]; - { - /* TODO : 쿠키에서 로그인 여부 받아서 수정할 수 있도록 하기 */ - } - const isLogin = true; + useEffect(() => { + const token = localStorage.getItem('token'); + const user = localStorage.getItem('user'); + setIsLogin(!!(token && user)); + }, []); return ( @@ -29,7 +32,6 @@ export default function Header() { {menus.map((menu) => { const isActive = pathname.startsWith(menu.href); - return ( { alert('로그아웃!'); }} diff --git a/src/hooks/users/useSignUp.ts b/src/hooks/users/useSignUp.ts new file mode 100644 index 0000000..134df54 --- /dev/null +++ b/src/hooks/users/useSignUp.ts @@ -0,0 +1,7 @@ +import { SignupRequest, userSignUp } from '@/services/users/postSignUp'; +import { useMutation } from '@tanstack/react-query'; + +export const useSignUp = () => + useMutation({ + mutationFn: (data) => userSignUp(data), + }); diff --git a/src/services/users/postSignUp.ts b/src/services/users/postSignUp.ts new file mode 100644 index 0000000..6482a4e --- /dev/null +++ b/src/services/users/postSignUp.ts @@ -0,0 +1,14 @@ +import { customFetch } from '@/utils/customFetch'; + +export interface SignupRequest { + name: string; + job: string; + tags: string[]; +} + +export const userSignUp = async (payload: SignupRequest): Promise => { + return customFetch('/users/sign-up', { + method: 'POST', + body: JSON.stringify(payload), + }); +}; diff --git a/src/utils/loginHandler.ts b/src/utils/loginHandler.ts new file mode 100644 index 0000000..7020e3c --- /dev/null +++ b/src/utils/loginHandler.ts @@ -0,0 +1,15 @@ +import { decode } from 'js-base64'; + +export const handleLoginSuccess = (accessToken: string | null) => { + if (accessToken) { + const payload = accessToken.split('.')[1] || ''; + const decodedPayload = decode(payload); + const payloadObject = JSON.parse(decodedPayload); + const { name: tokenName } = payloadObject; + + const userInfo = { name: tokenName }; + + localStorage.setItem('token', accessToken); + localStorage.setItem('user', JSON.stringify(userInfo)); + } +}; diff --git a/yarn.lock b/yarn.lock index 7265896..cbaf6d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3876,6 +3876,7 @@ __metadata: eslint-plugin-react: "npm:^7.37.5" eslint-plugin-react-hooks: "npm:^5.2.0" husky: "npm:^9.1.7" + js-base64: "npm:^3.7.7" lint-staged: "npm:^15.5.0" next: "npm:15.2.4" postcss: "npm:^8.5.3" @@ -4564,6 +4565,13 @@ __metadata: languageName: node linkType: hard +"js-base64@npm:^3.7.7": + version: 3.7.7 + resolution: "js-base64@npm:3.7.7" + checksum: 10c0/3c905a7e78b601e4751b5e710edd0d6d045ce2d23eb84c9df03515371e1b291edc72808dc91e081cb9855aef6758292a2407006f4608ec3705373dd8baf2f80f + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0"
Redirecting...