import { Box, Button, Container, Paper, TextField, Typography, LinearProgress, InputAdornment, IconButton } from '@mui/material' import { useState } from 'react' import { useNavigate } from 'react-router-dom' import api from '../lib/api' import Visibility from '@mui/icons-material/Visibility' import VisibilityOff from '@mui/icons-material/VisibilityOff' export default function Signup() { const [email, setEmail] = useState('') const [emailError, setEmailError] = useState('') const [emailStatus, setEmailStatus] = useState<'available' | 'duplicate' | ''>('') const [password, setPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [error, setError] = useState('') const [strengthScore, setStrengthScore] = useState(0) const [strengthLabel, setStrengthLabel] = useState<'약함' | '보통' | '강함' | ''>('') const [showPassword, setShowPassword] = useState(false) const navigate = useNavigate() const analyzePassword = (pw: string) => { let score = 0 if (/[a-z]/.test(pw)) score++ if (/[A-Z]/.test(pw)) score++ if (/\d/.test(pw)) score++ if (/[^A-Za-z0-9]/.test(pw)) score++ if (pw.length >= 10) score++ setStrengthScore(score) if (score <= 2) setStrengthLabel('약함') else if (score <= 4) setStrengthLabel('보통') else setStrengthLabel('강함') } const validatePassword = (pw: string) => { const regex = /^(?=.*[A-Za-z])(?=.*\d).{6,20}$/ return regex.test(pw) } const validateEmailFormat = (value: string) => { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ return regex.test(value) } const checkEmailDuplicate = async (value: string) => { if (!validateEmailFormat(value)) { setEmailError('유효한 이메일 형식을 입력해주세요.') setEmailStatus('') return } try { const res = await api.get(`/auth/check-email?email=${value}`) if (res.data.available === false) { setEmailError('이미 존재하는 이메일입니다.') setEmailStatus('duplicate') } else { setEmailError('') setEmailStatus('available') } } catch (err: any) { setEmailError('서버 오류로 이메일 확인 실패') setEmailStatus('') } } const handleSignup = async () => { setError('') if (!validateEmailFormat(email)) { setError('유효한 이메일 형식을 입력해주세요.') return } if (!validatePassword(password)) { setError('비밀번호는 영문자+숫자를 포함한 6~20자여야 합니다.') return } if (password !== confirmPassword) { setError('비밀번호가 일치하지 않습니다.') return } try { await api.post('/users/', { email, password, }) navigate('/login') } catch (err: any) { setError('회원가입 실패: 이미 존재하는 이메일이거나 서버 오류입니다.') } } return ( 회원가입 setEmail(e.target.value)} onKeyUp={(e) => { if (e.key === 'Enter') handleSignup() else checkEmailDuplicate(email) }} error={!!emailError} helperText={emailError || (emailStatus === 'available' ? '사용 가능한 이메일입니다.' : ' ')} /> { const pw = e.target.value setPassword(pw) analyzePassword(pw) }} onKeyUp={(e) => e.key === 'Enter' && handleSignup()} helperText="영문자+숫자 포함 6~20자, 특수문자 허용" InputProps={{ endAdornment: ( setShowPassword(!showPassword)} edge="end"> {showPassword ? : } ), }} /> {strengthLabel && ( 비밀번호 강도: {strengthLabel} )} setConfirmPassword(e.target.value)} onKeyUp={(e) => e.key === 'Enter' && handleSignup()} /> {error && {error}} ) }