From 2ac2823eccf36cdcb172b5861060738a81f4a390 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Tue, 24 Feb 2026 15:11:32 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=8F=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RegisterPage 추가 (아이디/비밀번호/비밀번호 확인 폼, 클라이언트 유효성 검사) - /register 라우트 추가 (로그인 상태 시 홈 리다이렉트) - 로그인 페이지에 회원가입 링크 및 가입 완료 안내 메시지 추가 - api/auth.js에 register() 함수 추가 Co-Authored-By: Claude Sonnet 4.6 --- src/App.jsx | 2 + src/api/auth.js | 7 +++ src/pages/LoginPage.css | 7 +++ src/pages/LoginPage.jsx | 6 ++- src/pages/RegisterPage.jsx | 94 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/pages/RegisterPage.jsx diff --git a/src/App.jsx b/src/App.jsx index 3de530a..a17dd8e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,7 @@ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import { AuthProvider, useAuth } from './context/AuthContext'; import LoginPage from './pages/LoginPage'; +import RegisterPage from './pages/RegisterPage'; import HomePage from './pages/HomePage'; import AdminPage from './pages/AdminPage'; @@ -16,6 +17,7 @@ function AppRoutes() { return ( : } /> + : } /> } /> { e.preventDefault(); @@ -58,6 +60,7 @@ export default function LoginPage() { /> + {justRegistered &&

회원가입이 완료되었습니다. 로그인해주세요.

} {error &&

{error}

} + 계정이 없으신가요? 회원가입 메인으로 돌아가기 diff --git a/src/pages/RegisterPage.jsx b/src/pages/RegisterPage.jsx new file mode 100644 index 0000000..48a4cc4 --- /dev/null +++ b/src/pages/RegisterPage.jsx @@ -0,0 +1,94 @@ +import { useState } from 'react'; +import { useNavigate, Link } from 'react-router-dom'; +import { register } from '../api/auth'; +import './LoginPage.css'; + +export default function RegisterPage() { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [confirm, setConfirm] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(''); + + if (password !== confirm) { + setError('비밀번호가 일치하지 않습니다.'); + return; + } + if (password.length < 6) { + setError('비밀번호는 6자 이상이어야 합니다.'); + return; + } + + setLoading(true); + try { + await register(username, password); + navigate('/login', { state: { registered: true } }); + } catch (err) { + setError(err.message || '회원가입에 실패했습니다.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+

A301

+

MULTIPLAYER

+
+ +
+
+ + setUsername(e.target.value)} + placeholder="아이디를 입력하세요" + autoComplete="username" + /> +
+ +
+ + setPassword(e.target.value)} + placeholder="6자 이상 입력하세요" + autoComplete="new-password" + /> +
+ +
+ + setConfirm(e.target.value)} + placeholder="비밀번호를 다시 입력하세요" + autoComplete="new-password" + /> +
+ + {error &&

{error}

} + + +
+ + 이미 계정이 있으신가요? 로그인 +
+
+ ); +}