-
Notifications
You must be signed in to change notification settings - Fork 1
비밀번호 재설정 폼 제출 구조 리팩토링 #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,3 +34,4 @@ yarn-error.log* | |
| # typescript | ||
| *.tsbuildinfo | ||
| next-env.d.ts | ||
| .env | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| 'use server'; | ||
|
|
||
| import { ActionStatus } from '@/enums/ActionStatus'; | ||
|
|
||
| export const requestTokenValidation = async (token: string) => { | ||
| try { | ||
| // api 개발 완료되면 엔드 포인트 추가 예정 | ||
| const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ token }), | ||
| }); | ||
|
|
||
| return { status: ActionStatus.Success, fields: response.json() }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } catch (error) { | ||
| alert('토큰 확인 요망 👾'); | ||
| return { status: ActionStatus.Error, issues: [error] }; | ||
| } | ||
|
Comment on lines
+17
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 어떨 때 catch로 들어오는지 알고 쓰신 건 아닌 것 같아서, fetch의 Error Handling에 대해서 알아볼 필요가 있을 것 같아요. 그래서 response의 status값을 확인해서(주로 response.ok) 에러에 따라 throw를 던져주는 형태가 되어야 합니다. try {
const res = await fetch("~");
if (!res.ok) {
// 아니면 응답에서 확인한다면 json으로 serialize하고 status를 확인
if (res.status === "400") {
throw new Error("404 Error")
}
if (res.status === "500") {
throw new Error("500 Error")
}
throw new Error(res.status)
}
return res.json()
} catch (e) {
}
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 물론 이것도 Fetcher를 만들어서 따로 수정하면 되겠지만, 관련 내용은 알고 계시는 게 좋을 것 같아서~ |
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,12 @@ import Link from 'next/link'; | |
| import ResetPasswordForm from '../_forms/ResetPasswordForm'; | ||
| import { HeadingWithDescription } from '@/components/HeadingWithDescription/HeadingWithDescription'; | ||
| import { redirect } from 'next/navigation'; | ||
| import { requestTokenValidation } from './_actions/actions'; | ||
|
|
||
| export default async function Page({ params: { token } }: { params: { token: string } }) { | ||
| const response = await requestTokenValidation(token); | ||
| const { email } = await response.fields; | ||
|
Comment on lines
+9
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 처리가 안 되어 있어요. fetch 과정에서 에러가 발생하면 작성하신 코드상으론 fields가 존재하지 않으므로 undefined가 반환될 텐데, undefined에서 email을 구조분해 하려고 하니 오류가 발생하게 될 거에요 ^~^ |
||
|
|
||
| export default function Page() { | ||
| const handleSuccess = async () => { | ||
| 'use server'; | ||
|
|
||
|
|
@@ -21,7 +25,7 @@ export default function Page() { | |
|
|
||
| <HeadingWithDescription heading="비밀번호 재설정" description="변경할 비밀번호를 입력해주세요." /> | ||
|
|
||
| <ResetPasswordForm onSuccess={handleSuccess} /> | ||
| <ResetPasswordForm onSuccess={handleSuccess} email={email} /> | ||
| </section> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,10 +11,26 @@ import { Control, useForm } from 'react-hook-form'; | |
| import { z } from 'zod'; | ||
| import { resetPassword } from './ResetPasswordForm.action'; | ||
|
|
||
| const formSchema = z.object({ | ||
| password: z.string(), | ||
| passwordConfirm: z.string(), | ||
| }); | ||
| const passwordRegex = new RegExp(/^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,15}$/); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맨 마지막 정규식은 앞단 min()과 max()로 처리해주고 있는데, 그럼에도 필요한 정규식일까요~? |
||
|
|
||
| const formSchema = z | ||
| .object({ | ||
| password: z | ||
| .string() | ||
| .min(8, { message: '비밀번호는 최소 8자 이상이어야 합니다.' }) | ||
| .max(15, { message: '비밀번호는 최대 15자 이내이어야 합니다.' }) | ||
| .regex(passwordRegex, { message: '영문, 숫자, 특수문자 포함해서 머시기해라.' }), | ||
| passwordConfirm: z.string(), | ||
| }) | ||
| .superRefine(({ password, passwordConfirm }, ctx) => { | ||
| if (password !== passwordConfirm) { | ||
| ctx.addIssue({ | ||
| code: z.ZodIssueCode.custom, | ||
| message: '비밀번호가 일치하지 않네요.', | ||
| path: ['passwordConfirm'], | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| export type ResetPasswordRequest = z.infer<typeof formSchema>; | ||
|
|
||
|
|
@@ -25,9 +41,10 @@ const initialValues: ResetPasswordRequest = { | |
|
|
||
| type ResetPasswordFormProps = { | ||
| onSuccess: () => void; | ||
| email: string; | ||
| }; | ||
|
|
||
| export default function ResetPasswordForm({ onSuccess }: ResetPasswordFormProps) { | ||
| export default function ResetPasswordForm({ onSuccess, email }: ResetPasswordFormProps) { | ||
| const [isPending, startTransition] = useTransition(); | ||
|
|
||
| const [state, setState] = useState<FormState>({ | ||
|
|
@@ -52,6 +69,9 @@ export default function ResetPasswordForm({ onSuccess }: ResetPasswordFormProps) | |
|
|
||
| const formData = new FormData(formRef.current); | ||
|
|
||
| // input hidden 대신 email 값을 formData에 포함시켰어요. | ||
| formData.append('email', email); | ||
|
Comment on lines
+72
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 관련해서 디자인이 바뀔 것 같아서 input hidden으로 해놓는 게 좋을 것 같다는 거였는데, 제가 말씀을 못드렸네요😇 |
||
|
|
||
| setState({ | ||
| status: ActionStatus.Idle, | ||
| fields: { ...(Object.fromEntries(formData) as Record<string, string>) }, | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에는 Fetcher로 따로 빼서 Endpoint만 신경쓸 수 있게 해도 좋을 것 같아요