-
Notifications
You must be signed in to change notification settings - Fork 5
[feat, refactor] #177 ResetPasswordModal, OpenPasswordModal, Modal.tsx, useModalStore: 비밀번호 재설정 모달 구현 / Modal, useModalStore: form 제출 기능 확장 및 IIFE 지원 / 상태 구독 기반 API 호출 처리 #192
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
Changes from all commits
d9c55b0
32ef02b
ac18ce5
9db5472
c47aaf1
9bb4e6d
af0f46e
dbcdcc4
48f626a
7479f1e
41fa164
3c585b2
372717b
d6b21e3
734fa9e
f758015
395f1a8
205486a
0be972f
6c58ff8
90b1fc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| 'use client'; | ||
|
|
||
| import ResetPasswordModal from '@/components/common/Modal/content/ResetPasswordModal'; | ||
| import { postResetPasswordToEmail } from '@/lib/apis/user'; | ||
| import { ResetPasswordToEmailBody } from '@/lib/apis/user/type'; | ||
| import { useModalStore } from '@/store/useModalStore'; | ||
| import { useRouter } from 'next/navigation'; | ||
| import { useEffect } from 'react'; | ||
| import { toast } from 'react-toastify'; | ||
|
|
||
| export default function OpenPasswordResetModal({ ...props }) { | ||
| const { openModal } = useModalStore(); | ||
| const router = useRouter(); | ||
|
|
||
| useEffect(() => { | ||
| const unsubscribe = useModalStore.subscribe( | ||
| (state) => state.requestBody, // selector | ||
| // listener | ||
| (newRequestBody) => { | ||
| if (!newRequestBody) return; | ||
| const { email } = newRequestBody as { email: string }; | ||
|
|
||
| if (!email) return; | ||
|
|
||
| const requestBody = { | ||
| email, | ||
| redirectUrl: `${window.location.origin}/`, | ||
| }; | ||
| handleSendResetPasswordLink(requestBody); | ||
| } | ||
| ); | ||
| return () => { | ||
| unsubscribe(); // 언마운트, 구독 해제 | ||
| }; | ||
| }, []); | ||
|
|
||
| // send reset password link | ||
| const handleSendResetPasswordLink = async ( | ||
| requestBody: ResetPasswordToEmailBody | ||
| ) => { | ||
| try { | ||
| const response = await postResetPasswordToEmail({ body: requestBody }); | ||
|
|
||
| if (!response) { | ||
| throw new Error('204 : No Content'); | ||
| } | ||
| toast.success('비밀번호 재설정 링크가 전송되었습니다.'); | ||
| } catch (error) { | ||
| if (error instanceof Error) { | ||
| const errorMessage = error.message; | ||
|
|
||
| // 존재하지 않는 유저 : User not found | ||
| if (errorMessage.includes('User not found')) { | ||
| toast.error('존재하지 않는 이메일입니다. 회원가입을 먼저 해주세요.'); | ||
| router.push('/signup'); | ||
| } else { | ||
| toast.error(`Error : ${error}`); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="mt-3 mb-10 flex justify-end"> | ||
| <button | ||
| className="leading-normal font-medium text-emerald-500 underline" | ||
| type="button" | ||
| onClick={() => { | ||
| openModal( | ||
| { | ||
| title: '비밀번호 재설정', | ||
| description: '비밀번호 재설정 링크를 보내드립니다.', | ||
| button: { | ||
| formId: 'reset-password-form', // formId 연결 | ||
| number: 2, | ||
| text: '링크 보내기', | ||
| // 기존 모달 컴포넌트의 onRequest 호출 방식과의 일관성을 위해 빈 함수 전달 | ||
| onRequest: () => {}, | ||
|
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. 이번에 모달 컴포넌트를 확장하기 전에
Contributor
Author
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. 흠 저도 이 부분을 고민하긴 했는데, 다른 모달 컴포넌트 일관성을 고려했을 때는 남겨둬도 좋을 것 같아요. onRequest 가 기존 모달 컴포넌트들에서는 필수 콜백함수이기도 하고요 ! 현지님 생각은 어떠신가요?
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. 해명님 말씀처럼 기존 모달들, 특히 폼이 없는 모달들의 경우에는 필수로 있어야 되겠네요..! 그럼 우선은 필수로 해놓는 걸로 합시다~! 의견 감사합니다! |
||
| }, | ||
| }, | ||
|
|
||
| (() => <ResetPasswordModal />)() | ||
| ); | ||
| }} | ||
| {...props} | ||
| > | ||
| 비밀번호를 잊으셨나요? | ||
| </button> | ||
| </div> | ||
| ); | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| 'use client'; | ||
|
|
||
| import InputBase from '@/components/common/Input/InputBase'; | ||
| import { useModalStore } from '@/store/useModalStore'; | ||
| import { validateEmail } from '@/utils/inputValidation'; | ||
| import { useRef } from 'react'; | ||
|
|
||
| export default function ResetPasswordModal() { | ||
| const inputRef = useRef<HTMLInputElement>(null); | ||
| const { setRequestBody, closeModal } = useModalStore.getState(); | ||
|
|
||
| return ( | ||
| <form | ||
| id="reset-password-form" | ||
| onSubmit={(e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // 유효성 검사 | ||
| const email = inputRef.current?.value.trim(); | ||
| if (!email || !validateEmail(email)) return; | ||
|
|
||
| const requestBody = { email }; | ||
| setRequestBody(requestBody); // 상태 변경, useEffect 에서 api 호출 위해 필요 | ||
|
|
||
| // options.button?.onRequest?.(requestBody); | ||
| closeModal(); | ||
| }} | ||
| > | ||
| <InputBase | ||
| ref={inputRef} | ||
| placeholder="이메일을 입력하세요." | ||
| defaultValue="" // 초기값 설정 | ||
| /> | ||
| </form> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,19 +17,43 @@ export default function Modal() { | |
| closeModal, | ||
| } = useModalStore(); | ||
|
|
||
| const formId = button?.formId; // formId 추출 | ||
| const modalRef = useRef<HTMLDivElement>(null); | ||
| const isModalOpen = Boolean(title || content); | ||
|
|
||
| // 폼 제출 시도 여부를 추적하는 플래그 | ||
| const isSubmittedRef = useRef(false); | ||
|
Contributor
Author
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. 수정 이후 |
||
|
|
||
| useClosePopup(modalRef, closeModal); | ||
| useLockBackgroundScroll(isModalOpen); | ||
|
|
||
| if (!isModalOpen) return null; | ||
|
|
||
| // button 클릭 시 실행되는 함수 (form 존재 여부에 따른 분기 처리 ) | ||
| const handleRequest = () => { | ||
| button?.onRequest?.(requestBody); | ||
| closeModal(); | ||
| isSubmittedRef.current = true; | ||
|
|
||
| // 기존 모달 흐름 | ||
| if (!formId) { | ||
| if (isSubmittedRef.current) { | ||
| button?.onRequest?.(requestBody); | ||
| closeModal(); | ||
| isSubmittedRef.current = false; | ||
| console.log('handleRequest 실행'); | ||
|
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. 이 부분은 디버깅을 위한 |
||
| return; | ||
| } | ||
| } | ||
|
|
||
| // formId 존재 (브라우저 submit 트리거) | ||
| const form = document.getElementById( | ||
| formId as string | ||
| ) as HTMLFormElement | null; | ||
| if (form) { | ||
| form.requestSubmit(); // 폼 제출만 담당 | ||
| } | ||
| isSubmittedRef.current = false; | ||
| }; | ||
|
|
||
| if (!isModalOpen) return null; | ||
|
|
||
| return ( | ||
| <div className="tablet:items-center fixed inset-0 z-80 flex h-full w-full items-end justify-center bg-black/50"> | ||
| <div | ||
|
|
@@ -82,7 +106,11 @@ export default function Modal() { | |
| )} | ||
| </div> | ||
| )} | ||
| {content && <div className="overflow-y-auto">{content}</div>} | ||
| {content && ( | ||
| <div className="overflow-y-auto"> | ||
| {typeof content === 'function' ? content() : content} | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| <div className="flex gap-2"> | ||
|
|
@@ -99,6 +127,7 @@ export default function Modal() { | |
| </Button> | ||
| )} | ||
| <Button | ||
| {...(formId ? { form: formId } : {})} // formId 존재 여부에 따라 form 속성 추가 | ||
| variant="primary" | ||
| styleType={variant === 'danger' ? 'danger' : 'filled'} | ||
| className="flex-1" | ||
|
|
||
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.
사용자가 가입되지 않은 이메일을 입력한 경우 회원가입 페이지로 리다이렉트 하는 것도 좋다고 생각하지만, 만약 회원가입한 유저가 이메일을 잘못 입력한 경우 매번 회원가입 페이지로 이동하게 된다면 조금 피로도가 생길 것 같기도 합니다..!
회원가입 페이지로 리다이렉트 없이 존재하지 않는 이메일이라는 에러 토스트만 띄워줘도 괜찮지 않을까 생각합니다!
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.
의견 감사합니다! 다음 PR에서 반영하겠습니다 ~!