diff --git a/package.json b/package.json index 41d90327..f91142d0 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "swiper": "^11.2.5", "tailwind-merge": "^2.6.0", "tailwind-scrollbar-hide": "^1.3.1", + "uuid": "^11.1.0", "workbox-window": "^7.3.0", "yup": "^1.6.1", "zustand": "^4.5.6" diff --git a/src/_common/BottomButton.tsx b/src/_common/BottomButton.tsx index 22fde910..a93195c7 100644 --- a/src/_common/BottomButton.tsx +++ b/src/_common/BottomButton.tsx @@ -34,9 +34,6 @@ export default function BottomButton({ ) : ( - + 다음 diff --git a/src/app/(view)/(auth)/register/details/page.tsx b/src/_components/auth/register/userInfo/UserInfo.tsx similarity index 66% rename from src/app/(view)/(auth)/register/details/page.tsx rename to src/_components/auth/register/userInfo/UserInfo.tsx index 2047ccf0..9ae3631e 100644 --- a/src/app/(view)/(auth)/register/details/page.tsx +++ b/src/_components/auth/register/userInfo/UserInfo.tsx @@ -3,30 +3,24 @@ import { AxiosError } from "axios"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { useCookies } from "react-cookie"; import { toast } from "react-toastify"; import { instance } from "@/app/api/axios"; import requests from "@/app/api/requests"; -import DetailsText from "@/_components/auth/DetailsText"; -import GenderForm from "@/_components/auth/GenderForm"; -import PreferredAlcoholForm from "@/_components/auth/PreferredAlcoholForm"; -import RegisterConfirmModal from "@/_components/auth/RegisterConfirmModal"; +import RegisterConfirmModal from "@/_components/auth/register/RegisterConfirmModal"; +import UserInfoText from "@/_components/auth/register/userInfo/UserInfoText"; import BottomButton from "@/_common/BottomButton"; -import ConfirmModal from "@/_common/ConfirmModal"; -import TopHeader from "@/_common/TopHeader"; +import GenderForm from "@/_common/GenderForm"; +import PreferredAlcoholForm from "@/_common/PreferredAlcoholForm"; import { useRegisterStore } from "@/_store/register"; -export default function Page() { +export default function UserInfo() { const registerStore = useRegisterStore(); const router = useRouter(); - const [cookies, setCookie] = useCookies(["accessToken"]); const [alcoholTypes, setAlcoholTypes] = useState([]); const [gender, setGender] = useState(""); const [genderCheck, setGenderCheck] = useState(false); const [registerConfirmModalOpen, setRegisterConfirmModalOpen] = useState(false); - const [registerCancelModalOpen, setRegisterCancelModalOpen] = - useState(false); const enableRegisterButton = (gender || genderCheck) && alcoholTypes.length ? true : false; @@ -58,40 +52,17 @@ export default function Page() { setRegisterConfirmModalOpen(false); }; - const handleRegisterCancelModalOpen = () => { - setRegisterCancelModalOpen(true); - }; - - const handleRegisterCancelModalClose = () => { - setRegisterCancelModalOpen(false); - }; - - const handleRegisterCancel = () => { - router.replace("/"); - }; - const handleRegisterConfirm = async () => { const data = { - email: registerStore.email, nickname: registerStore.nickname, gender: registerStore.gender, - provider: registerStore.provider, - providerId: registerStore.providerId, alcoholTypeIds: registerStore.preferredAlcoholType, - termsAgreements: [ - registerStore.serviceAgree, - registerStore.privateInformationAgree, - registerStore.marketingAgree, - ], + termsAgreements: [registerStore.privateInformationAgree], }; try { const response = await instance.post(requests.postSignUp, data); if (response.status === 200) { localStorage.setItem("recentLogin", registerStore.provider); - registerStore.setMemberId(response.data.result.memberId); - setCookie("accessToken", response.data.result.token.accessToken, { - path: "/", - }); router.replace("/share/note"); } } catch (error) { @@ -118,13 +89,7 @@ export default function Page() { return ( registerStore.nickname && ( <> - - + 회원가입 완료하기 - {registerCancelModalOpen && ( - - )} {registerConfirmModalOpen && ( diff --git a/src/_components/notification/NotificationProvider.tsx b/src/_components/notification/NotificationProvider.tsx index a69089fb..ce825c76 100644 --- a/src/_components/notification/NotificationProvider.tsx +++ b/src/_components/notification/NotificationProvider.tsx @@ -23,10 +23,10 @@ export default function NotificationProvider({ }); }; - const unsubscribe = subscribeToNotifications(handleNewNotification); + // const unsubscribe = subscribeToNotifications(handleNewNotification); return () => { - unsubscribe(); + // unsubscribe(); }; }, []); diff --git a/src/_components/user/EditMyInfo.tsx b/src/_components/user/EditMyInfo.tsx index 55379672..3010d4e3 100644 --- a/src/_components/user/EditMyInfo.tsx +++ b/src/_components/user/EditMyInfo.tsx @@ -8,10 +8,10 @@ import { toast } from "react-toastify"; import { checkNickname } from "@/app/api/auth/checkName"; import { formInstance } from "@/app/api/axios"; import { urlToFile } from "@/app/api/life/urlToFile"; -import GenderForm from "@/_components/auth/GenderForm"; -import PreferredAlcoholForm from "@/_components/auth/PreferredAlcoholForm"; import ProfileChangeModal from "@/_components/user/ProfileChangeModal"; import BottomButton from "@/_common/BottomButton"; +import GenderForm from "@/_common/GenderForm"; +import PreferredAlcoholForm from "@/_common/PreferredAlcoholForm"; import { resizeImage } from "@/_utils/resizeImage"; import { IMyInfo } from "@/_types"; diff --git a/src/_store/register.ts b/src/_store/register.ts index b01ea818..1145dd5c 100644 --- a/src/_store/register.ts +++ b/src/_store/register.ts @@ -18,7 +18,6 @@ interface IRegisterUserState { nickname: string; email: string; provider: string; - providerId: string; gender: string; genderCheck: boolean; preferredAlcoholType: number[]; @@ -30,7 +29,6 @@ interface IRegisterUserState { setNickname: (value: string) => void; setEmail: (value: string) => void; setProvider: (value: string) => void; - setProviderId: (value: string) => void; setGender: (value: string) => void; setGendercheck: (value: boolean) => void; setPreferredAlcoholType: (value: number[]) => void; @@ -56,7 +54,6 @@ export const useRegisterStore = create( nickname: "", email: "", provider: "", - providerId: "", gender: "", genderCheck: false, preferredAlcoholType: [], @@ -92,8 +89,6 @@ export const useRegisterStore = create( setEmail: (value: string) => set((state) => ({ ...state, email: value })), setProvider: (value: string) => set((state) => ({ ...state, provider: value })), - setProviderId: (value: string) => - set((state) => ({ ...state, providerId: value })), setGender: (value: string) => set((state) => ({ ...state, gender: value })), setGendercheck: (value: boolean) => @@ -107,6 +102,7 @@ export const useRegisterStore = create( }), { name: "userRegisterStorage", + getStorage: () => sessionStorage, }, ), ); diff --git a/src/_utils/hooks/useFunnel.tsx b/src/_utils/hooks/useFunnel.tsx new file mode 100644 index 00000000..4ab40773 --- /dev/null +++ b/src/_utils/hooks/useFunnel.tsx @@ -0,0 +1,30 @@ +import { useState, ReactNode, isValidElement, ReactElement } from "react"; + +export interface StepProps { + name: string; + children: ReactNode; +} + +export interface FunnelProps { + children: Array>; +} + +function Step({ children }: StepProps) { + return <>{children}; +} + +export default function useFunnel(steps: string) { + const [step, setStep] = useState(steps); + + function Funnel({ children }: FunnelProps) { + const targetStep = children.find( + (childStep) => isValidElement(childStep) && childStep.props.name === step, + ); + + return <>{targetStep}; + } + + Funnel.Step = Step; + + return { Funnel, setStep, currentStep: step } as const; +} diff --git a/src/app/(view)/(auth)/login/kakao/loading.tsx b/src/app/(view)/(auth)/login/kakao/loading.tsx deleted file mode 100644 index 436def21..00000000 --- a/src/app/(view)/(auth)/login/kakao/loading.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import LoginLoading from "@/_components/auth/handler/LoginLoading"; - -export default function KaKaoLoginLoading() { - return ; -} diff --git a/src/app/(view)/(auth)/login/kakao/page.tsx b/src/app/(view)/(auth)/login/kakao/page.tsx deleted file mode 100644 index 7370188e..00000000 --- a/src/app/(view)/(auth)/login/kakao/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import KaKaoLoginHandler from "@/_components/auth/handler/KaKaoLoginHandler"; - -export default function Page() { - return ; -} diff --git a/src/app/(view)/(auth)/login/oauth2/code/google/page.tsx b/src/app/(view)/(auth)/login/oauth2/code/google/page.tsx deleted file mode 100644 index cda698eb..00000000 --- a/src/app/(view)/(auth)/login/oauth2/code/google/page.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { useRouter, useSearchParams } from "next/navigation"; -import { Suspense, useEffect } from "react"; -import { useCookies } from "react-cookie"; -import { instance } from "@/app/api/axios"; -import requests from "@/app/api/requests"; -import Loading from "@/_common/Loading"; -import { useRegisterStore } from "@/_store/register"; - -function GoogleLoginHandlerComponent() { - const { setEmail, setProvider, setProviderId } = useRegisterStore(); - const searchParams = useSearchParams(); - const router = useRouter(); - const [cookies, setCookie, removeCookie] = useCookies(["accessToken"]); - - useEffect(() => { - const authCode = searchParams.get("code"); - if (authCode) { - const loginHandler = async () => { - try { - const response = await instance.post(requests.postGoogleLogin, { - code: authCode, - provider: "GOOGLE", - redirectUri: process.env.NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI, - }); - if (response.status === 200) { - const data = response.data.result.oAuthUserInfo; - setEmail(data.email); - setProvider(data.provider); - setProviderId(data.providerId); - if (response.data.result.isNewMember) { - router.push("/register/agreement"); - } else { - setCookie("accessToken", response.data.result.token.accessToken, { - path: "/", - expires: new Date(response.data.result.token.accessExpiredAt), - }); - router.push("/share/note"); //추후 수정 예정 - } - } - } catch (error) { - console.error(error); - } - }; - loginHandler(); - } - }); - return
구글 소셜 로그인
; -} - -export default function Page() { - return ( - }> - - - ); -} diff --git a/src/app/(view)/(auth)/login/redirect/page.tsx b/src/app/(view)/(auth)/login/redirect/page.tsx new file mode 100644 index 00000000..28bcf938 --- /dev/null +++ b/src/app/(view)/(auth)/login/redirect/page.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { Suspense, useEffect } from "react"; +import LoginLoading from "@/_components/auth/login/LoginLoading"; + +function LoginHandlerComponent() { + const router = useRouter(); + + useEffect(() => { + router.replace("/share/note"); + }, []); + + return null; +} + +export default function Page() { + return ( + }> + + + ); +} diff --git a/src/app/(view)/(auth)/register/document/[id]/page.tsx b/src/app/(view)/(auth)/sign-up/document/[id]/page.tsx similarity index 100% rename from src/app/(view)/(auth)/register/document/[id]/page.tsx rename to src/app/(view)/(auth)/sign-up/document/[id]/page.tsx diff --git a/src/app/(view)/(auth)/register/layout.tsx b/src/app/(view)/(auth)/sign-up/layout.tsx similarity index 100% rename from src/app/(view)/(auth)/register/layout.tsx rename to src/app/(view)/(auth)/sign-up/layout.tsx diff --git a/src/app/(view)/(auth)/sign-up/redirect/page.tsx b/src/app/(view)/(auth)/sign-up/redirect/page.tsx new file mode 100644 index 00000000..4161b8ad --- /dev/null +++ b/src/app/(view)/(auth)/sign-up/redirect/page.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import RegisterSetup from "@/_components/auth/register/RegisterSetup"; +import ConfirmModal from "@/_common/ConfirmModal"; +import TopHeader from "@/_common/TopHeader"; +import useFunnel from "@/_utils/hooks/useFunnel"; + +const STEPS = ["약관동의", "닉네임", "가입정보"]; + +export default function Page() { + const { Funnel, currentStep, setStep } = useFunnel(STEPS[0]); + const router = useRouter(); + const step = STEPS.indexOf(currentStep) + 1; + const [registerCancelModalOpen, setRegisterCancelModalOpen] = + useState(false); + + const handleRegisterCancelModalOpen = () => { + setRegisterCancelModalOpen(true); + }; + + const handleRegisterCancelModalClose = () => { + setRegisterCancelModalOpen(false); + }; + + const handleStepChange = (newStep: string) => { + if (STEPS.includes(newStep)) { + setStep(newStep); + } + }; + + const handleRegisterCancel = () => { + router.replace("/"); + }; + + const handleBack = () => { + const currentIndex = STEPS.indexOf(currentStep); + if (currentIndex > 0) { + setStep(STEPS[currentIndex - 1]); + } else { + setStep(STEPS[0]); + } + }; + + useEffect(() => { + const handleBeforeUnload = (event: BeforeUnloadEvent) => { + event.preventDefault(); + }; + + window.addEventListener("beforeunload", handleBeforeUnload); + + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, []); + + return ( + <> + handleBack()} + onClick={handleRegisterCancelModalOpen} + /> + + {registerCancelModalOpen && ( + + )} + + ); +} diff --git a/src/app/(view)/(main)/report/page.tsx b/src/app/(view)/(main)/report/page.tsx index 95493707..bbfdc660 100644 --- a/src/app/(view)/(main)/report/page.tsx +++ b/src/app/(view)/(main)/report/page.tsx @@ -1,11 +1,11 @@ "use client"; -import ReportForm from "@/_components/report/ReportForm"; -import { useReportStore } from "@/_store/useReportStore"; -import postReport from "@/app/api/report/postReport"; import { useRouter } from "next/navigation"; import { toast } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; +import postReport from "@/app/api/report/postReport"; +import ReportForm from "@/_components/report/ReportForm"; +import { useReportStore } from "@/_store/useReportStore"; export default function Page() { const { reportId } = useReportStore(); diff --git a/src/app/api/axios.ts b/src/app/api/axios.ts index be451ba2..2c75ed4e 100644 --- a/src/app/api/axios.ts +++ b/src/app/api/axios.ts @@ -4,6 +4,7 @@ import nookies from "nookies"; export const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_JUULABEL_API_URL, timeout: 20000, + withCredentials: true, }); instance.interceptors.request.use( @@ -11,10 +12,10 @@ instance.interceptors.request.use( // 브라우저 환경에서만 쿠키 읽기 if (typeof window !== "undefined") { const cookies = nookies.get(); - const accessToken = cookies.accessToken; + const csrftoken = cookies.csrfToken; - if (accessToken) { - config.headers.Authorization = `Bearer ${accessToken}`; + if (csrftoken) { + config.headers["X-CSRF-TOKEN"] = csrftoken; } } return config; @@ -28,4 +29,5 @@ export const formInstance = axios.create({ headers: { "Content-Type": "multipart/form-data", }, + withCredentials: true, }); diff --git a/src/app/api/requests.ts b/src/app/api/requests.ts index 3f476ed1..fd264443 100644 --- a/src/app/api/requests.ts +++ b/src/app/api/requests.ts @@ -1,14 +1,14 @@ const requests = { getAlcoholTypes: `/v1/api/alcohols/types`, - postSignUp: `/v1/api/members/sign-up`, - postKakaoLogin: `/v1/api/members/login/kakao`, - postGoogleLogin: `/v1/api/members/login/google`, + postSignUp: `/v1/api/auth/sign-up`, + postKakaoLogin: `/v1/api/auth/oauth/callback/kakao`, + postGoogleLogin: `/v1/api/auth/oauth/callback/google`, getTerms: `/v1/api/terms`, follow: `/v1/api/follow`, typeSerach: `/v1/api/alcoholicDrinks/typeSearch?`, noteSearch: `/v1/api/shared-space/tasting-notes/search?`, lifeDelete: `/v1/api/daily-lives/`, - deleteMe: `/v1/api/members/me`, + deleteMe: `/v1/api/auth/me`, deleteFollow: `/v1/api/delete/following`, };