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}
/>