Skip to content

Commit 8cde40e

Browse files
authored
[Feat] 회원가입 페이지 구현 (#32)
* feat: signup-step 상수화 * feat: 회원가입 페이지 구현
1 parent b87927b commit 8cde40e

File tree

4 files changed

+186
-1
lines changed

4 files changed

+186
-1
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { style } from '@vanilla-extract/css';
2+
import { vars } from '../../styles/theme.css';
3+
4+
export const container = style({
5+
display: 'flex',
6+
flexDirection: 'column',
7+
alignItems: 'stretch',
8+
justifyContent: 'center',
9+
width: '50rem',
10+
minHeight: '100vh',
11+
gap: '1.2rem',
12+
});
13+
14+
export const errorMsg = style({
15+
margin: '0',
16+
textAlign: 'left',
17+
fontSize: vars.size.xs,
18+
color: vars.color.danger,
19+
});
20+
21+
export const moveToLogin = style({
22+
display: 'flex',
23+
flexDirection: 'row',
24+
alignItems: 'center',
25+
justifyContent: 'center',
26+
gap: '1rem',
27+
fontSize: vars.size.xs,
28+
color: vars.color.black,
29+
});
30+
31+
export const memberText = style({
32+
margin: '0',
33+
});

src/pages/signup/signup-page.tsx

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,150 @@
1+
import { useNavigate } from 'react-router-dom';
2+
import { useState } from 'react';
3+
//import { AxiosError } from 'axios';
4+
//import { signUp } from '../../services/authService';
5+
import Title from '../../shared/components/title/title';
6+
import Input from '../../shared/components/input/input';
7+
import Button from '../../shared/components/button/button';
8+
import * as styles from './signup-page.css';
9+
import { SignUpStep } from '../../shared/constants/signup-step';
10+
111
const SignupPage = () => {
2-
return <div>회원가입 페이지</div>;
12+
const navigate = useNavigate();
13+
14+
const [step, setStep] = useState<SignUpStep>(SignUpStep.USERNAME);
15+
const [username, setUsername] = useState('');
16+
const [password, setPassword] = useState('');
17+
const [passwordCheck, setPasswordCheck] = useState('');
18+
const [email, setEmail] = useState('');
19+
20+
const handleNext = () => {
21+
if (step === SignUpStep.USERNAME && username.trim()) {
22+
setStep(SignUpStep.PASSWORD);
23+
} else if (
24+
step === SignUpStep.PASSWORD &&
25+
password.trim() &&
26+
passwordCheck.trim() &&
27+
password === passwordCheck
28+
) {
29+
setStep(SignUpStep.EMAIL);
30+
}
31+
};
32+
33+
/*const handleSignup = async () => {
34+
try {
35+
await signUp(id, password, nickname);
36+
alert(`${nickname}님 반갑습니다! 회원가입에 성공했어요.`);
37+
navigate('/login');
38+
} catch (error) {
39+
const err = error as AxiosError<{ message: string }>;
40+
alert(err.response?.data?.message || '회원가입에 실패했어요.');
41+
}
42+
};*/
43+
44+
return (
45+
<div className={styles.container}>
46+
<Title>회원가입</Title>
47+
48+
{/* 아이디 입력 단계 */}
49+
{step === SignUpStep.USERNAME && (
50+
<>
51+
<Input
52+
label='아이디'
53+
type='text'
54+
placeholder='아이디를 입력해주세요 (8~20자, 대소문자/숫자만 가능)'
55+
value={username}
56+
onChange={(e) => setUsername(e.target.value)}
57+
/>
58+
59+
{username.trim().length > 20 && (
60+
<p className={styles.errorMsg}>최대 길이는 20자 이하로 입력해주세요.</p>
61+
)}
62+
63+
<Button
64+
onClick={handleNext}
65+
disabled={
66+
username.trim().length < 8 ||
67+
username.trim().length > 20 ||
68+
!/^[a-zA-Z0-9]+$/.test(username)
69+
}
70+
>
71+
다음
72+
</Button>
73+
</>
74+
)}
75+
76+
{/* 비밀번호 입력 단계 */}
77+
{step === SignUpStep.PASSWORD && (
78+
<>
79+
<Input
80+
label='비밀번호'
81+
type='password'
82+
placeholder='비밀번호를 입력해주세요'
83+
value={password}
84+
onChange={(e) => setPassword(e.target.value)}
85+
showIcon
86+
/>
87+
<Input
88+
type='password'
89+
placeholder='비밀번호 확인'
90+
value={passwordCheck}
91+
onChange={(e) => setPasswordCheck(e.target.value)}
92+
showIcon
93+
/>
94+
95+
{(password.trim().length > 20 || passwordCheck.trim().length > 20) && (
96+
<p className={styles.errorMsg}>최대 길이는 20자 이하로 입력해주세요.</p>
97+
)}
98+
{password !== passwordCheck && (
99+
<p className={styles.errorMsg}>비밀번호가 일치하지 않아요.</p>
100+
)}
101+
102+
<Button
103+
onClick={handleNext}
104+
disabled={
105+
password.length < 8 ||
106+
password.length > 20 ||
107+
!/^[a-zA-Z0-9!@#$%^&*()_\-+=\[\]{}|;:',.<>\/?]+$/.test(password) ||
108+
password !== passwordCheck
109+
}
110+
>
111+
다음
112+
</Button>
113+
</>
114+
)}
115+
116+
{/* 이메일 입력 단계 */}
117+
{step === SignUpStep.EMAIL && (
118+
<>
119+
<Input
120+
label='이메일'
121+
type='text'
122+
placeholder='이메일을 입력해주세요'
123+
value={email}
124+
onChange={(e) => setEmail(e.target.value)}
125+
/>
126+
127+
{email.trim() !== '' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && (
128+
<p className={styles.errorMsg}>이메일 형식이 올바르지 않아요.</p>
129+
)}
130+
131+
<Button
132+
//onClick={handleSignup}
133+
disabled={!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)}
134+
>
135+
회원가입 하기
136+
</Button>
137+
</>
138+
)}
139+
140+
<div className={styles.moveToLogin}>
141+
<p className={styles.memberText}>이미 회원이신가요?</p>
142+
<Button variant='secondary' onClick={() => navigate('/login')}>
143+
로그인
144+
</Button>
145+
</div>
146+
</div>
147+
);
3148
};
4149

5150
export default SignupPage;

src/shared/constants/.gitkeep

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const SignUpStep = {
2+
USERNAME: 'username',
3+
PASSWORD: 'password',
4+
EMAIL: 'email',
5+
} as const;
6+
7+
export type SignUpStep = (typeof SignUpStep)[keyof typeof SignUpStep];

0 commit comments

Comments
 (0)