Skip to content

Commit 0d1e3f9

Browse files
authored
[Refactor] react-hook-form 도입 (#593)
* chore[#589]: react-hook-form 설치 * refactor[#589]: login form에 useForm 적용 * refactor[#589]: 이메일 회원가입 폼에 useForm 적용 * refactor[#589]: 비밀번호 확인 폼에 useForm 적용 * refactor[#589]: 비밀번호 확인 폼에 form 태그 적용 - "Password field is not contained in a form" 에러로 인해 form 태그 사용 * fix[#589]: 탈퇴하기와 취소 버튼을 감싸는 div 태그 스타일 수정 * refactor[#589]: 비밀번호 설정 폼에 useForm 적용 * refactor[#589]: 회원가입 폼에 이전 페이지로 이동하는 모달 로직 추가 * refactor[#589]: 비밀번호 변경 폼에 useForm 적용 * refactor[#589]: 닉네임 변경 폼에 useForm 적용 * refactor[#589]: 로그인 폼의 handleSubmit에 onError 설정 * refactor[#589]: 기본정보 등록 폼에 useForm 적용 * refactor[#589]: handleSubmit의 onError 설정 * refactor[#589]: 에러 메세지 상수화 * refactor[#589]: 인증 코드 보내고 난 후 이메일 수정할 경우, 시간, 인증코드, mutation 초기화 * style[#589]: 새 비밀번호 Input 네이밍 변경 * refactor[#589]: 정규표현식을 저장하고 있는 변수 위치 이동
1 parent fd6e6b9 commit 0d1e3f9

40 files changed

+1686
-2002
lines changed

package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@vis.gl/react-google-maps": "^1.1.1",
1919
"react": "^18.3.1",
2020
"react-dom": "^18.3.1",
21+
"react-hook-form": "^7.54.2",
2122
"react-modal-sheet": "^3.1.0",
2223
"react-router-dom": "^6.26.1",
2324
"zustand": "^4.5.5"

src/entities/auth/ui/input.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import { useState } from "react";
1+
import { forwardRef, useState } from "react";
22
import { VisibilityOffIcon, VisibilityOnIcon } from "@/shared/ui/icon";
33
import { Input, type InputProps } from "@/shared/ui/input";
44

55
type FixedInputProps = "type" | "componentType";
66

7-
export const PasswordInput = (props: Omit<InputProps, FixedInputProps>) => {
7+
export const PasswordInput = forwardRef<
8+
HTMLInputElement,
9+
Omit<InputProps, FixedInputProps>
10+
>((props, ref) => {
811
const [isVisibilityOn, setIsVisibilityOn] = useState<boolean>(false);
912

1013
const handleVisibility = () => {
@@ -13,6 +16,7 @@ export const PasswordInput = (props: Omit<InputProps, FixedInputProps>) => {
1316

1417
return (
1518
<Input
19+
ref={ref}
1620
componentType="outlinedText"
1721
type={isVisibilityOn ? "text" : "password"}
1822
trailingNode={
@@ -30,4 +34,4 @@ export const PasswordInput = (props: Omit<InputProps, FixedInputProps>) => {
3034
{...props}
3135
/>
3236
);
33-
};
37+
});

src/features/auth/@x/setting.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1-
export { usePostCheckDuplicateNicknameState } from "../api";
2-
export { validateNickname } from "../lib";
3-
export { NicknameInput } from "../ui";
1+
export { usePostCheckDuplicateNickname } from "../api";
2+
export {
3+
NICKNAME_MAX_LENGTH,
4+
emailRegex,
5+
passwordRegex,
6+
nicknameRegex,
7+
} from "../constants";
Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
import {
2-
MutationState,
3-
useMutation,
4-
useMutationState,
5-
} from "@tanstack/react-query";
6-
import { apiClient } from "@/shared/lib";
1+
import { useMutation } from "@tanstack/react-query";
2+
import { apiClient, type HttpError } from "@/shared/lib";
73
import { SIGN_UP_END_POINT } from "../constants";
84

95
export interface PostCheckDuplicateNicknameRequest {
@@ -21,27 +17,8 @@ const postCheckDuplicateNickname = async ({
2117
};
2218

2319
export const usePostCheckDuplicateNickname = () => {
24-
return useMutation<unknown, Error, PostCheckDuplicateNicknameRequest>({
20+
return useMutation<unknown, HttpError, PostCheckDuplicateNicknameRequest>({
2521
mutationFn: postCheckDuplicateNickname,
2622
mutationKey: ["checkDuplicateNickname"],
2723
});
2824
};
29-
30-
export const usePostCheckDuplicateNicknameState = () => {
31-
// mutate 상태 결과들을 보고 중복된 닉네임인지 판단
32-
const mutationState = useMutationState<
33-
MutationState<unknown, Error, PostCheckDuplicateNicknameRequest>
34-
>({
35-
filters: {
36-
mutationKey: ["checkDuplicateNickname"],
37-
},
38-
});
39-
40-
const lastMutationState = mutationState[mutationState.length - 1];
41-
42-
// todo 409 코드일 경우, 중복된 닉네임 처리
43-
const isDuplicateNickname = lastMutationState?.status === "error";
44-
const isPending = lastMutationState?.status === "pending";
45-
46-
return { isDuplicateNickname, isPending };
47-
};

src/features/auth/constants/form.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,11 @@ export const dogBreeds = [
336336
export const REGION_API_DEBOUNCE_DELAY = 500;
337337

338338
export const VERIFICATION_CODE_LENGTH = 7;
339+
340+
export const NICKNAME_MAX_LENGTH = 20;
341+
342+
// 정규표현식
343+
export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
344+
export const passwordRegex =
345+
/^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*()_+]).{8,15}$/;
346+
export const nicknameRegex = /^[a-zA-Z0-9-]{1,20}$/;

src/features/auth/constants/message.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { NICKNAME_MAX_LENGTH, VERIFICATION_CODE_LENGTH } from "./form";
2+
13
export const errorMessage = {
24
PERMISSION_DENIED: "위치 정보를 요청 할 수 있도록 권한을 허용해주세요",
35
POSITION_UNAVAILABLE:
@@ -6,3 +8,64 @@ export const errorMessage = {
68
UNKNOWN: "알 수 없는 에러가 발생했습니다",
79
NON_SELECTED_ADDRESS: "동네를 선택해주세요",
810
};
11+
12+
export const loginErrorMessage = {
13+
email: {
14+
required: "이메일 형식으로 입력해 주세요.",
15+
pattern: "올바른 이메일 형식으로 입력해 주세요.",
16+
},
17+
submit: {
18+
required: "아이디 또는 비밀번호를 모두 입력해 주세요.",
19+
invalid: "이메일 또는 비밀번호를 다시 확인해 주세요.",
20+
},
21+
};
22+
23+
export const signUpFormValidationMessage = {
24+
email: "올바른 이메일 형식입니다.",
25+
verificationCode: "인증되었습니다.",
26+
password: "사용가능한 비밀번호 입니다.",
27+
confirmPassword: "비밀번호가 일치합니다.",
28+
};
29+
export const signUpFormErrorMessage = {
30+
email: {
31+
required: "이메일 형식으로 입력해 주세요.",
32+
pattern: "이메일 형식으로 입력해 주세요.",
33+
validate: "이미 가입된 이메일입니다.",
34+
},
35+
verificationCode: {
36+
required: `인증코드 ${VERIFICATION_CODE_LENGTH}자리를 입력해 주세요.`,
37+
minLength: `인증코드 ${VERIFICATION_CODE_LENGTH}자리를 입력해 주세요.`,
38+
isNotMatched: "인증코드를 다시 확인해 주세요.",
39+
isTimeOver: "인증시간이 만료되었습니다. 재전송 버튼을 눌러주세요.",
40+
},
41+
password: {
42+
required: "비밀번호를 입력해 주세요.",
43+
pattern: "비밀번호 형식에 맞게 입력해 주세요.",
44+
},
45+
confirmPassword: {
46+
required: "비밀번호를 다시 입력해 주세요.",
47+
pattern: "비밀번호 형식에 맞게 입력해 주세요.",
48+
isNotMatchedWithPassword: "비밀번호가 서로 일치하지 않습니다.",
49+
},
50+
submit: {
51+
required: "이메일과 비밀번호를 모두 입력해 주세요.",
52+
invalid: "이메일 또는 비밀번호를 올바르게 입력해 주세요.",
53+
},
54+
};
55+
56+
export const userInfoFormValidationMessage = {
57+
email: "사용가능한 닉네임입니다.",
58+
};
59+
export const userInfoFormErrorMessage = {
60+
nickname: {
61+
required: `${NICKNAME_MAX_LENGTH}자 이내의 한글 영어 숫자만 사용 가능합니다.`,
62+
pattern: `${NICKNAME_MAX_LENGTH}자 이내의 한글 영어 숫자만 사용 가능합니다.`,
63+
maxLength: `${NICKNAME_MAX_LENGTH}자 이내의 한글 영어 숫자만 사용 가능합니다.`,
64+
validate: "이미 존재하는 닉네임입니다.",
65+
},
66+
submit: {
67+
required: "필수 항목을 모두 입력해 주세요.",
68+
invalidNickname: "올바른 닉네임을 입력해 주세요.",
69+
requiredTermsAgreement: "필수 약관에 모두 동의해 주세요.",
70+
},
71+
};

src/features/auth/lib/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/features/auth/lib/validate.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/features/auth/store/form.ts

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)