From f938a50919c5caece78c95d52c3d205b5ffb3306 Mon Sep 17 00:00:00 2001 From: ohamin26 Date: Mon, 26 May 2025 22:35:44 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix=20:=20eslint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(view)/(main)/report/page.tsx | 6 +++--- src/app/api/requests.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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/requests.ts b/src/app/api/requests.ts index 3f476ed1..9967ffa1 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`, + postKakaoLogin: `/v1/api/auth/login/kakao`, + postGoogleLogin: `/v1/api/auth/login/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`, }; From fe1db2e3f9f10892bcc89e585acbcf90531766e8 Mon Sep 17 00:00:00 2001 From: ohamin26 Date: Fri, 6 Jun 2025 18:53:54 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor=20:=20Jwt=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JWT 토큰 기반 => 세션 기반으로 리팩토링 - 그에 따른 불필요한 파일 삭제 - 사용자 구분 방식(deviceID) 추가 --- package.json | 1 + src/_common/BottomButton.tsx | 3 - src/_components/SplashScreen.tsx | 2 +- src/_components/auth/login/LoginButton.tsx | 25 +++++ src/_components/auth/login/LoginForm.tsx | 92 +++++++++++++++++++ src/_components/auth/login/LoginLoading.tsx | 32 +++++++ src/app/(view)/(auth)/login/kakao/loading.tsx | 5 - src/app/(view)/(auth)/login/kakao/page.tsx | 5 - .../(auth)/login/oauth2/code/google/page.tsx | 58 ------------ 9 files changed, 151 insertions(+), 72 deletions(-) create mode 100644 src/_components/auth/login/LoginButton.tsx create mode 100644 src/_components/auth/login/LoginForm.tsx create mode 100644 src/_components/auth/login/LoginLoading.tsx delete mode 100644 src/app/(view)/(auth)/login/kakao/loading.tsx delete mode 100644 src/app/(view)/(auth)/login/kakao/page.tsx delete mode 100644 src/app/(view)/(auth)/login/oauth2/code/google/page.tsx 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/_components/auth/login/LoginForm.tsx b/src/_components/auth/login/LoginForm.tsx new file mode 100644 index 00000000..6bf8c020 --- /dev/null +++ b/src/_components/auth/login/LoginForm.tsx @@ -0,0 +1,92 @@ +"use client"; + +import Image from "next/image"; +import { useEffect, useState } from "react"; +import { v4 as uuidv4 } from "uuid"; +import LoginButton from "@/_components/auth/login/LoginButton"; +import TopHeader from "@/_common/TopHeader"; + +export default function LoginForm() { + // state 추가 deviceID + const deviceId = localStorage.getItem("device-id") || uuidv4(); + if (!localStorage.getItem("device-id")) { + localStorage.setItem("device-id", deviceId); + } + const kakaoSocialLoginLink = `https://kauth.kakao.com/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_KAKAO_API_KEY}&redirect_uri=${process.env.NEXT_PUBLIC_KAKAO_LOGIN_REDIRECT_URI}&response_type=code&state=${deviceId}`; + const googleSocialLoginLink = `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=${process.env.NEXT_PUBLIC_GOOGLE_API_KEY}&scope=email&redirect_uri=${process.env.NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI}&state=${deviceId}`; + const [kakaoRecentLogin, setKakaoRecentLogin] = useState(false); + const [googleRecentLogin, setGoogleRecentLogin] = useState(false); + + const handleKakaoLogin = () => { + window.location.href = kakaoSocialLoginLink; + }; + + const handleGoogleLogin = () => { + window.location.href = googleSocialLoginLink; + }; + + useEffect(() => { + if (localStorage.getItem("recentLogin") === "KAKAO") + setKakaoRecentLogin(true); + else if (localStorage.getItem("recentLogin") === "GOOGLE") + setGoogleRecentLogin(true); + }, []); + + return ( +
+ +
+ 주라벨 메인로고 +

+ 주라벨에 오신 것을 환영해요! +

+
+ + {kakaoRecentLogin && ( +
+ 최근 로그인 +
+ )} + + 카카오 소셜 로그인 버튼 +

카카오로 시작하기

+
+ + +
+ {googleRecentLogin && ( + 최근 로그인 + )} +
+ 구글 소셜 로그인 버튼 +

구글로 시작하기

+
+
+ ); +} diff --git a/src/_components/auth/login/LoginLoading.tsx b/src/_components/auth/login/LoginLoading.tsx new file mode 100644 index 00000000..7c6c174e --- /dev/null +++ b/src/_components/auth/login/LoginLoading.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { motion } from "framer-motion"; + +export default function LoginLoading() { + return ( + + + 가장 쉽고, 간편하게 우리술과 가까워지는 방법 + + + + "주라벨" + + + ); +} 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 ( - }> - - - ); -} From 2575f36fd922633ae636aeab1f6c161ff4443eea Mon Sep 17 00:00:00 2001 From: ohamin26 Date: Fri, 6 Jun 2025 19:35:09 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix=20:=20=EA=B0=9C=EB=B0=9C=EC=A4=91=20-?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth => _common}/GenderForm.tsx | 0 .../auth => _common}/PreferredAlcoholForm.tsx | 0 src/_common/TopHeader.tsx | 18 ++-- src/_components/auth/LoginButton.tsx | 25 ------ src/_components/auth/LoginForm.tsx | 86 ------------------- .../auth/handler/KaKaoLoginHandler.tsx | 61 ------------- src/_components/auth/handler/LoginLoading.tsx | 32 ------- .../{ => register}/RegisterConfirmModal.tsx | 0 .../auth/register/RegisterSetup.tsx | 37 ++++++++ .../auth/register/agreement/Agreement.tsx} | 8 +- .../agreement/AgreementForm.tsx} | 4 +- .../auth/register/nickname/Nickname.tsx | 20 +++++ .../{ => register/nickname}/NicknameForm.tsx | 14 +-- .../auth/register/userInfo/UserInfo.tsx} | 59 ++----------- .../userInfo/UserInfoText.tsx} | 2 +- .../notification/NotificationProvider.tsx | 4 +- src/_components/user/EditMyInfo.tsx | 4 +- src/_store/register.ts | 7 ++ src/_utils/hooks/useFunnel.tsx | 30 +++++++ src/app/(view)/(auth)/login/redirect/page.tsx | 23 +++++ .../document/[id]/page.tsx | 0 .../(auth)/{register => sign-up}/layout.tsx | 0 .../{register => sign-up}/name/page.tsx | 4 +- .../(view)/(auth)/sign-up/redirect/page.tsx | 73 ++++++++++++++++ src/app/api/axios.ts | 10 ++- src/app/api/requests.ts | 6 +- 26 files changed, 238 insertions(+), 289 deletions(-) rename src/{_components/auth => _common}/GenderForm.tsx (100%) rename src/{_components/auth => _common}/PreferredAlcoholForm.tsx (100%) delete mode 100644 src/_components/auth/LoginButton.tsx delete mode 100644 src/_components/auth/LoginForm.tsx delete mode 100644 src/_components/auth/handler/KaKaoLoginHandler.tsx delete mode 100644 src/_components/auth/handler/LoginLoading.tsx rename src/_components/auth/{ => register}/RegisterConfirmModal.tsx (100%) create mode 100644 src/_components/auth/register/RegisterSetup.tsx rename src/{app/(view)/(auth)/register/agreement/page.tsx => _components/auth/register/agreement/Agreement.tsx} (50%) rename src/_components/auth/{RegisterAgreementForm.tsx => register/agreement/AgreementForm.tsx} (98%) create mode 100644 src/_components/auth/register/nickname/Nickname.tsx rename src/_components/auth/{ => register/nickname}/NicknameForm.tsx (94%) rename src/{app/(view)/(auth)/register/details/page.tsx => _components/auth/register/userInfo/UserInfo.tsx} (66%) rename src/_components/auth/{DetailsText.tsx => register/userInfo/UserInfoText.tsx} (97%) create mode 100644 src/_utils/hooks/useFunnel.tsx create mode 100644 src/app/(view)/(auth)/login/redirect/page.tsx rename src/app/(view)/(auth)/{register => sign-up}/document/[id]/page.tsx (100%) rename src/app/(view)/(auth)/{register => sign-up}/layout.tsx (100%) rename src/app/(view)/(auth)/{register => sign-up}/name/page.tsx (87%) create mode 100644 src/app/(view)/(auth)/sign-up/redirect/page.tsx diff --git a/src/_components/auth/GenderForm.tsx b/src/_common/GenderForm.tsx similarity index 100% rename from src/_components/auth/GenderForm.tsx rename to src/_common/GenderForm.tsx diff --git a/src/_components/auth/PreferredAlcoholForm.tsx b/src/_common/PreferredAlcoholForm.tsx similarity index 100% rename from src/_components/auth/PreferredAlcoholForm.tsx rename to src/_common/PreferredAlcoholForm.tsx diff --git a/src/_common/TopHeader.tsx b/src/_common/TopHeader.tsx index 58c66c49..c5afe107 100644 --- a/src/_common/TopHeader.tsx +++ b/src/_common/TopHeader.tsx @@ -10,22 +10,28 @@ interface ITopHeader { step: number; rest: number; onClick?: (event: React.MouseEvent) => void; + goBack?: () => void; } -export default function TopHeader({ title, step, rest, onClick }: ITopHeader) { +export default function TopHeader({ + title, + step, + rest, + onClick, + goBack, +}: ITopHeader) { const router = useRouter(); const totalSteps = step + rest; // 전체 단계 수 const pathname = usePathname(); const handleClick = (event: React.MouseEvent) => { - if (onClick) { - event.preventDefault(); - onClick(event); - } else { + if (goBack) { if (step === 1) { router.replace("/"); + } else if (step === 3 && onClick) { + onClick(event); } else { - router.back(); + goBack(); } } }; diff --git a/src/_components/auth/LoginButton.tsx b/src/_components/auth/LoginButton.tsx deleted file mode 100644 index e9cb1aa6..00000000 --- a/src/_components/auth/LoginButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -interface ILoginButton { - children?: React.ReactNode; - buttonType: string; - handleButton: () => void; -} - -type buttonType = Record; - -export default function LoginButton({ - children, - buttonType, - handleButton, -}: ILoginButton) { - const buttonColor: buttonType = { - kakao: - "flex justify-center items-center rounded-[6px] w-[91%] mx-[4%] py-[13px] bg-[#EDD923] relative", - google: - "flex justify-center items-center rounded-[6px] w-[91%] mx-[4%] py-[13px] bg-white border-[1px] border-cool-grayscale-300 relative", - }; - return ( - - ); -} diff --git a/src/_components/auth/LoginForm.tsx b/src/_components/auth/LoginForm.tsx deleted file mode 100644 index 8f2b33e3..00000000 --- a/src/_components/auth/LoginForm.tsx +++ /dev/null @@ -1,86 +0,0 @@ -"use client"; - -import Image from "next/image"; -import { useEffect, useState } from "react"; -import LoginButton from "@/_components/auth/LoginButton"; -import TopHeader from "@/_common/TopHeader"; - -export default function LoginForm() { - const kakaoSocialLoginLink = `https://kauth.kakao.com/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_KAKAO_API_KEY}&redirect_uri=${process.env.NEXT_PUBLIC_KAKAO_LOGIN_REDIRECT_URI}&response_type=code`; - const googleSocialLoginLink = `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=${process.env.NEXT_PUBLIC_GOOGLE_API_KEY}&scope=email&redirect_uri=${process.env.NEXT_PUBLIC_GOOGLE_LOGIN_REDIRECT_URI}`; - const [kakaoRecentLogin, setKakaoRecentLogin] = useState(false); - const [googleRecentLogin, setGoogleRecentLogin] = useState(false); - - const handleKakaoLogin = () => { - window.location.href = kakaoSocialLoginLink; - }; - - const handleGoogleLogin = () => { - window.location.href = googleSocialLoginLink; - }; - - useEffect(() => { - if (localStorage.getItem("recentLogin") === "KAKAO") - setKakaoRecentLogin(true); - else if (localStorage.getItem("recentLogin") === "GOOGLE") - setGoogleRecentLogin(true); - }, []); - - return ( -
- -
- 주라벨 메인로고 -

- 주라벨에 오신 것을 환영해요! -

-
- - {kakaoRecentLogin && ( -
- 최근 로그인 -
- )} - - 카카오 소셜 로그인 버튼 -

카카오로 시작하기

-
- - -
- {googleRecentLogin && ( - 최근 로그인 - )} -
- 구글 소셜 로그인 버튼 -

구글로 시작하기

-
-
- ); -} diff --git a/src/_components/auth/handler/KaKaoLoginHandler.tsx b/src/_components/auth/handler/KaKaoLoginHandler.tsx deleted file mode 100644 index 6703b471..00000000 --- a/src/_components/auth/handler/KaKaoLoginHandler.tsx +++ /dev/null @@ -1,61 +0,0 @@ -"use client"; - -import { isAxiosError } from "axios"; -import { useRouter, useSearchParams } from "next/navigation"; -import { useEffect } 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 { useRegisterStore } from "@/_store/register"; -import LoginLoading from "./LoginLoading"; - -export default function KaKaoLoginHandler() { - const { setEmail, setProvider, setProviderId } = useRegisterStore(); - - const searchParams = useSearchParams(); - - const [cookies, setCookie, removeCookie] = useCookies(["accessToken"]); - const authCode = searchParams.get("code"); - const router = useRouter(); - - useEffect(() => { - if (authCode) { - const loginHandler = async () => { - try { - const response = await instance.post(requests.postKakaoLogin, { - code: authCode, - provider: "KAKAO", - redirectUri: process.env.NEXT_PUBLIC_KAKAO_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) { - if (isAxiosError(error) && error.response?.status === 400) { - toast("탈퇴한 회원입니다."); - } else { - console.error(error); - toast("비정상 접근입니다."); - } - router.push("/"); - } - }; - loginHandler(); - } - }, [authCode]); - - return ; -} diff --git a/src/_components/auth/handler/LoginLoading.tsx b/src/_components/auth/handler/LoginLoading.tsx deleted file mode 100644 index 7c6c174e..00000000 --- a/src/_components/auth/handler/LoginLoading.tsx +++ /dev/null @@ -1,32 +0,0 @@ -"use client"; - -import { motion } from "framer-motion"; - -export default function LoginLoading() { - return ( - - - 가장 쉽고, 간편하게 우리술과 가까워지는 방법 - - - - "주라벨" - - - ); -} diff --git a/src/_components/auth/RegisterConfirmModal.tsx b/src/_components/auth/register/RegisterConfirmModal.tsx similarity index 100% rename from src/_components/auth/RegisterConfirmModal.tsx rename to src/_components/auth/register/RegisterConfirmModal.tsx diff --git a/src/_components/auth/register/RegisterSetup.tsx b/src/_components/auth/register/RegisterSetup.tsx new file mode 100644 index 00000000..45579842 --- /dev/null +++ b/src/_components/auth/register/RegisterSetup.tsx @@ -0,0 +1,37 @@ +import { FunnelProps, StepProps } from "@/_utils/hooks/useFunnel"; +import Agreement from "./agreement/Agreement"; +import NickName from "./nickname/Nickname"; +import UserInfo from "./userInfo/UserInfo"; + +interface ProfileSetupInterface { + steps: string[]; + onNext: (nextStep: string) => void; + Funnel: React.ComponentType; + Step: React.ComponentType; +} + +export default function RegisterSetup({ + steps, + onNext, + Funnel, + Step, +}: ProfileSetupInterface) { + return ( + <> + + {/* 약관동의 */} + + onNext(steps[1])} /> + + {/* 닉네임 설정 */} + + onNext(steps[2])} /> + + {/* 가입정보 설정 */} + + + + + + ); +} diff --git a/src/app/(view)/(auth)/register/agreement/page.tsx b/src/_components/auth/register/agreement/Agreement.tsx similarity index 50% rename from src/app/(view)/(auth)/register/agreement/page.tsx rename to src/_components/auth/register/agreement/Agreement.tsx index a18944c3..78879eb5 100644 --- a/src/app/(view)/(auth)/register/agreement/page.tsx +++ b/src/_components/auth/register/agreement/Agreement.tsx @@ -1,16 +1,14 @@ -import RegisterAgreementForm from "@/_components/auth/RegisterAgreementForm"; -import TopHeader from "@/_common/TopHeader"; +import AgreementForm from "./AgreementForm"; -export default function Page() { +export default function Agreement({ onNext }: { onNext: () => void }) { return ( <> -

주라벨을 사용하려면
아래에 대한 약관 동의가 필요해요

- + onNext()} /> ); } diff --git a/src/_components/auth/RegisterAgreementForm.tsx b/src/_components/auth/register/agreement/AgreementForm.tsx similarity index 98% rename from src/_components/auth/RegisterAgreementForm.tsx rename to src/_components/auth/register/agreement/AgreementForm.tsx index ef24cc10..b729d06b 100644 --- a/src/_components/auth/RegisterAgreementForm.tsx +++ b/src/_components/auth/register/agreement/AgreementForm.tsx @@ -14,7 +14,7 @@ import { termsMapping, } from "@/_types/yup/yupRegister"; -export default function RegisterAgreementForm() { +export default function AgreementForm({ onNext }: { onNext: () => void }) { const router = useRouter(); const { data: terms, @@ -55,6 +55,7 @@ export default function RegisterAgreementForm() { setServiceAgree(getValues("serviceAgree")); setPrivateInformationAgree(getValues("privateInformationAgree")); setMarketingAgree(getValues("marketingAgree")); + onNext(); }; // 리렌더링 시 zustand 상태 반영 @@ -163,7 +164,6 @@ export default function RegisterAgreementForm() { )} void }) { + return ( + <> +
+

닉네임을 설정해주세요.

+
+ 닉네임은 최소 2자, 최대 8자를 입력할 수 있어요. + 띄어쓰기 및 특수문자는 사용할 수 없어요. +
+ + 닉네임은 추후 변경이 가능해요! + +
+ onNext()} /> + + ); +} diff --git a/src/_components/auth/NicknameForm.tsx b/src/_components/auth/register/nickname/NicknameForm.tsx similarity index 94% rename from src/_components/auth/NicknameForm.tsx rename to src/_components/auth/register/nickname/NicknameForm.tsx index d1a6fc1d..e7d8dd15 100644 --- a/src/_components/auth/NicknameForm.tsx +++ b/src/_components/auth/register/nickname/NicknameForm.tsx @@ -12,7 +12,7 @@ import { NicknameUserFormValues, } from "@/_types/yup/yupRegister"; -export default function NicknameForm() { +export default function NicknameForm({ onNext }: { onNext: () => void }) { const { setNickname } = useRegisterStore(); const [nicknamePass, setNicknamePass] = useState(""); const [enableButton, setEnableButton] = useState(false); @@ -129,9 +129,13 @@ export default function NicknameForm() { [isSubmitting, clearErrors, setError], ); + // const saveNicknameData = useCallback(() => { + // setNickname(getValues("nickname")); + // }, [getValues, setNickname]); const saveNicknameData = useCallback(() => { setNickname(getValues("nickname")); - }, [getValues, setNickname]); + onNext(); + }, [getValues, setNickname, onNext]); useEffect(() => { clearErrors("nickname"); @@ -179,11 +183,7 @@ export default function NicknameForm() { > 중복 검사 - + 다음 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..66c2400e 100644 --- a/src/_store/register.ts +++ b/src/_store/register.ts @@ -23,6 +23,7 @@ interface IRegisterUserState { genderCheck: boolean; preferredAlcoholType: number[]; memberId: number; + signUpToken: string; setAllAgree: (value: boolean) => void; setServiceAgree: (value: boolean) => void; setPrivateInformationAgree: (value: boolean) => void; @@ -35,6 +36,7 @@ interface IRegisterUserState { setGendercheck: (value: boolean) => void; setPreferredAlcoholType: (value: number[]) => void; setMemberId: (value: number) => void; + setSignUpToken: (value: string) => void; } export const useRegisterStore = create( @@ -61,6 +63,7 @@ export const useRegisterStore = create( genderCheck: false, preferredAlcoholType: [], memberId: 0, + signUpToken: "", setAllAgree: (value: boolean) => set((state) => ({ ...state, allAgree: value })), setServiceAgree: (value: boolean) => @@ -104,9 +107,13 @@ export const useRegisterStore = create( setMemberId: (value: number) => { set((state) => ({ ...state, memberId: value })); }, + setSignUpToken: (value: string) => { + set((state) => ({ ...state, signUpToken: value })); + }, }), { 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/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)/register/name/page.tsx b/src/app/(view)/(auth)/sign-up/name/page.tsx similarity index 87% rename from src/app/(view)/(auth)/register/name/page.tsx rename to src/app/(view)/(auth)/sign-up/name/page.tsx index eba2c583..dfc5fc86 100644 --- a/src/app/(view)/(auth)/register/name/page.tsx +++ b/src/app/(view)/(auth)/sign-up/name/page.tsx @@ -1,4 +1,4 @@ -import NicknameForm from "@/_components/auth/NicknameForm"; +import NicknameForm from "@/_components/auth/register/nickname/NicknameForm"; import TopHeader from "@/_common/TopHeader"; export default function Page() { @@ -15,7 +15,7 @@ export default function Page() { 닉네임은 추후 변경이 가능해요! - + {/* */} ); } 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..9f1c7ada --- /dev/null +++ b/src/app/(view)/(auth)/sign-up/redirect/page.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { 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]); + } + }; + + return ( + <> + handleBack()} + onClick={handleRegisterCancelModalOpen} + /> + + {registerCancelModalOpen && ( + + )} + + ); +} diff --git a/src/app/api/axios.ts b/src/app/api/axios.ts index be451ba2..e2675d43 100644 --- a/src/app/api/axios.ts +++ b/src/app/api/axios.ts @@ -4,6 +4,9 @@ import nookies from "nookies"; export const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_JUULABEL_API_URL, timeout: 20000, + withCredentials: true, + xsrfCookieName: "csrfToken", + xsrfHeaderName: "X-CSRF-TOKEN", }); instance.interceptors.request.use( @@ -11,10 +14,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 +31,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 9967ffa1..fd264443 100644 --- a/src/app/api/requests.ts +++ b/src/app/api/requests.ts @@ -1,8 +1,8 @@ const requests = { getAlcoholTypes: `/v1/api/alcohols/types`, - postSignUp: `/v1/api/members/sign-up`, - postKakaoLogin: `/v1/api/auth/login/kakao`, - postGoogleLogin: `/v1/api/auth/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?`, From 55fe703f6dc90e8fdce6f8b5b0f45945fd72d896 Mon Sep 17 00:00:00 2001 From: ohamin26 Date: Sat, 7 Jun 2025 17:18:36 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/register/agreement/AgreementForm.tsx | 10 ++++-- .../agreement/AgreementformtSkeleton.tsx | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/_components/auth/register/agreement/AgreementformtSkeleton.tsx diff --git a/src/_components/auth/register/agreement/AgreementForm.tsx b/src/_components/auth/register/agreement/AgreementForm.tsx index b729d06b..9bbe414a 100644 --- a/src/_components/auth/register/agreement/AgreementForm.tsx +++ b/src/_components/auth/register/agreement/AgreementForm.tsx @@ -13,6 +13,7 @@ import { AgreementUserFormValues, termsMapping, } from "@/_types/yup/yupRegister"; +import AgreementFormSkeleton from "./AgreementformtSkeleton"; export default function AgreementForm({ onNext }: { onNext: () => void }) { const router = useRouter(); @@ -100,8 +101,13 @@ export default function AgreementForm({ onNext }: { onNext: () => void }) { setValue, ]); - if (isLoading) return
Loading...
; - if (error) return
Error : {error.message}
; + if (isLoading) { + return ; + } + if (error) { + router.push("/error"); + return null; + } return (
diff --git a/src/_components/auth/register/agreement/AgreementformtSkeleton.tsx b/src/_components/auth/register/agreement/AgreementformtSkeleton.tsx new file mode 100644 index 00000000..cdc01ac0 --- /dev/null +++ b/src/_components/auth/register/agreement/AgreementformtSkeleton.tsx @@ -0,0 +1,32 @@ +"use client"; + +import Skeleton from "react-loading-skeleton"; +import "react-loading-skeleton/dist/skeleton.css"; + +export default function AgreementFormSkeleton() { + return ( +
+
+ + +
+ + {[...Array(3)].map((_, idx) => ( +
+
+ + +
+ +
+ ))} + +
+ +
+
+ ); +} From 3ad11835f8f514a8cfe59660fdb91a5d42ad5514 Mon Sep 17 00:00:00 2001 From: ohamin26 Date: Sat, 7 Jun 2025 17:27:46 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20=EC=8B=9C=20?= =?UTF-8?q?alert=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(view)/(auth)/sign-up/redirect/page.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/app/(view)/(auth)/sign-up/redirect/page.tsx b/src/app/(view)/(auth)/sign-up/redirect/page.tsx index 9f1c7ada..4161b8ad 100644 --- a/src/app/(view)/(auth)/sign-up/redirect/page.tsx +++ b/src/app/(view)/(auth)/sign-up/redirect/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useRouter } from "next/navigation"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import RegisterSetup from "@/_components/auth/register/RegisterSetup"; import ConfirmModal from "@/_common/ConfirmModal"; import TopHeader from "@/_common/TopHeader"; @@ -43,6 +43,18 @@ export default function Page() { } }; + useEffect(() => { + const handleBeforeUnload = (event: BeforeUnloadEvent) => { + event.preventDefault(); + }; + + window.addEventListener("beforeunload", handleBeforeUnload); + + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, []); + return ( <> Date: Sat, 7 Jun 2025 17:37:48 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/_store/register.ts | 11 ----------- src/app/(view)/(auth)/sign-up/name/page.tsx | 21 --------------------- src/app/api/axios.ts | 2 -- 3 files changed, 34 deletions(-) delete mode 100644 src/app/(view)/(auth)/sign-up/name/page.tsx diff --git a/src/_store/register.ts b/src/_store/register.ts index 66c2400e..1145dd5c 100644 --- a/src/_store/register.ts +++ b/src/_store/register.ts @@ -18,12 +18,10 @@ interface IRegisterUserState { nickname: string; email: string; provider: string; - providerId: string; gender: string; genderCheck: boolean; preferredAlcoholType: number[]; memberId: number; - signUpToken: string; setAllAgree: (value: boolean) => void; setServiceAgree: (value: boolean) => void; setPrivateInformationAgree: (value: boolean) => void; @@ -31,12 +29,10 @@ 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; setMemberId: (value: number) => void; - setSignUpToken: (value: string) => void; } export const useRegisterStore = create( @@ -58,12 +54,10 @@ export const useRegisterStore = create( nickname: "", email: "", provider: "", - providerId: "", gender: "", genderCheck: false, preferredAlcoholType: [], memberId: 0, - signUpToken: "", setAllAgree: (value: boolean) => set((state) => ({ ...state, allAgree: value })), setServiceAgree: (value: boolean) => @@ -95,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,9 +99,6 @@ export const useRegisterStore = create( setMemberId: (value: number) => { set((state) => ({ ...state, memberId: value })); }, - setSignUpToken: (value: string) => { - set((state) => ({ ...state, signUpToken: value })); - }, }), { name: "userRegisterStorage", diff --git a/src/app/(view)/(auth)/sign-up/name/page.tsx b/src/app/(view)/(auth)/sign-up/name/page.tsx deleted file mode 100644 index dfc5fc86..00000000 --- a/src/app/(view)/(auth)/sign-up/name/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import NicknameForm from "@/_components/auth/register/nickname/NicknameForm"; -import TopHeader from "@/_common/TopHeader"; - -export default function Page() { - return ( - <> - -
-

닉네임을 설정해주세요.

-
- 닉네임은 최소 2자, 최대 8자를 입력할 수 있어요. - 띄어쓰기 및 특수문자는 사용할 수 없어요. -
- - 닉네임은 추후 변경이 가능해요! - -
- {/* */} - - ); -} diff --git a/src/app/api/axios.ts b/src/app/api/axios.ts index e2675d43..2c75ed4e 100644 --- a/src/app/api/axios.ts +++ b/src/app/api/axios.ts @@ -5,8 +5,6 @@ export const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_JUULABEL_API_URL, timeout: 20000, withCredentials: true, - xsrfCookieName: "csrfToken", - xsrfHeaderName: "X-CSRF-TOKEN", }); instance.interceptors.request.use(