From f93d81b6d99eb9cf3c40ed1999befb23f661c86e Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Fri, 13 Mar 2026 21:40:09 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=9E=85=EB=A0=A5=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EC=9D=BC=EA=B4=80=EC=84=B1=C2=B7UX=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회원가입 username 검증을 서버와 동일하게 맞춤 - 비밀번호 maxLength를 bcrypt 제한(72)에 맞춤 - 공지사항 줄바꿈 CSS 처리 (pre→white-space) - 어드민 페이지 에러 로깅 추가 - 다운로드 섹션 로딩 스켈레톤 추가 Co-Authored-By: Claude Opus 4.6 --- src/components/AnnouncementBoard.css | 2 ++ src/components/DownloadSection.jsx | 11 ++++++++++- src/components/admin/AnnouncementAdmin.jsx | 4 +++- src/components/admin/DownloadAdmin.jsx | 4 +++- src/components/admin/UserAdmin.jsx | 2 +- src/pages/LoginPage.jsx | 2 +- src/pages/RegisterPage.jsx | 17 +++++++++++++---- 7 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/components/AnnouncementBoard.css b/src/components/AnnouncementBoard.css index 08c934c..77a56f8 100644 --- a/src/components/AnnouncementBoard.css +++ b/src/components/AnnouncementBoard.css @@ -58,6 +58,8 @@ font-size: 0.9rem; color: rgba(255, 255, 255, 0.6); line-height: 1.6; + white-space: pre-wrap; + word-break: break-word; } .announcement-error { diff --git a/src/components/DownloadSection.jsx b/src/components/DownloadSection.jsx index 926dd70..5865e37 100644 --- a/src/components/DownloadSection.jsx +++ b/src/components/DownloadSection.jsx @@ -62,7 +62,16 @@ export default function DownloadSection() { } }; - if (!ready) return null; + if (!ready) { + return ( +
+
+

One of the plans

+

불러오는 중...

+
+
+ ); + } return (
diff --git a/src/components/admin/AnnouncementAdmin.jsx b/src/components/admin/AnnouncementAdmin.jsx index f7c5acd..182c5f3 100644 --- a/src/components/admin/AnnouncementAdmin.jsx +++ b/src/components/admin/AnnouncementAdmin.jsx @@ -10,7 +10,9 @@ export default function AnnouncementAdmin() { const [error, setError] = useState(''); const load = useCallback(() => { - getAnnouncements().then(setList).catch(() => {}); + getAnnouncements().then(setList).catch((err) => { + console.error('공지사항 로드 실패:', err); + }); }, []); useEffect(() => { load(); }, [load]); diff --git a/src/components/admin/DownloadAdmin.jsx b/src/components/admin/DownloadAdmin.jsx index 1a026e3..b4746fd 100644 --- a/src/components/admin/DownloadAdmin.jsx +++ b/src/components/admin/DownloadAdmin.jsx @@ -143,7 +143,9 @@ export default function DownloadAdmin() { const [info, setInfo] = useState(null); useEffect(() => { - getDownloadInfo().then(setInfo).catch(() => {}); + getDownloadInfo().then(setInfo).catch((err) => { + console.error('다운로드 정보 로드 실패:', err); + }); }, []); return ( diff --git a/src/components/admin/UserAdmin.jsx b/src/components/admin/UserAdmin.jsx index 537e521..30a88cf 100644 --- a/src/components/admin/UserAdmin.jsx +++ b/src/components/admin/UserAdmin.jsx @@ -12,7 +12,7 @@ export default function UserAdmin() { const load = useCallback(() => { getUsers() .then(setUsers) - .catch(() => {}) + .catch((err) => { console.error('유저 목록 로드 실패:', err); }) .finally(() => setLoading(false)); }, []); useEffect(() => { load(); }, [load]); diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index 8a7598a..e55ae65 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -63,7 +63,7 @@ export default function LoginPage() { onChange={(e) => setPassword(e.target.value)} placeholder="비밀번호를 입력하세요" autoComplete="current-password" - maxLength={100} + maxLength={72} /> diff --git a/src/pages/RegisterPage.jsx b/src/pages/RegisterPage.jsx index b9a320c..08fac2e 100644 --- a/src/pages/RegisterPage.jsx +++ b/src/pages/RegisterPage.jsx @@ -31,10 +31,19 @@ export default function RegisterPage() { e.preventDefault(); setError(''); - if (!username.trim()) { + const trimmed = username.trim().toLowerCase(); + if (!trimmed) { setError('아이디를 입력해주세요.'); return; } + if (trimmed.length < 3) { + setError('아이디는 3자 이상이어야 합니다.'); + return; + } + if (!/^[a-z0-9_-]+$/.test(trimmed)) { + setError('아이디는 영문 소문자, 숫자, _, -만 사용 가능합니다.'); + return; + } if (password !== confirm) { setError('비밀번호가 일치하지 않습니다.'); return; @@ -46,7 +55,7 @@ export default function RegisterPage() { setLoading(true); try { - await register(username, password); + await register(trimmed, password); navigate('/login', { state: { registered: true } }); } catch (err) { setError(err.message || '회원가입에 실패했습니다.'); @@ -86,7 +95,7 @@ export default function RegisterPage() { onChange={(e) => setPassword(e.target.value)} placeholder="6자 이상 입력하세요" autoComplete="new-password" - maxLength={100} + maxLength={72} /> @@ -105,7 +114,7 @@ export default function RegisterPage() { onChange={(e) => setConfirm(e.target.value)} placeholder="비밀번호를 다시 입력하세요" autoComplete="new-password" - maxLength={100} + maxLength={72} />