diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f0b620f..e969afb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -40,6 +40,7 @@ jobs: - name: Copy JAR to EC2 run: | scp -o StrictHostKeyChecking=no -i pirocheck.pem backend/pirocheck/build/libs/*.jar ubuntu@${{ secrets.EC2_HOST }}:/home/ubuntu/ + - name: Restart Spring Boot on EC2 run: | @@ -106,4 +107,4 @@ jobs: AWS_S3_BUCKET: www.pirocheck.org AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE_DIR: frontend/dist \ No newline at end of file + SOURCE_DIR: frontend/dist diff --git a/backend/pirocheck/src/main/java/backend/pirocheck/config/WebConfig.java b/backend/pirocheck/src/main/java/backend/pirocheck/config/WebConfig.java index a6ae980..f67ed5b 100644 --- a/backend/pirocheck/src/main/java/backend/pirocheck/config/WebConfig.java +++ b/backend/pirocheck/src/main/java/backend/pirocheck/config/WebConfig.java @@ -10,7 +10,7 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") // 백엔드 API 요청에만 CORS 허용 - .allowedOrigins("http://pirocheck.org:3000") // 프론트 배포 URL + .allowedOrigins("http://pirocheck.org") // 프론트 배포 URL .allowedMethods("GET", "POST", "PUT", "DELETE") // 허용할 HTTP 메서드 .allowCredentials(true); // 세션 쿠키 주고받기 허용 } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index db217ff..50530e1 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,11 +2,11 @@ import React from "react"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import Login from "./Login"; import Home from "./Home"; -import Assignment from "./Assignment"; -import Deposit from "./Deposit"; +import Assignment from "./pages/generation/Assignment"; +import Deposit from "./pages/generation/Deposit"; import Intro from "./Intro"; -import Admin from "./Admin"; -import Attendance from "./Attendance"; +import Admin from "./pages/admin/Admin"; +import Attendance from "./pages/generation/Attendance"; function App() { return ( diff --git a/frontend/src/api/api.js b/frontend/src/api/api.js new file mode 100644 index 0000000..9ba4ee2 --- /dev/null +++ b/frontend/src/api/api.js @@ -0,0 +1,19 @@ +import axios from "axios"; + +const api = axios.create({ + baseURL: "http://api.pirocheck.org:8080/api", + withCredentials: true, +}); + +// 401 오류 시 로그인 페이지로 리다이렉트 +api.interceptors.response.use( + (response) => response, + (error) => { + if (error.response?.status === 401) { + window.location.href = "/login"; + } + return Promise.reject(error); + } +); + +export default api; diff --git a/frontend/src/api/assignment.js b/frontend/src/api/assignment.js index a576b62..cf1a11a 100644 --- a/frontend/src/api/assignment.js +++ b/frontend/src/api/assignment.js @@ -1,8 +1,6 @@ -import axios from "axios"; +import api from "./api"; export const fetchAssignmentsByUser = async (userId) => { - const res = await axios.get(`/api/assignment/grouped/${userId}`, { - withCredentials: true - }); + const res = await api.get(`/assignment/grouped/${userId}`); return res.data; }; diff --git a/frontend/src/api/user.js b/frontend/src/api/user.js index 3964bac..12e057a 100644 --- a/frontend/src/api/user.js +++ b/frontend/src/api/user.js @@ -1,5 +1,5 @@ export const loginUser = async ({ name, password }) => { - const res = await fetch("/api/login", { + const res = await fetch("http://api.pirocheck.org:8080/api/login", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/src/Admin.jsx b/frontend/src/pages/admin/Admin.jsx similarity index 100% rename from frontend/src/Admin.jsx rename to frontend/src/pages/admin/Admin.jsx diff --git a/frontend/src/Assignment.jsx b/frontend/src/pages/generation/Assignment.jsx similarity index 64% rename from frontend/src/Assignment.jsx rename to frontend/src/pages/generation/Assignment.jsx index cff140f..d1c0115 100644 --- a/frontend/src/Assignment.jsx +++ b/frontend/src/pages/generation/Assignment.jsx @@ -1,9 +1,10 @@ import React, { useEffect, useState } from "react"; -import WeeklyListBlock from "./components/WeeklyListBlock"; -import Header from "./components/Header"; -import AssignmentInfoBlock from "./components/AssignmentInfoBlock"; +import WeeklyListBlock from "../../components/WeeklyListBlock"; +import Header from "../../components/Header"; +import AssignmentInfoBlock from "../../components/AssignmentInfoBlock"; import styles from "./Assignment.module.css"; -import { mapStatus } from "./utils/AssignmentStatus.js"; +import { mapStatus } from "../../utils/AssignmentStatus.js"; +import { fetchAssignmentsByUser } from "../../api/assignment.js"; const Assignment = () => { const [weeks, setWeeks] = useState([]); @@ -16,18 +17,18 @@ const Assignment = () => { if (!userId) return; fetchAssignmentsByUser(userId) - .then((weekData) => { - const formatted = weekData.map((weekItem) => ({ - label: `${weekItem.week}주차 ${weekItem.title}`, - details: weekItem.days.map((dayItem) => ({ - day: dayItem.day, - subject: weekItem.title, - tasks: dayItem.details.map((task) => ({ - label: task.assignmentName, - status: mapStatus(task.status), + .then((weekData) => { + const formatted = weekData.map((weekItem) => ({ + label: `${weekItem.week}주차 ${weekItem.title}`, + details: weekItem.days.map((dayItem) => ({ + day: dayItem.day, + subject: weekItem.title, + tasks: dayItem.details.map((task) => ({ + label: task.assignmentName, + status: mapStatus(task.status), + })), })), - })), - })); + })); setWeeks(formatted); @@ -63,4 +64,3 @@ const Assignment = () => { }; export default Assignment; - diff --git a/frontend/src/Assignment.module.css b/frontend/src/pages/generation/Assignment.module.css similarity index 100% rename from frontend/src/Assignment.module.css rename to frontend/src/pages/generation/Assignment.module.css diff --git a/frontend/src/Attendance.jsx b/frontend/src/pages/generation/Attendance.jsx similarity index 95% rename from frontend/src/Attendance.jsx rename to frontend/src/pages/generation/Attendance.jsx index 1abe9db..dce08b1 100644 --- a/frontend/src/Attendance.jsx +++ b/frontend/src/pages/generation/Attendance.jsx @@ -1,9 +1,9 @@ import { useEffect, useState } from "react"; -import Header from "./components/Header"; -import InputBlock from "./components/InputBlock"; -import AttendanceWeekInfo from "./components/AttendanceWeekInfo"; +import Header from "../../components/Header"; +import InputBlock from "../../components/InputBlock"; +import AttendanceWeekInfo from "../../components/AttendanceWeekInfo"; import styles from "./Attendance.module.css"; -import axios from "axios"; +import api from "../../api/api"; const Attendance = () => { const [attendanceCode, setAttendanceCode] = useState([""]); @@ -88,7 +88,7 @@ const Attendance = () => { if (!userId) return; // 유저 전체 출석 데이터 불러오기 - const res = await axios.get(`/api/attendance/user`, { + const res = await api.get(`/attendance/user`, { params: { userId }, withCredentials: true, // 세션 기반 인증 요청처리 }); @@ -108,7 +108,7 @@ const Attendance = () => { if (!userId) return; const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD - const res = await axios.get(`/api/attendance/user/date`, { + const res = await api.get(`/attendance/user/date`, { params: { userId, date: today }, withCredentials: true, // 세션 기반 인증 요청처리 }); @@ -151,8 +151,10 @@ const Attendance = () => { if (!userId) return; // 유저가 입력한 출석 코드 서버에 전달(서버에서 출석코드 체크) + const res = await axios.post( "/api/attendance/mark", + { userId, code: attendanceCode[0], diff --git a/frontend/src/Attendance.module.css b/frontend/src/pages/generation/Attendance.module.css similarity index 100% rename from frontend/src/Attendance.module.css rename to frontend/src/pages/generation/Attendance.module.css diff --git a/frontend/src/Deposit.jsx b/frontend/src/pages/generation/Deposit.jsx similarity index 88% rename from frontend/src/Deposit.jsx rename to frontend/src/pages/generation/Deposit.jsx index 500de27..8b9c17b 100644 --- a/frontend/src/Deposit.jsx +++ b/frontend/src/pages/generation/Deposit.jsx @@ -1,7 +1,8 @@ -import Header from "./components/Header"; +import Header from "../../components/Header"; import styles from "./Deposit.module.css"; import axios from "axios"; import { useEffect, useState } from "react"; +import api from "../../api/api"; const Deposit = () => { const [deposit, setDeposit] = useState(null); @@ -12,10 +13,8 @@ const Deposit = () => { if (!userId) return; - axios - .get(`/api/deposit/${userId}`, { - withCredentials: true, // 세션 쿠키 포함 - }) + api + .get(`/deposit/${userId}`) .then((res) => setDeposit(res.data)) .catch((err) => { alert("보증금 정보를 불러오지 못했습니다."); diff --git a/frontend/src/Deposit.module.css b/frontend/src/pages/generation/Deposit.module.css similarity index 100% rename from frontend/src/Deposit.module.css rename to frontend/src/pages/generation/Deposit.module.css