diff --git a/frontend/package.json b/frontend/package.json
index 56b3680..142f4f5 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,5 +1,6 @@
{
"name": "piroinfront",
+ "proxy": "http://localhost:8080",
"version": "0.1.0",
"private": true,
"dependencies": {
@@ -16,7 +17,6 @@
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
- "proxy": "http://13.209.73.127:8080",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
diff --git a/frontend/src/pages/OnboardingPage.js b/frontend/src/pages/OnboardingPage.js
index b819e21..933057d 100644
--- a/frontend/src/pages/OnboardingPage.js
+++ b/frontend/src/pages/OnboardingPage.js
@@ -7,10 +7,15 @@ function OnboardingPage() {
const navigate = useNavigate();
useEffect(() => {
- const timer = setTimeout(() => {
- navigate('/login');
- }, 2000);
- return () => clearTimeout(timer);
+ const timer = setTimeout(() => {
+ const token = localStorage.getItem('token');
+ if (token) {
+ navigate('/pirocheck');
+ } else {
+ navigate('/login');
+ }
+ }, 2000);
+ return () => clearTimeout(timer);
}, []);
return (
diff --git a/frontend/src/pages/login/LoginPage.js b/frontend/src/pages/login/LoginPage.js
index a232d89..bac5e88 100644
--- a/frontend/src/pages/login/LoginPage.js
+++ b/frontend/src/pages/login/LoginPage.js
@@ -1,5 +1,6 @@
import { useState } from 'react';
-import { useNavigate } from 'react-router-dom'; // 추가
+import { useNavigate } from 'react-router-dom';
+import { authFetch } from '../../utils/Api';
import styles from './LoginPage.module.css';
function LoginPage() {
@@ -15,7 +16,7 @@ function LoginPage() {
const handleLogin = async () => {
try {
- const response = await fetch('/api/auth/login', {
+ const response = await authFetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form),
diff --git a/frontend/src/pages/pirocheck/PIroCheckMain.js b/frontend/src/pages/pirocheck/PIroCheckMain.js
index 678c45a..8faf3a2 100644
--- a/frontend/src/pages/pirocheck/PIroCheckMain.js
+++ b/frontend/src/pages/pirocheck/PIroCheckMain.js
@@ -1,4 +1,3 @@
-import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import styles from './PIroCheckMain.module.css';
diff --git a/frontend/src/pages/pirocheck/assignment/Assignment.js b/frontend/src/pages/pirocheck/assignment/Assignment.js
index 9f1644a..81a0f13 100644
--- a/frontend/src/pages/pirocheck/assignment/Assignment.js
+++ b/frontend/src/pages/pirocheck/assignment/Assignment.js
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
+import { authFetch } from '../../../utils/Api';
import styles from './Assignment.module.css';
import LogoImg from '../../../assets/images/logo.png';
import EditIcon from '../../../assets/images/icon_edit.svg';
@@ -14,26 +15,7 @@ const dayMap = {
SATURDAY: 'SAT',
};
-// 임시 데이터 (백엔드 연동 전)
-const MOCK_DATA = [
- {
- week: '1',
- assignments: [
- { assignmentId: 1, title: '코딩앵무 클론 코딩', week: '1', day: 'TUESDAY', sessionDate: 'HTML/CSS, Git 기초', submitted: 'SUBMITTED' },
- { assignmentId: 2, title: '피로그래밍 페이지 클론 코딩', week: '1', day: 'TUESDAY', sessionDate: 'HTML/CSS, Git 기초', submitted: 'NOT_SUBMITTED' },
- { assignmentId: 3, title: '코딩앵무 클론 코딩', week: '1', day: 'THURSDAY', sessionDate: 'JS', submitted: 'SUBMITTED' },
- { assignmentId: 4, title: '숫자야구 게임', week: '1', day: 'THURSDAY', sessionDate: 'JS', submitted: 'LATE' },
- { assignmentId: 5, title: '파이썬 코딩도장', week: '1', day: 'THURSDAY', sessionDate: 'JS', submitted: 'NOT_SUBMITTED' },
- { assignmentId: 6, title: '아르사 팀플', week: '1', day: 'SATURDAY', sessionDate: 'DB 개론', submitted: 'SUBMITTED' },
- ],
- },
- { week: '2', assignments: [] },
- { week: '3', assignments: [] },
- { week: '4', assignments: [] },
- { week: '5', assignments: [] },
-];
-
-const IS_MOCK = true; // 백엔드 연동 시 false로 변경
+const IS_MOCK = false;
// 제출 상태 아이콘 (부원용)
function StatusIcon({ status }) {
@@ -69,7 +51,7 @@ function AssignmentModal({ item, onClose, onSave }) {
? `/api/assignments/modify/${item.assignmentId}`
: '/api/assignments/create';
const method = isEdit ? 'PATCH' : 'POST';
- await fetch(url, {
+ await authFetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: form.title, week: form.week, day: form.day }),
@@ -164,13 +146,9 @@ function Assignment() {
const [modalItem, setModalItem] = useState(undefined); // undefined=닫힘, null=생성, object=수정
const fetchAll = async () => {
- if (IS_MOCK) {
- setWeeks(MOCK_DATA);
- return;
- }
const results = await Promise.all(
['1', '2', '3', '4', '5'].map(w =>
- fetch(`/api/assignments/me/${w}`)
+ authFetch(`/api/assignments/me/${w}`)
.then(r => r.json())
.catch(() => ({ week: w, assignments: [] }))
)
@@ -182,17 +160,12 @@ function Assignment() {
const handleDelete = async (assignmentId) => {
if (!window.confirm('삭제하시겠습니까?')) return;
- if (!IS_MOCK) {
- await fetch(`/api/assignments/${assignmentId}`, { method: 'DELETE' });
- }
+ await authFetch(`/api/assignments/${assignmentId}`, { method: 'DELETE' });
fetchAll();
- };
+ };
return (
- {IS_MOCK && (
-
⚠️ 현재 임시 데이터로 표시 중입니다. 백엔드 연동 후 IS_MOCK을 false로 변경하세요.
- )}
ASSIGNMENT CHECK
{weeks.map((w, i) => (
diff --git a/frontend/src/pages/pirocheck/assignment/Assignment.module.css b/frontend/src/pages/pirocheck/assignment/Assignment.module.css
index 8e38911..5ca20b9 100644
--- a/frontend/src/pages/pirocheck/assignment/Assignment.module.css
+++ b/frontend/src/pages/pirocheck/assignment/Assignment.module.css
@@ -103,7 +103,7 @@
.dayLabel {
color: var(--dark);
- font-family: var(--font-title);
+ font-family: var(--font-main);
font-size: 1.2rem;
font-weight: 700;
}
@@ -212,7 +212,7 @@
}
.modalTitle {
- font-family: var(--font-title);
+ font-family: var(--font-main);
font-size: 3rem;
font-weight: 800;
color: var(--main);
diff --git a/frontend/src/pages/pirocheck/attendance/Attendance.js b/frontend/src/pages/pirocheck/attendance/Attendance.js
index 64d25e2..fe8f350 100644
--- a/frontend/src/pages/pirocheck/attendance/Attendance.js
+++ b/frontend/src/pages/pirocheck/attendance/Attendance.js
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
+import { authFetch } from '../../../utils/Api';
import styles from './Attendance.module.css';
import CloverGreen from '../../../assets/images/CloverGreen.svg';
import CloverRed from '../../../assets/images/CloverRed.svg';
@@ -14,8 +15,11 @@ function cloverForSlot(status) {
return

;
}
-function slotIcon(status) {
- if (status === true) return

;
+function historyIcon(slots) {
+ const successCount = slots.filter(s => s.status === true).length;
+ if (successCount === 3) return

;
+ if (successCount === 2) return

;
+ if (successCount === 1) return

;
return

;
}
@@ -27,7 +31,7 @@ function AdminView() {
useEffect(() => {
const fetchActiveCode = async () => {
try {
- const res = await fetch('/api/admin/attendance/active-code');
+ const res = await authFetch('/api/admin/attendance/active-code');
if (res.ok) {
const data = await res.json();
if (!data.isExpired) {
@@ -41,14 +45,14 @@ function AdminView() {
}, []);
const handleGenerate = async () => {
- const res = await fetch('/api/admin/attendance/start', { method: 'POST' });
+ const res = await authFetch('/api/admin/attendance/start', { method: 'POST' });
const data = await res.json();
setCode(data.code);
setHasCode(true);
};
const handleExpire = async () => {
- await fetch('/api/admin/attendance/active-code/expire', { method: 'PUT' });
+ await authFetch('/api/admin/attendance/active-code/expire', { method: 'PUT' });
setCode(null);
setHasCode(false);
};
@@ -72,7 +76,7 @@ function AdminView() {
종료
)}
-
출석 관리
+
출석 관리
>
);
@@ -96,7 +100,7 @@ function MemberView() {
]
}));
- fetch('/api/attendance/user')
+ authFetch('/api/attendance/user')
.then(r => r.json())
.then(data => {
const apiData = data.data || [];
@@ -112,7 +116,7 @@ function MemberView() {
const handleSubmit = async () => {
if (!inputCode.trim()) return;
- const res = await fetch('/api/attendance/mark', {
+ const res = await authFetch('/api/attendance/mark', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: inputCode }),
@@ -123,7 +127,7 @@ function MemberView() {
if (result.statusCode === 'SUCCESS') {
setMessage('출석 성공!');
const today = new Date().toISOString().split('T')[0];
- fetch(`/api/attendance/user/date?date=${today}`)
+ authFetch(`/api/attendance/user/date?date=${today}`)
.then(r => r.json())
.then(d => setTodaySlots(d.data || []));
} else if (result.statusCode === 'INVALID_CODE') {
@@ -166,10 +170,7 @@ function MemberView() {
{row.week}주차
- {[0, 1, 2].map(j => {
- const slot = row.slots[j] ?? { status: false };
- return
{slotIcon(slot.status)}
;
- })}
+ {historyIcon(row.slots)}
))}
diff --git a/frontend/src/pages/pirocheck/deposit/Deposit.js b/frontend/src/pages/pirocheck/deposit/Deposit.js
index 8b7f2f9..07a1ba2 100644
--- a/frontend/src/pages/pirocheck/deposit/Deposit.js
+++ b/frontend/src/pages/pirocheck/deposit/Deposit.js
@@ -1,40 +1,23 @@
import { useState, useEffect } from 'react';
+import { authFetch } from '../../../utils/Api';
import styles from './Deposit.module.css';
-// 임시 데이터
-const MOCK_DATA = {
- amount: 100000,
- descentAssignment: 10000,
- descentAttendance: 10000,
- ascentDefence: 10000,
-};
-
-const IS_MOCK = true; // 백엔드 연동 시 false로 변경
+const IS_MOCK = false
function Deposit() {
const [deposit, setDeposit] = useState(null);
useEffect(() => {
- if (IS_MOCK) {
- setDeposit(MOCK_DATA);
- return;
- }
- fetch('/api/deposit/me')
+ authFetch('/api/deposit/me')
.then(r => r.json())
.then(data => setDeposit(data))
- .catch(() => setDeposit(MOCK_DATA));
+ .catch(() => {});
}, []);
if (!deposit) return null;
return (
- {IS_MOCK && (
-
- ⚠️ 현재 임시 데이터로 표시 중입니다. 백엔드 연동 후 IS_MOCK을 false로 변경하세요.
-
- )}
-
DEPOSIT CHECK
diff --git a/frontend/src/pages/pirocheck/students/StudentDetail.js b/frontend/src/pages/pirocheck/students/StudentDetail.js
index 6c31628..469c2f7 100644
--- a/frontend/src/pages/pirocheck/students/StudentDetail.js
+++ b/frontend/src/pages/pirocheck/students/StudentDetail.js
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { useParams, useLocation } from 'react-router-dom';
+import { authFetch } from '../../../utils/Api';
import styles from './StudentDetail.module.css';
import ProfileImg from '../../../assets/images/profile.svg';
import Logo2 from '../../../assets/images/logo2.svg';
@@ -191,7 +192,7 @@ function StudentDetail() {
// TODO: GET /api/admin/admin/student/{userId}/status/{week} (1~5주차)
// 커리큘럼에서 세션 제목 가져오기
- const curriculumRes = await fetch('/api/curriculums');
+ const curriculumRes = await authFetch('/api/curriculums');
const curriculums = await curriculumRes.json();
// weeks 데이터에 sessionTitles 추가
diff --git a/frontend/src/pages/pirocheck/students/StudentList.js b/frontend/src/pages/pirocheck/students/StudentList.js
index 98790c8..802bfb4 100644
--- a/frontend/src/pages/pirocheck/students/StudentList.js
+++ b/frontend/src/pages/pirocheck/students/StudentList.js
@@ -1,17 +1,10 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
+import { authFetch } from '../../../utils/Api';
import styles from './StudentList.module.css';
import ArrowRight from '../../../assets/images/icon_arrow_right.svg';
-const MOCK_STUDENTS = [
- { userId: 1, name: '김피로' },
- { userId: 2, name: '이피로' },
- { userId: 3, name: '박피로' },
- { userId: 4, name: '최피로' },
- { userId: 5, name: '정피로' },
-];
-
-const IS_MOCK = true;
+const IS_MOCK = false;
function StudentList() {
const navigate = useNavigate();
@@ -19,20 +12,14 @@ function StudentList() {
const [search, setSearch] = useState('');
const fetchStudents = async (keyword = '') => {
- if (IS_MOCK) {
- if (keyword) {
- setStudents(MOCK_STUDENTS.filter(s => s.name.includes(keyword)));
- } else {
- setStudents(MOCK_STUDENTS);
- }
- return;
- }
- const url = keyword
- ? `/api/admin/studentlist/search?name=${keyword}`
- : '/api/admin/studentlist';
- const res = await fetch(url);
- const data = await res.json();
- setStudents(data);
+ try {
+ const url = keyword
+ ? `/api/admin/studentlist/search?name=${keyword}`
+ : '/api/admin/studentlist';
+ const res = await authFetch(url);
+ const data = await res.json();
+ setStudents(Array.isArray(data) ? data : data.data || []);
+ } catch (e) {}
};
useEffect(() => { fetchStudents(); }, []);
@@ -41,11 +28,6 @@ function StudentList() {
return (
- {IS_MOCK && (
-
- ⚠️ 현재 임시 데이터로 표시 중입니다.
-
- )}
PIROGRAMMER
diff --git a/frontend/src/utils/Api.js b/frontend/src/utils/Api.js
new file mode 100644
index 0000000..91cdd7c
--- /dev/null
+++ b/frontend/src/utils/Api.js
@@ -0,0 +1,11 @@
+export function authFetch(url, options = {}) {
+ const token = localStorage.getItem('token');
+ return fetch(url, {
+ ...options,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ ...options.headers,
+ },
+ });
+}
\ No newline at end of file