Add frontend pages with login, download, and announcements
All checks were successful
Client CI/CD / deploy (push) Successful in 12s

- React Router v7: public home page, /login page
- Auth context with JWT localStorage management
- Login: ID/PW form + SSAFY login button (UI only)
- Home: hero banner, download section (login required), announcement board
- API layer with mock data (ready for Go Fiber backend)
- Color scheme: #2E2C2F dark + #BACDB0 accent
- Add .env.example for environment variable reference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 12:40:29 +09:00
parent 1a472df39c
commit 7e2f9419ab
20 changed files with 792 additions and 125 deletions

39
src/api/announcements.js Normal file
View File

@@ -0,0 +1,39 @@
import { apiFetch } from './client';
// TODO: 백엔드 연동 시 mock 제거
const USE_MOCK = true;
const MOCK_DATA = [
{
id: 1,
title: '오픈 베타 테스트 안내',
content: '2월 28일부터 오픈 베타 테스트가 시작됩니다. 많은 참여 부탁드립니다.',
createdAt: '2026-02-24',
},
{
id: 2,
title: '클라이언트 v0.2.0 업데이트',
content: '멀티플레이어 매칭 시스템이 개선되었습니다. 런처를 통해 업데이트해주세요.',
createdAt: '2026-02-20',
},
{
id: 3,
title: '서버 점검 안내 (2/18)',
content: '2월 18일 02:00 ~ 06:00 서버 점검이 진행됩니다.',
createdAt: '2026-02-17',
},
{
id: 4,
title: '테스터 모집 공고',
content: '신규 테스터를 모집합니다. 관심 있으신 분은 신청해주세요.',
createdAt: '2026-02-10',
},
];
export async function getAnnouncements() {
if (USE_MOCK) {
await new Promise((r) => setTimeout(r, 300));
return MOCK_DATA;
}
return apiFetch('/api/announcements');
}

16
src/api/auth.js Normal file
View File

@@ -0,0 +1,16 @@
import { apiFetch } from './client';
// TODO: 백엔드 연동 시 mock 제거
const USE_MOCK = true;
export async function login(username, password) {
if (USE_MOCK) {
await new Promise((r) => setTimeout(r, 500));
if (!username || !password) throw new Error('아이디와 비밀번호를 입력해주세요.');
return { token: 'mock-jwt-token', username };
}
return apiFetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
});
}

15
src/api/client.js Normal file
View File

@@ -0,0 +1,15 @@
const BASE = import.meta.env.VITE_API_BASE_URL || '';
export async function apiFetch(path, options = {}) {
const token = localStorage.getItem('token');
const headers = { 'Content-Type': 'application/json', ...options.headers };
if (token) headers['Authorization'] = `Bearer ${token}`;
const res = await fetch(BASE + path, { ...options, headers });
if (!res.ok) {
const err = new Error(res.statusText);
err.status = res.status;
throw err;
}
return res.json();
}

17
src/api/download.js Normal file
View File

@@ -0,0 +1,17 @@
import { apiFetch } from './client';
// TODO: 백엔드 연동 시 mock 제거
const USE_MOCK = true;
export async function getDownloadInfo() {
if (USE_MOCK) {
await new Promise((r) => setTimeout(r, 200));
return {
url: '#',
version: 'v0.2.0',
fileSize: '1.2 GB',
fileName: 'A301_Launcher_Setup.exe',
};
}
return apiFetch('/api/download/info');
}