Skip to content

Commit 4117938

Browse files
authored
Merge pull request #303 from prgrms-web-devcourse-final-project/refactor/sign-up
[refactor] 회원가입 리펙토링
2 parents 4cede5f + e1b8621 commit 4117938

19 files changed

+236
-212
lines changed

src/apis/signup.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { axiosInstance } from '@/apis/axios';
2+
3+
interface SignUpPayload {
4+
nickName: string;
5+
loginId: string;
6+
password: string;
7+
email: string;
8+
}
9+
10+
// 회원가입
11+
export const postSignUp = async ({ nickName, loginId, password, email }: SignUpPayload) => {
12+
const { data } = await axiosInstance.post('/user/signup', {
13+
nickName,
14+
socialId: null,
15+
socialType: 'NONE',
16+
loginId,
17+
password,
18+
email,
19+
});
20+
return data;
21+
};

src/apis/user.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,6 @@ export const checkPassword = async (currentPassword: string) => {
3535
return data;
3636
};
3737

38-
// 회원가입
39-
export const postSignUp = async (
40-
nickName: string,
41-
loginId: string,
42-
password: string,
43-
email: string,
44-
) => {
45-
const { data } = await axiosInstance.post('/user/signup', {
46-
nickName,
47-
socialId: null,
48-
socialType: 'NONE',
49-
loginId,
50-
password,
51-
email,
52-
});
53-
return data;
54-
};
55-
5638
// 마이페이지 정보 불러오기
5739
export const getMyProfile = async () => {
5840
const { data } = await axiosInstance.get('/user/mypage');

src/components/Main.tsx

Whitespace-only changes.
Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import Input from '@/components/input/Input';
1+
import { Input } from '@/components/input/';
2+
import { forwardRef } from 'react';
23
import { twMerge } from 'tailwind-merge';
34

45
interface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
@@ -9,37 +10,35 @@ interface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
910
actionButton?: React.ReactNode;
1011
}
1112

12-
export default function InputField({
13-
id,
14-
label,
15-
isValid,
16-
message,
17-
actionButton,
18-
...props
19-
}: InputFieldProps) {
20-
return (
21-
<div className="flex flex-col w-full gap-0.5">
22-
{/* 라벨 */}
23-
<label htmlFor={id} className="body-r text-gray-80 pl-[5px]">
24-
{label}
25-
</label>
13+
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
14+
({ id, label, isValid, message, actionButton, ...props }, ref) => {
15+
return (
16+
<div className="flex flex-col w-full gap-0.5">
17+
{/* 라벨 */}
18+
<label htmlFor={id} className="body-r text-gray-80 pl-[5px]">
19+
{label}
20+
</label>
2621

27-
{/* 입력 필드 + 버튼 */}
28-
<div className="flex gap-2">
29-
<Input id={id} {...props} />
30-
{actionButton && <div className="shrink-0">{actionButton}</div>}
22+
{/* 입력 필드 + 버튼 */}
23+
<div className="flex gap-2">
24+
<Input id={id} ref={ref} {...props} />
25+
{actionButton && <div className="shrink-0">{actionButton}</div>}
26+
</div>
27+
28+
{/* 유효성 검사 메시지 */}
29+
<p
30+
className={twMerge(
31+
'text-[9px]/[18px] pl-[5px]',
32+
isValid ? 'text-functional-success' : 'text-functional-danger',
33+
message === '' && 'invisible',
34+
)}
35+
>
36+
{message || '‎'}
37+
</p>
3138
</div>
39+
);
40+
},
41+
);
3242

33-
{/* 유효성 검사 메시지 */}
34-
<p
35-
className={twMerge(
36-
'text-[9px]/[18px] pl-[5px]',
37-
isValid ? 'text-functional-success' : 'text-functional-danger',
38-
message === '' && 'invisible',
39-
)}
40-
>
41-
{message || '‎'}
42-
</p>
43-
</div>
44-
);
45-
}
43+
InputField.displayName = 'InputField';
44+
export default InputField;

src/components/input/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
import Input from '@/components/input/Input';
2-
import InputField from '@/components/input/InputField';
3-
4-
export { Input, InputField };
1+
export { default as Input } from './Input';
2+
export { default as InputField } from './InputField';

src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './emotions';
22
export * from './validation';
33
export * from './limits';
4+
export * from './email';
45

56
// 사용
67
// import { EMOTIONS, MAX_NICKNAME_LENGTH, PASSWORD_REQUIRED_RULES } from "@/constants";

src/pages/signup/SignUp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import SignUpForm from '@/pages/signup/components/SignUpForm';
1+
import { SignUpForm } from '@/pages/signup/components';
22

33
function SignUp() {
44
return (

src/pages/signup/components/AuthCodeInput.tsx

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { postEmailVerificationCheck, postEmailVerificationRequest } from '@/apis/email';
2-
import InputAuthCode from '@/components/InputAuthCode';
3-
import { AUTHCODE_REGEX } from '@/constants';
4-
import { MAX_RESEND_COUNT } from '@/constants/email';
2+
import { LoadingSpinnerButton } from '@/components/button';
3+
import { InputAuthCode } from '@/pages/signup/components/';
4+
import { AUTHCODE_REGEX, MAX_RESEND_COUNT } from '@/constants';
55
import { useModalStore } from '@/store/modalStore';
66
import { useMutation } from '@tanstack/react-query';
77
import { useState } from 'react';
88

99
interface AuthCodeInputProps {
1010
email: string;
11-
authcodeValidity: boolean;
11+
emailValidity: boolean;
1212
setValidity: (val: boolean) => void; // 인증코드 유효성 바꾸는 함수
1313
}
14-
function AuthCodeInput({ email, authcodeValidity, setValidity }: AuthCodeInputProps) {
14+
function AuthCodeInput({ email, emailValidity, setValidity }: AuthCodeInputProps) {
1515
const { openModal, closeModal } = useModalStore(); // 모달
1616
const [resendCount, setResendCount] = useState(0); // 재전송 횟수
1717
const [text, setText] = useState('');
18-
const [validationMessage, setValidationMessage] = useState({
19-
success: false,
18+
const [validationStatus, setValidationStatus] = useState({
19+
isValid: false, // 유효성 통과여부
2020
message: '인증번호가 오지 않았나요?',
2121
});
2222

@@ -28,9 +28,9 @@ function AuthCodeInput({ email, authcodeValidity, setValidity }: AuthCodeInputPr
2828
const isValid = AUTHCODE_REGEX.test(value);
2929

3030
if (isValid) {
31-
setValidationMessage({ success: true, message: '' });
31+
setValidationStatus({ isValid: true, message: '' });
3232
} else {
33-
setValidationMessage({ success: false, message: '올바른 인증번호를 입력해주세요' });
33+
setValidationStatus({ isValid: false, message: '올바른 인증번호를 입력해주세요' });
3434
}
3535
};
3636

@@ -57,25 +57,25 @@ function AuthCodeInput({ email, authcodeValidity, setValidity }: AuthCodeInputPr
5757
mutationFn: () => postEmailVerificationCheck(email, text),
5858
onSuccess: ({ code }) => {
5959
if (code === 200) {
60-
setValidationMessage({ success: true, message: '이메일 인증이 완료되었습니다' });
60+
setValidationStatus({ isValid: true, message: '이메일 인증이 완료되었습니다' });
6161
setValidity(true); // 완료 처리
6262
} else {
63-
setValidationMessage({ success: false, message: '인증 코드가 올바르지 않습니다' });
63+
setValidationStatus({ isValid: false, message: '인증 코드가 올바르지 않습니다' });
6464
}
6565
},
6666

6767
onError: () => {
68-
setValidationMessage({
69-
success: false,
68+
setValidationStatus({
69+
isValid: false,
7070
message: '오류가 발생했습니다. 잠시 후 다시 시도해 주세요.',
7171
});
7272
},
7373
});
7474

7575
// 시간 초과시 실행할 함수
7676
const onTimeout = () => {
77-
setValidationMessage({
78-
success: false,
77+
setValidationStatus({
78+
isValid: false,
7979
message: '인증 시간이 만료되었습니다. 다시 요청해주세요.',
8080
});
8181
};
@@ -95,27 +95,28 @@ function AuthCodeInput({ email, authcodeValidity, setValidity }: AuthCodeInputPr
9595
resendEmailVerification();
9696
};
9797

98-
const buttonHandler = {
99-
disable: validationMessage.success && !authcodeValidity,
100-
text: '인증확인',
101-
isLoading: isPending,
102-
onClick: verifyEmail,
103-
};
104-
10598
return (
10699
<InputAuthCode
107100
id="emailVerificationConfrim"
108101
label="인증번호 확인"
109102
placeholder="인증번호 6자리를 입력해 주세요"
110-
isValid={validationMessage.success} // ✅ 유효성 검사 여부 전달
111-
validationMessage={validationMessage.message} // ✅ 메시지 전달
103+
isValid={validationStatus.isValid} // ✅ 유효성 검사 여부 전달
104+
message={validationStatus.message} // ✅ 메시지 전달
112105
value={text.toUpperCase()}
113106
onChange={handleChange}
114107
onTimeout={onTimeout} // 시간이 만료되었을 때 실행할 함수
115108
onResendEmail={handleResendEmail} // 재전송 요청
116109
resendCount={resendCount} // 재전송 횟수
117-
authcodeValidity={authcodeValidity} // 인증코드 유효성
118-
buttonHandler={buttonHandler}
110+
isEmailVerified={emailValidity}
111+
actionButton={
112+
<LoadingSpinnerButton
113+
isLoading={isPending}
114+
className="w-[65px] flex-shrink-0"
115+
text={emailValidity ? '인증완료' : '인증확인'}
116+
disabled={!validationStatus.isValid || emailValidity}
117+
onClick={() => verifyEmail()}
118+
/>
119+
}
119120
/>
120121
);
121122
}
File renamed without changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AuthCodeInput, EmailInput } from '@/pages/signup/components';
2+
import { useState } from 'react';
3+
4+
interface EmailGroupSectionProps {
5+
updateValidity: (key: 'email', value: boolean) => void;
6+
validity: { email: boolean };
7+
}
8+
9+
const EmailGroupSection = ({ updateValidity, validity }: EmailGroupSectionProps) => {
10+
const [email, setEmail] = useState('');
11+
return (
12+
<>
13+
<EmailInput
14+
setValidity={(value) => updateValidity('email', value)}
15+
emailValidity={validity.email}
16+
setEmail={setEmail}
17+
/>
18+
{email !== '' && (
19+
<AuthCodeInput
20+
email={email}
21+
emailValidity={validity.email}
22+
setValidity={(value) => updateValidity('email', value)}
23+
/>
24+
)}
25+
</>
26+
);
27+
};
28+
29+
export default EmailGroupSection;

0 commit comments

Comments
 (0)