어드민 회원 가입신청 기능 추가

This commit is contained in:
2026-01-22 16:11:55 +09:00
parent 64d9fb8c6c
commit 1f2c39869c
4 changed files with 190 additions and 74 deletions

View File

@@ -73,7 +73,7 @@ const App = () => {
>
<Routes>
{/* 1. 로그인 여부와 관계없이 항상 독립적으로 표시되는 페이지 */}
<Route path="/register" element={<Register />} />
<Route path="/admin/member/register" element={<Register />} />
<Route path="/404" element={<Page404 />} />
<Route path="/500" element={<Page500 />} />

View File

@@ -62,3 +62,9 @@ export const deleteAdminMember = async (memberSeq: number): Promise<AdminMemberR
const response = await axios.post<AdminMemberResponse>('/admin/member/delete', { memberSeq });
return response.data;
};
// 회원 가입 (비로그인 상태에서 회원가입)
export const registerAdminMember = async (member: Partial<AdminMember>): Promise<AdminMemberResponse> => {
const response = await axios.post<AdminMemberResponse>('/admin/member/register', member);
return response.data;
};

View File

@@ -53,7 +53,11 @@ const Login = () => {
setError('쿠키에서 accessToken을 찾을 수 없습니다.');
}
} else {
setError(response.resultMessage || '로그인에 실패했습니다.');
let errorMsg = response.resultMessage || '로그인에 실패했습니다.'
if (response.resultData && typeof response.resultData === 'string') {
errorMsg += `\n${response.resultData}`
}
setError(errorMsg)
}
} catch (err: any) {
setError(err.response?.data?.message || '로그인 중 오류가 발생했습니다.');
@@ -65,16 +69,16 @@ const Login = () => {
return (
<div className="bg-body-tertiary min-vh-100 d-flex flex-row align-items-center">
<CContainer>
<CContainer fluid>
<CRow className="justify-content-center">
<CCol md={8}>
<CCardGroup>
<CCardGroup style={{ minWidth: '300px' }}>
<CCard className="p-4">
<CCardBody>
<CForm onSubmit={handleSubmit}>
<h1>Login</h1>
<p className="text-body-secondary">Sign In to your account</p>
{error && <div className="text-danger mb-3">{error}</div>}
{error && <div className="text-danger mb-3" style={{ whiteSpace: 'pre-line' }}>{error}</div>}
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilUser} />
@@ -108,16 +112,19 @@ const Login = () => {
</CForm>
</CCardBody>
</CCard>
<CCard className="text-white bg-primary py-5">
<CCard className="text-white bg-primary py-5" style={{ minWidth: '300px' }}>
<CCardBody className="text-center">
<div>
<h2>Sign up</h2>
<h2></h2>
<br/>
<p>
, .
<br/>
<br/>
.
</p>
<Link to="/register">
<Link to="/admin/member/register">
<CButton color="primary" className="mt-3" active tabIndex={-1}>
Register Now!
</CButton>
</Link>
</div>

View File

@@ -1,4 +1,5 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import { useNavigate, Link } from 'react-router-dom'
import {
CButton,
CCard,
@@ -7,14 +8,79 @@ import {
CContainer,
CForm,
CFormInput,
CInputGroup,
CInputGroupText,
CFormLabel,
CRow,
CSpinner,
} from '@coreui/react'
import CIcon from '@coreui/icons-react'
import { cilLockLocked, cilUser } from '@coreui/icons'
import { registerAdminMember } from 'src/services/adminMemberService'
import { logout } from 'src/axios/authService'
const Register = () => {
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
// 회원가입 페이지 진입 시 기존 토큰 제거
useEffect(() => {
logout()
}, [])
const [formData, setFormData] = useState({
memberId: '',
memberName: '',
password: '',
passwordConfirm: '',
})
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setFormData(prev => ({
...prev,
[name]: value,
}))
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// 유효성 검사
if (!formData.memberId.trim()) {
alert('어드민 회원 ID를 입력해주세요.')
return
}
if (!formData.memberName.trim()) {
alert('이름을 입력해주세요.')
return
}
if (!formData.password) {
alert('비밀번호를 입력해주세요.')
return
}
if (formData.password !== formData.passwordConfirm) {
alert('비밀번호가 일치하지 않습니다.')
return
}
setLoading(true)
try {
const response = await registerAdminMember({
memberId: formData.memberId,
memberName: formData.memberName,
password: formData.password,
})
if (response.resultCode === '200') {
alert('회원가입이 완료되었습니다. 관리자 승인 후 로그인이 가능합니다.')
navigate('/login')
} else {
alert(response.resultMessage || '회원가입에 실패했습니다.')
}
} catch (error) {
console.error('회원가입 실패:', error)
alert('회원가입에 실패했습니다.')
} finally {
setLoading(false)
}
}
return (
<div className="bg-body-tertiary min-vh-100 d-flex flex-row align-items-center">
<CContainer>
@@ -22,41 +88,78 @@ const Register = () => {
<CCol md={9} lg={7} xl={6}>
<CCard className="mx-4">
<CCardBody className="p-4">
<CForm>
<h1>Register</h1>
<p className="text-body-secondary">Create your account</p>
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilUser} />
</CInputGroupText>
<CFormInput placeholder="Username" autoComplete="username" />
</CInputGroup>
<CInputGroup className="mb-3">
<CInputGroupText>@</CInputGroupText>
<CFormInput placeholder="Email" autoComplete="email" />
</CInputGroup>
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilLockLocked} />
</CInputGroupText>
<CForm onSubmit={handleSubmit}>
<h1> </h1>
<p className="text-body-secondary"> </p>
<div className="mb-3">
<CFormLabel htmlFor="memberId"> ID *</CFormLabel>
<CFormInput
type="text"
id="memberId"
name="memberId"
value={formData.memberId}
onChange={handleInputChange}
placeholder="어드민 회원 ID를 입력하세요"
autoComplete="username"
/>
</div>
<div className="mb-3">
<CFormLabel htmlFor="memberName"> *</CFormLabel>
<CFormInput
type="text"
id="memberName"
name="memberName"
value={formData.memberName}
onChange={handleInputChange}
placeholder="이름을 입력하세요"
autoComplete="name"
/>
</div>
<div className="mb-3">
<CFormLabel htmlFor="password"> *</CFormLabel>
<CFormInput
type="password"
placeholder="Password"
id="password"
name="password"
value={formData.password}
onChange={handleInputChange}
placeholder="비밀번호를 입력하세요"
autoComplete="new-password"
/>
</CInputGroup>
<CInputGroup className="mb-4">
<CInputGroupText>
<CIcon icon={cilLockLocked} />
</CInputGroupText>
</div>
<div className="mb-3">
<CFormLabel htmlFor="passwordConfirm"> *</CFormLabel>
<CFormInput
type="password"
placeholder="Repeat password"
id="passwordConfirm"
name="passwordConfirm"
value={formData.passwordConfirm}
onChange={handleInputChange}
placeholder="비밀번호를 다시 입력하세요"
autoComplete="new-password"
/>
</CInputGroup>
</div>
<div className="d-grid">
<CButton color="success">Create Account</CButton>
<CButton color="success" type="submit" disabled={loading}>
{loading ? (
<>
<CSpinner size="sm" className="me-2" />
...
</>
) : (
'회원가입'
)}
</CButton>
</div>
<div className="text-center mt-3">
<span className="text-body-secondary"> ? </span>
<Link to="/login"></Link>
</div>
</CForm>
</CCardBody>