fix: 코드 리뷰 기반 전체 개선 — 보안, 품질, UX
All checks were successful
Client CI/CD / deploy (push) Successful in 30s

- refreshToken 중복 로직 일원화 (동시 호출 방지 포함)
- 파일 업로드 401 시 토큰 갱신 후 재시도 추가
- XHR JSON.parse 에러 보호
- index.html lang="ko", title "One of the plans" 변경
- Vite 기본 에셋(vite.svg, react.svg) 및 빈 App.css 제거
- 공지 CRUD API 레이어 분리 (AnnouncementAdmin → announcements.js)
- load 함수 useCallback 적용 및 useEffect 의존성 정상화
- 로딩/빈 목록 상태 표시 추가 (AnnouncementBoard, UserAdmin)
- 누락 CSS 정의 추가 (announcement-error, announcement-empty)
- 로그인/회원가입 빈 필드 클라이언트 검증 추가
- 공지 등록 시 빈 제목/내용 에러 피드백 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 14:37:05 +09:00
parent c2e3be491d
commit 97453b1d81
17 changed files with 162 additions and 77 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../context/useAuth';
import { getDownloadInfo } from '../api/download';
@@ -14,15 +14,19 @@ export default function DownloadSection() {
const { user } = useAuth();
const navigate = useNavigate();
const loadInfo = useCallback(() => {
const loadInfo = () => {
setReady(false);
setLoadError(false);
getDownloadInfo()
.then((data) => { setInfo(data); setReady(true); })
.catch(() => { setLoadError(true); setReady(true); });
}, []);
};
useEffect(() => { loadInfo(); }, [loadInfo]);
useEffect(() => {
getDownloadInfo()
.then((data) => { setInfo(data); setReady(true); })
.catch(() => { setLoadError(true); setReady(true); });
}, []);
const handlePlay = async () => {
if (!user) {
@@ -76,7 +80,7 @@ export default function DownloadSection() {
<button onClick={handlePlay} className="btn-play" disabled={launching}>
{launching ? '준비 중...' : '게임 시작'}
</button>
{info?.launcherUrl && (
{info.launcherUrl && (
<button onClick={handleDownloadLauncher} className="btn-launcher-download">
런처 다운로드
</button>