From 815a82399431f3e92886b9f27946426c36c5bd02 Mon Sep 17 00:00:00 2001 From: k-impossible Date: Mon, 14 Apr 2025 18:54:12 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[#164]fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/user-check-action.ts | 48 +++++----- src/actions/user-signin-action.ts | 60 ++++++------- src/actions/user-signup-action.ts | 41 ++++----- .../[tab]/_components/ReviewListParent.tsx | 2 +- src/app/layout.tsx | 10 +++ src/app/login/_components/LoginForm.tsx | 89 ++++++++++++++++--- src/components/layout/ReviewCardItem.tsx | 2 +- 7 files changed, 158 insertions(+), 94 deletions(-) diff --git a/src/actions/user-check-action.ts b/src/actions/user-check-action.ts index 47e749a1..8474fbd7 100644 --- a/src/actions/user-check-action.ts +++ b/src/actions/user-check-action.ts @@ -6,38 +6,36 @@ import { getCookieOfToken } from "@/utils/cookieToken"; const userCheckAction = async () => { const TOKEN = await getCookieOfToken(); - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auths/user`, - { - method: "GET", - headers: { - Authorization: `Bearer ${TOKEN}`, - }, + console.log("TOKEN", TOKEN); + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auths/user`, + { + method: "GET", + headers: { + Authorization: `Bearer ${TOKEN}`, }, - ); + }, + ); - // 유저 정보 확인 실패 - if (!response.ok) { - throw new Error(await response.text()); - } - - // 유저 정보 확인 성공 시 image null 처리 - const resUser: IUser = await response.json(); - resUser.image = resUser.image || ""; - - return { - status: true, - user: resUser, - error: "", - }; - } catch (err) { + // 유저 정보 확인 실패 + if (!response.ok) { + // throw new Error(await response.text()); return { status: false, user: null, - error: `${(err as Error).message}`, + error: await response.text(), }; } + + // 유저 정보 확인 성공 시 image null 처리 + const resUser: IUser = await response.json(); + resUser.image = resUser.image || ""; + + return { + status: true, + user: resUser, + error: "", + }; }; export default userCheckAction; diff --git a/src/actions/user-signin-action.ts b/src/actions/user-signin-action.ts index 37db9098..6835f015 100644 --- a/src/actions/user-signin-action.ts +++ b/src/actions/user-signin-action.ts @@ -11,48 +11,38 @@ const userSignInAction = async (_: any, formData: FormData) => { const userData = { email, password }; - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auths/signin`, - { - method: "POST", - body: JSON.stringify(userData), - headers: { - "Content-Type": "application/json", - }, + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auths/signin`, + { + method: "POST", + body: JSON.stringify(userData), + headers: { + "Content-Type": "application/json", }, - ); + }, + ); - // 로그인 실패 - if (!response.ok) { - throw new Error(await response.text()); - } - - // 로그인 성공 시 유저 정보 확인 - const chkResult = await userCheckAction(); - - // 유저 정보 확인 실패 - if (!chkResult.status) { - throw new Error(chkResult.error); - } - - // 성공시 쿠키에 토큰 저장 - const res = await response.json(); - await setCookieOfToken(res.token); - - // 유저 정보 반환 - return { - status: true, - error: "", - user: chkResult.user, - }; - } catch (err) { + // 로그인 실패 + if (!response.ok) { + // throw new Error(await response.text()); return { status: false, - error: `${(err as Error).message}`, + error: await response.text(), user: null, }; } + + // 성공시 쿠키에 토큰 저장 + const res = await response.json(); + await setCookieOfToken(res.token); + + await new Promise(resolve => setTimeout(resolve, 500)); + + // 로그인 성공 시 유저 정보 확인 + const state = await userCheckAction(); + + console.log(state); + return state; }; export default userSignInAction; diff --git a/src/actions/user-signup-action.ts b/src/actions/user-signup-action.ts index 3e79c3aa..7ff46a6b 100644 --- a/src/actions/user-signup-action.ts +++ b/src/actions/user-signup-action.ts @@ -9,34 +9,31 @@ const userSignUpAction = async (_: any, formData: FormData) => { const userData = { email, name, companyName, password }; - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auths/signup`, - { - method: "POST", - body: JSON.stringify(userData), - headers: { - "Content-Type": "application/json", - }, + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auths/signup`, + { + method: "POST", + body: JSON.stringify(userData), + headers: { + "Content-Type": "application/json", }, - ); + }, + ); - // 회원가입 실패 - if (!response.ok) { - throw new Error(await response.text()); - } - - // 회원가입 성공 - return { - status: true, - error: "", - }; - } catch (err) { + // 회원가입 실패 + if (!response.ok) { + // throw new Error(await response.text()); return { status: false, - error: `${(err as Error).message}`, + error: await response.text(), }; } + + // 회원가입 성공 + return { + status: true, + error: "", + }; }; export default userSignUpAction; diff --git a/src/app/all-reviews/[tab]/_components/ReviewListParent.tsx b/src/app/all-reviews/[tab]/_components/ReviewListParent.tsx index 6d62969b..08e334e0 100644 --- a/src/app/all-reviews/[tab]/_components/ReviewListParent.tsx +++ b/src/app/all-reviews/[tab]/_components/ReviewListParent.tsx @@ -54,7 +54,7 @@ const ReviewListParent = () => {
{isFetchingNextPage && ( -
+
)} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d46a53a1..542d0fbd 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -21,6 +21,16 @@ export default function RootLayout({ }>) { return ( + + + +
diff --git a/src/app/login/_components/LoginForm.tsx b/src/app/login/_components/LoginForm.tsx index 0fb659db..35cac9b4 100644 --- a/src/app/login/_components/LoginForm.tsx +++ b/src/app/login/_components/LoginForm.tsx @@ -3,7 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; -import { useFormState } from "react-dom"; +// import { useFormState } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -18,6 +18,7 @@ import useFavorite from "@/hooks/useFavorite"; import { useModal } from "@/hooks/useModal"; import { LoginFormSchema } from "@/schemas/loginJoinSchema"; import useUserStore from "@/stores/useUserStore"; +import { IUser } from "@/types/user"; import { IRegisterWithValidation, @@ -41,17 +42,59 @@ const LoginForm = () => { const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); + const [submitAttempted, setSubmitAttempted] = useState(false); // 추가: 폼 제출 시도 추적 + const [alertMessage, setAlertMessage] = useState(""); - const [state, formAction] = useFormState(userSignInAction, null); + // const [state, formAction] = useFormState(userSignInAction, { + // status: false, + // error: "", + // user: null, + // }); + + const [authState, setAuthState] = useState< + | { status: boolean; error: string; user: null } + | { status: boolean; error: string; user: IUser } + >({ + status: false, + error: "", + user: null, + }); const { setUserState } = useUserStore(); const { isOpen, onClose, onOpen } = useModal(); const { setFavoriteInitValue } = useFavorite(); + // 디버깅용 로거 추가 + useEffect(() => { + console.log("isSubmitting 변경:", isSubmitting); + console.log("submitAttempted 변경:", submitAttempted); + }, [isSubmitting, submitAttempted]); + useEffect(() => { - if (state && !state.status && !state.user) { - const error: TErrorMsg = JSON.parse(state.error); + console.log("authState 변경:", authState); + + // 폼 제출 시도가 없었다면 처리 건너뛰기 + if (!submitAttempted) { + console.log("폼 제출 시도 없음, 처리 건너뜀"); + return; + } + + // 로딩 중이 아니면서 초기 상태라면 처리하지 않음 + if ( + !isSubmitting && + authState.error === "" && + !authState.status && + !authState.user + ) { + console.log("로딩 중이 아님 & 초기 상태, 처리 건너뜀"); + return; + } + + console.log("상태 처리 시작"); + + if (!authState.status && !authState.user && authState.error) { + const error: TErrorMsg = JSON.parse(authState.error); if (error.code === "SERVER_ERROR" || error.code === "INVALID_TOKEN") { setAlertMessage(error.message); @@ -66,28 +109,54 @@ const LoginForm = () => { { shouldFocus: true }, ); } + console.log("error", error); setIsSubmitting(false); - } else if (state && state.status && state.user) { - setUserState(state.user); + } else if (authState.status && authState.user) { + setUserState(authState.user); // 로그인 유저의 즐겨찾기 목록 가져오기 - setFavoriteInitValue(state.user?.email); + setFavoriteInitValue(authState.user?.email); router.replace("/"); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state, setError, router, setUserState, onOpen]); + }, [authState, isSubmitting, submitAttempted]); - const onSubmit = handleSubmit(data => { + const onSubmit = handleSubmit(async data => { setIsSubmitting(true); + setSubmitAttempted(true); + const formData = new FormData(); Object.entries(data).forEach(([key, value]) => { formData.append(key, value as string); }); - formAction(formData); + try { + console.log("서버 액션 호출 시작"); + // setTimeout 대신 Promise 사용 + await userSignInAction(null, formData).then(result => { + console.log("서버 액션 결과:", result); + + // 상태 업데이트를 Promise로 처리 + Promise.resolve().then(() => { + console.log("상태 업데이트 시도", result); + setAuthState(result); // 직접 설정 + }); + }); + } catch (error) { + console.error("서버 액션 오류:", error); + setAuthState({ + status: false, + error: JSON.stringify({ + code: "CLIENT_ERROR", + message: "로그인 처리 중 오류가 발생했습니다.", + }), + user: null, + }); + setIsSubmitting(false); + } }); const registerWithValidation = ( diff --git a/src/components/layout/ReviewCardItem.tsx b/src/components/layout/ReviewCardItem.tsx index d48c0ca7..3d7d835d 100644 --- a/src/components/layout/ReviewCardItem.tsx +++ b/src/components/layout/ReviewCardItem.tsx @@ -30,7 +30,7 @@ const ReviewCardItem = memo(({ review }: TReviewCardItemProps) => { href={`/gathering/detail/${Gathering.id}`} style={{ display: "contents" }} > -
+
{Gathering.name} Date: Mon, 14 Apr 2025 20:46:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[#164]fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/user-check-action.ts | 46 ++++++------- src/actions/user-signin-action.ts | 60 ++++++++++------- src/actions/user-signup-action.ts | 39 +++++------ src/app/login/_components/LoginForm.tsx | 89 +++---------------------- 4 files changed, 88 insertions(+), 146 deletions(-) diff --git a/src/actions/user-check-action.ts b/src/actions/user-check-action.ts index 8474fbd7..f23a1319 100644 --- a/src/actions/user-check-action.ts +++ b/src/actions/user-check-action.ts @@ -6,36 +6,36 @@ import { getCookieOfToken } from "@/utils/cookieToken"; const userCheckAction = async () => { const TOKEN = await getCookieOfToken(); - console.log("TOKEN", TOKEN); - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auths/user`, - { - method: "GET", - headers: { - Authorization: `Bearer ${TOKEN}`, + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auths/user`, + { + method: "GET", + headers: { + Authorization: `Bearer ${TOKEN}`, + }, }, - }, - ); + ); - // 유저 정보 확인 실패 - if (!response.ok) { - // throw new Error(await response.text()); + if (!response.ok) { + throw new Error(await response.text()); + } + + // 유저 정보 받아와서 image null 처리 + const resUser: IUser = await response.json(); + resUser.image = resUser.image || ""; + return { + status: true, + user: resUser, + error: "", + }; + } catch (err) { return { status: false, user: null, - error: await response.text(), + error: `${(err as Error).message}`, }; } - - // 유저 정보 확인 성공 시 image null 처리 - const resUser: IUser = await response.json(); - resUser.image = resUser.image || ""; - - return { - status: true, - user: resUser, - error: "", - }; }; export default userCheckAction; diff --git a/src/actions/user-signin-action.ts b/src/actions/user-signin-action.ts index 6835f015..036a42f8 100644 --- a/src/actions/user-signin-action.ts +++ b/src/actions/user-signin-action.ts @@ -11,38 +11,48 @@ const userSignInAction = async (_: any, formData: FormData) => { const userData = { email, password }; - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auths/signin`, - { - method: "POST", - body: JSON.stringify(userData), - headers: { - "Content-Type": "application/json", + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auths/signin`, + { + method: "POST", + body: JSON.stringify(userData), + headers: { + "Content-Type": "application/json", + }, }, - }, - ); + ); - // 로그인 실패 - if (!response.ok) { - // throw new Error(await response.text()); + // 토큰 받아오기 실패 + if (!response.ok) { + throw new Error(await response.text()); + } + + // 토큰을 받아오면 쿠키에 저장 + const res = await response.json(); + await setCookieOfToken(res.token); + + // 유저 정보 확인 + const chkResult = await userCheckAction(); + + // 유저 정보 확인 성공 + if (!chkResult.status) { + throw new Error(chkResult.error); + } + + // 성공시 유저 정보 반환 + return { + status: true, + error: "", + user: chkResult.user, + }; + } catch (err) { return { status: false, - error: await response.text(), + error: `${(err as Error).message}`, user: null, }; } - - // 성공시 쿠키에 토큰 저장 - const res = await response.json(); - await setCookieOfToken(res.token); - - await new Promise(resolve => setTimeout(resolve, 500)); - - // 로그인 성공 시 유저 정보 확인 - const state = await userCheckAction(); - - console.log(state); - return state; }; export default userSignInAction; diff --git a/src/actions/user-signup-action.ts b/src/actions/user-signup-action.ts index 7ff46a6b..8ba92f26 100644 --- a/src/actions/user-signup-action.ts +++ b/src/actions/user-signup-action.ts @@ -9,31 +9,32 @@ const userSignUpAction = async (_: any, formData: FormData) => { const userData = { email, name, companyName, password }; - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auths/signup`, - { - method: "POST", - body: JSON.stringify(userData), - headers: { - "Content-Type": "application/json", + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auths/signup`, + { + method: "POST", + body: JSON.stringify(userData), + headers: { + "Content-Type": "application/json", + }, }, - }, - ); + ); - // 회원가입 실패 - if (!response.ok) { - // throw new Error(await response.text()); + if (!response.ok) { + throw new Error(await response.text()); + } + + return { + status: true, + error: "", + }; + } catch (err) { return { status: false, - error: await response.text(), + error: `${(err as Error).message}`, }; } - - // 회원가입 성공 - return { - status: true, - error: "", - }; }; export default userSignUpAction; diff --git a/src/app/login/_components/LoginForm.tsx b/src/app/login/_components/LoginForm.tsx index 35cac9b4..0fb659db 100644 --- a/src/app/login/_components/LoginForm.tsx +++ b/src/app/login/_components/LoginForm.tsx @@ -3,7 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; -// import { useFormState } from "react-dom"; +import { useFormState } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -18,7 +18,6 @@ import useFavorite from "@/hooks/useFavorite"; import { useModal } from "@/hooks/useModal"; import { LoginFormSchema } from "@/schemas/loginJoinSchema"; import useUserStore from "@/stores/useUserStore"; -import { IUser } from "@/types/user"; import { IRegisterWithValidation, @@ -42,59 +41,17 @@ const LoginForm = () => { const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); - const [submitAttempted, setSubmitAttempted] = useState(false); // 추가: 폼 제출 시도 추적 - const [alertMessage, setAlertMessage] = useState(""); - // const [state, formAction] = useFormState(userSignInAction, { - // status: false, - // error: "", - // user: null, - // }); - - const [authState, setAuthState] = useState< - | { status: boolean; error: string; user: null } - | { status: boolean; error: string; user: IUser } - >({ - status: false, - error: "", - user: null, - }); + const [state, formAction] = useFormState(userSignInAction, null); const { setUserState } = useUserStore(); const { isOpen, onClose, onOpen } = useModal(); const { setFavoriteInitValue } = useFavorite(); - // 디버깅용 로거 추가 - useEffect(() => { - console.log("isSubmitting 변경:", isSubmitting); - console.log("submitAttempted 변경:", submitAttempted); - }, [isSubmitting, submitAttempted]); - useEffect(() => { - console.log("authState 변경:", authState); - - // 폼 제출 시도가 없었다면 처리 건너뛰기 - if (!submitAttempted) { - console.log("폼 제출 시도 없음, 처리 건너뜀"); - return; - } - - // 로딩 중이 아니면서 초기 상태라면 처리하지 않음 - if ( - !isSubmitting && - authState.error === "" && - !authState.status && - !authState.user - ) { - console.log("로딩 중이 아님 & 초기 상태, 처리 건너뜀"); - return; - } - - console.log("상태 처리 시작"); - - if (!authState.status && !authState.user && authState.error) { - const error: TErrorMsg = JSON.parse(authState.error); + if (state && !state.status && !state.user) { + const error: TErrorMsg = JSON.parse(state.error); if (error.code === "SERVER_ERROR" || error.code === "INVALID_TOKEN") { setAlertMessage(error.message); @@ -109,54 +66,28 @@ const LoginForm = () => { { shouldFocus: true }, ); } - console.log("error", error); setIsSubmitting(false); - } else if (authState.status && authState.user) { - setUserState(authState.user); + } else if (state && state.status && state.user) { + setUserState(state.user); // 로그인 유저의 즐겨찾기 목록 가져오기 - setFavoriteInitValue(authState.user?.email); + setFavoriteInitValue(state.user?.email); router.replace("/"); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [authState, isSubmitting, submitAttempted]); + }, [state, setError, router, setUserState, onOpen]); - const onSubmit = handleSubmit(async data => { + const onSubmit = handleSubmit(data => { setIsSubmitting(true); - setSubmitAttempted(true); - const formData = new FormData(); Object.entries(data).forEach(([key, value]) => { formData.append(key, value as string); }); - try { - console.log("서버 액션 호출 시작"); - // setTimeout 대신 Promise 사용 - await userSignInAction(null, formData).then(result => { - console.log("서버 액션 결과:", result); - - // 상태 업데이트를 Promise로 처리 - Promise.resolve().then(() => { - console.log("상태 업데이트 시도", result); - setAuthState(result); // 직접 설정 - }); - }); - } catch (error) { - console.error("서버 액션 오류:", error); - setAuthState({ - status: false, - error: JSON.stringify({ - code: "CLIENT_ERROR", - message: "로그인 처리 중 오류가 발생했습니다.", - }), - user: null, - }); - setIsSubmitting(false); - } + formAction(formData); }); const registerWithValidation = ( From bb4544ccdd33281e646ff9965d28ecc7726f8225 Mon Sep 17 00:00:00 2001 From: k-impossible Date: Tue, 15 Apr 2025 02:33:47 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[#164]fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/user-check-action.ts | 7 ++----- src/actions/user-signin-action.ts | 9 ++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/actions/user-check-action.ts b/src/actions/user-check-action.ts index f23a1319..fbadd99b 100644 --- a/src/actions/user-check-action.ts +++ b/src/actions/user-check-action.ts @@ -1,18 +1,15 @@ "use server"; import { IUser } from "@/types/user"; -import { getCookieOfToken } from "@/utils/cookieToken"; - -const userCheckAction = async () => { - const TOKEN = await getCookieOfToken(); +const userCheckAction = async (token: string) => { try { const response = await fetch( `${process.env.NEXT_PUBLIC_API_URL}/auths/user`, { method: "GET", headers: { - Authorization: `Bearer ${TOKEN}`, + Authorization: `Bearer ${token}`, }, }, ); diff --git a/src/actions/user-signin-action.ts b/src/actions/user-signin-action.ts index 036a42f8..01fcb761 100644 --- a/src/actions/user-signin-action.ts +++ b/src/actions/user-signin-action.ts @@ -1,7 +1,5 @@ "use server"; -import { setCookieOfToken } from "@/utils/cookieToken"; - import userCheckAction from "./user-check-action"; // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars @@ -25,15 +23,16 @@ const userSignInAction = async (_: any, formData: FormData) => { // 토큰 받아오기 실패 if (!response.ok) { - throw new Error(await response.text()); + const errorText = await response.text(); + console.error("API 응답 오류:", errorText); + throw new Error(errorText); } // 토큰을 받아오면 쿠키에 저장 const res = await response.json(); - await setCookieOfToken(res.token); // 유저 정보 확인 - const chkResult = await userCheckAction(); + const chkResult = await userCheckAction(res.token); // 유저 정보 확인 성공 if (!chkResult.status) {