✨ 회원가입 신청 기능 완성 (간소화)
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
백엔드:
- signup_routes.py 신규 생성
- POST /auth/signup-request: 회원가입 신청
- GET /auth/signup-requests: 승인 대기 목록 (관리자)
- POST /auth/approve-signup/{id}: 승인 (관리자)
- DELETE /auth/reject-signup/{id}: 거부 (관리자)
- main.py에 signup_router 등록
프론트엔드:
- SimpleLogin에 회원가입 폼 추가
- 필수 항목만: 사용자명, 비밀번호, 비밀번호 확인, 이름
- 간단하고 깔끔한 UI
- 비밀번호 일치 검사 및 최소 길이 검사
- 제출 후 승인 대기 안내 메시지
This commit is contained in:
@@ -9,6 +9,18 @@ const SimpleLogin = ({ onLoginSuccess }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState('');
|
||||
const [showSignup, setShowSignup] = useState(false);
|
||||
const [signupData, setSignupData] = useState({
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
name: '',
|
||||
email: '',
|
||||
department: '',
|
||||
position: '',
|
||||
phone: '',
|
||||
reason: ''
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
@@ -19,6 +31,73 @@ const SimpleLogin = ({ onLoginSuccess }) => {
|
||||
if (error) setError('');
|
||||
};
|
||||
|
||||
const handleSignupChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setSignupData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
if (error) setError('');
|
||||
};
|
||||
|
||||
const handleSignupSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// 유효성 검사
|
||||
if (!signupData.username || !signupData.password || !signupData.name) {
|
||||
setError('필수 항목을 모두 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (signupData.password !== signupData.confirmPassword) {
|
||||
setError('비밀번호가 일치하지 않습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (signupData.password.length < 6) {
|
||||
setError('비밀번호는 최소 6자 이상이어야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const response = await api.post('/auth/signup-request', {
|
||||
username: signupData.username,
|
||||
password: signupData.password,
|
||||
name: signupData.name,
|
||||
email: signupData.email || null,
|
||||
department: signupData.department || null,
|
||||
position: signupData.position || null,
|
||||
phone: signupData.phone || null,
|
||||
reason: signupData.reason || null
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
setSuccess('회원가입 요청이 전송되었습니다. 관리자 승인 후 이용 가능합니다.');
|
||||
setShowSignup(false);
|
||||
// 폼 초기화
|
||||
setSignupData({
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
name: '',
|
||||
email: '',
|
||||
department: '',
|
||||
position: '',
|
||||
phone: '',
|
||||
reason: ''
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMsg = err.response?.data?.detail || '회원가입 요청 중 오류가 발생했습니다.';
|
||||
setError(errorMsg);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -175,6 +254,55 @@ const SimpleLogin = ({ onLoginSuccess }) => {
|
||||
{isLoading ? '로그인 중...' : '로그인'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* 회원가입 버튼 */}
|
||||
<div style={{ marginTop: '20px', textAlign: 'center' }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShowSignup(!showSignup);
|
||||
setError('');
|
||||
setSuccess('');
|
||||
}}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '14px',
|
||||
backgroundColor: showSignup ? '#6b7280' : '#10b981',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '8px',
|
||||
fontSize: '16px',
|
||||
fontWeight: '600',
|
||||
cursor: 'pointer',
|
||||
transition: 'background-color 0.3s ease'
|
||||
}}
|
||||
>
|
||||
{showSignup ? '✕ 닫기' : '➕ 회원가입 신청'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 회원가입 폼 */}
|
||||
{showSignup && (
|
||||
<div style={{
|
||||
marginTop: '20px',
|
||||
padding: '20px',
|
||||
background: '#f0fdf4',
|
||||
border: '2px solid #10b981',
|
||||
borderRadius: '12px',
|
||||
maxHeight: '500px',
|
||||
overflowY: 'auto'
|
||||
}}>
|
||||
<form onSubmit={handleSignupSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
|
||||
<input type="text" name="username" value={signupData.username} onChange={handleSignupChange} placeholder="사용자명*" required style={{ padding: '12px', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '14px' }} />
|
||||
<input type="password" name="password" value={signupData.password} onChange={handleSignupChange} placeholder="비밀번호 (최소 6자)*" required style={{ padding: '12px', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '14px' }} />
|
||||
<input type="password" name="confirmPassword" value={signupData.confirmPassword} onChange={handleSignupChange} placeholder="비밀번호 확인*" required style={{ padding: '12px', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '14px' }} />
|
||||
<input type="text" name="name" value={signupData.name} onChange={handleSignupChange} placeholder="이름*" required style={{ padding: '12px', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '14px' }} />
|
||||
<button type="submit" disabled={isLoading} style={{ padding: '14px', background: isLoading ? '#9ca3af' : '#10b981', color: 'white', border: 'none', borderRadius: '8px', fontSize: '16px', fontWeight: '600', cursor: isLoading ? 'not-allowed' : 'pointer', marginTop: '8px' }}>
|
||||
{isLoading ? '처리 중...' : '✓ 회원가입 신청'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div style={{
|
||||
|
||||
Reference in New Issue
Block a user