diff --git a/src/axios/TokenInterceptor.js b/src/axios/TokenInterceptor.js
index b5f28d1..3bfee88 100644
--- a/src/axios/TokenInterceptor.js
+++ b/src/axios/TokenInterceptor.js
@@ -70,12 +70,16 @@ instance.interceptors.response.use(async function (response) {
});
-const Logout = async () => {
+export const Logout = async () => {
try {
- // 로그아웃 API 호출
- await axios.post(`${LOCAL_SPRING_API_URL}/logout`);
+ const token = localStorage.getItem('accessToken');
+ await axios.post(`${LOCAL_SPRING_API_URL}/logout`, null, {
+ headers: {
+ Authorization: `Bearer ${token}`
+ }
+ });
localStorage.removeItem('accessToken');
- window.location.href = '/'; // 로그인 페이지 이동
+ window.location.href = '/'; // 로그인 페이지로 이동
} catch (error) {
console.error('로그아웃 오류 발생:', error);
}
diff --git a/src/components/oauth/KakaoRedirectPage.js b/src/components/oauth/KakaoRedirectPage.js
index c2cbb35..dbd76c8 100644
--- a/src/components/oauth/KakaoRedirectPage.js
+++ b/src/components/oauth/KakaoRedirectPage.js
@@ -1,60 +1,51 @@
-import React, { useEffect } from "react";
+import React, { useEffect, useRef } from "react";
import { useLocation, useNavigate } from "react-router-dom";
-import instance from "../../axios/TokenInterceptor"; // instance를 계속 사용하려면
+import instance from "../../axios/TokenInterceptor";
import { LOCAL_SPRING_API_URL } from "../../constants/api";
-// 카카오 OAuth 인증 후 리디렉션 처리 페이지
const KakaoRedirectPage = () => {
- const location = useLocation(); // 현재 URL 정보
- const navigate = useNavigate(); // 페이지 이동을 위한 navigate 함수
+ const location = useLocation();
+ const navigate = useNavigate();
+ const isCalled = useRef(false); // 한 번만 실행되도록 플래그
useEffect(() => {
- // 카카오 로그인 인증 코드 처리 함수
const handleOAuthKakao = async (code) => {
try {
- // 카카오 인증 코드로 서버에 로그인 요청 (여기서 instance 사용)
const response = await instance.get(
- `${ LOCAL_SPRING_API_URL}/oauth/login/kakao?code=${code}`
+ `${LOCAL_SPRING_API_URL}/oauth/login/kakao?code=${code}`
);
if (response.data.isSuccess) {
- // 로그인 성공 시, Authorization 헤더에서 액세스 토큰 가져오기
- const accessToken = response.headers["Authorization"] || response.headers["authorization"];
- localStorage.setItem("accessToken", accessToken); // 토큰을 로컬스토리지에 저장
+ const accessToken =
+ response.headers["Authorization"] || response.headers["authorization"];
+ localStorage.setItem("accessToken", accessToken);
- // 사용자의 역할(role)에 따라 페이지 이동
const role = response.data.result;
- if (role === "GUEST") {
- navigate("/voice-training"); // 게스트 페이지로 이동
- } else if (role === "USER") {
- navigate("/voice-training"); // 사용자 메인 페이지로 이동
+ if (role === "ROLE_FIRST") {
+ navigate("/voice-training");
+ } else {
+ navigate("/keywords");
}
} else {
- // 로그인 실패 처리
console.error("OAuth2 로그인 오류");
console.log(response.data.code);
console.log(response.data.message);
}
} catch (error) {
- // 요청 실패 시 처리
console.error("로그인 실패", error);
}
};
- // URL에서 카카오 인증 코드 추출
const searchParams = new URLSearchParams(location.search);
- const code = searchParams.get("code"); // URL에서 'code' 파라미터 추출
+ const code = searchParams.get("code");
- // 인증 코드가 존재하면 로그인 처리
- if (code) {
+ if (code && !isCalled.current) {
+ isCalled.current = true; // ✅ 한 번만 호출되도록 설정
handleOAuthKakao(code);
}
- }, [location, navigate]); // 의존성 배열에 'location'과 'navigate' 추가
+ }, [location, navigate]);
- return (
-
-
- );
+ return ;
};
export default KakaoRedirectPage;
diff --git a/src/pages/FinalPage.js b/src/pages/FinalPage.js
index 57483c3..a8975c6 100644
--- a/src/pages/FinalPage.js
+++ b/src/pages/FinalPage.js
@@ -1,6 +1,8 @@
+// FinalPage.jsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Logo from "../components/Logo";
+import { Logout } from "../axios/TokenInterceptor"; // 실제 axios 파일 경로에 맞게 수정
const FinalPage = () => {
const navigate = useNavigate();
@@ -10,9 +12,9 @@ const FinalPage = () => {
setShowConfirm(true);
};
- const handleConfirmYes = () => {
+ const handleConfirmYes = async () => {
setShowConfirm(false);
- navigate('/');
+ await Logout(); // 로그아웃 처리 및 이동
};
const handleConfirmNo = () => {
@@ -41,6 +43,7 @@ const FinalPage = () => {
);
};
+
const styles = {
container: {
backgroundColor: '#F4E6CE',
diff --git a/src/pages/KeywordSelectionPage.js b/src/pages/KeywordSelectionPage.js
index 251fbd9..4ce71cc 100644
--- a/src/pages/KeywordSelectionPage.js
+++ b/src/pages/KeywordSelectionPage.js
@@ -1,12 +1,12 @@
-import React, { useState } from "react";
-import { useNavigate } from "react-router-dom";
+import React, { useState, useEffect } from "react";
+import { useNavigate, useParams } from "react-router-dom";
import TimePicker from "react-time-picker";
import "react-time-picker/dist/TimePicker.css";
-import { LOCAL_SPRING_API_URL } from "../constants/api";
import "react-clock/dist/Clock.css";
import Logo from "../components/Logo";
import axios from "axios";
-import { getAccessToken } from "../components/Header"; // 토큰 불러오기
+import { LOCAL_SPRING_API_URL } from "../constants/api";
+import { getAccessToken } from "../components/Header";
const keywords = {
"수면 여부 확인": ["아침", "밤"],
@@ -16,29 +16,34 @@ const keywords = {
"심리적 상태 체크": ["O", "X"]
};
-const timeSettingKeywords = {
- "수면 여부 확인": ["아침"],
- "식사 여부 확인": ["아침", "점심", "저녁"],
- "약 복용 여부 확인": ["아침", "점심", "저녁"]
-};
+const timeSettingKeywords = new Set([
+ "수면 여부 확인", "식사 여부 확인", "약 복용 여부 확인", "활동 여부 확인"
+]);
const weekdays = ["월", "화", "수", "목", "금", "토", "일"];
+const dayMap = {
+ "월": "MONDAY", "화": "TUESDAY", "수": "WEDNESDAY",
+ "목": "THURSDAY", "금": "FRIDAY", "토": "SATURDAY", "일": "SUNDAY"
+};
+const reverseDayMap = Object.fromEntries(
+ Object.entries(dayMap).map(([k, v]) => [v, k])
+);
const TimeSetting = ({ value, onSave }) => {
- const [tempTime, setTempTime] = useState(value.time);
- const [tempDays, setTempDays] = useState(value.days || []);
+ const [time, setTime] = useState(value.time);
+ const [days, setDays] = useState(value.days || []);
const toggleDay = (day) => {
- setTempDays((prev) =>
- prev.includes(day) ? prev.filter((d) => d !== day) : [...prev, day]
- );
+ const newDays = days.includes(day)
+ ? days.filter(d => d !== day)
+ : [...days, day];
+ setDays(newDays);
+ onSave({ time, days: newDays });
};
- const handleSave = () => {
- onSave({
- time: tempTime,
- days: tempDays
- });
+ const handleTimeChange = (newTime) => {
+ setTime(newTime);
+ onSave({ time: newTime, days });
};
return (
@@ -50,7 +55,7 @@ const TimeSetting = ({ value, onSave }) => {
onClick={() => toggleDay(day)}
style={{
...styles.dayButton,
- backgroundColor: tempDays.includes(day) ? "#DABEC9" : "#EEE"
+ backgroundColor: days.includes(day) ? "#DABEC9" : "#EEE"
}}
>
{day}
@@ -58,36 +63,102 @@ const TimeSetting = ({ value, onSave }) => {
))}
-
);
};
+const KeywordOption = ({ category, keyword, data, onToggle, onSave }) => (
+
+
+ {data.selected && timeSettingKeywords.has(category) && (
+
+ )}
+
+);
+
const KeywordSelectionPage = () => {
const navigate = useNavigate();
+ const { id } = useParams();
const [selected, setSelected] = useState(
- Object.keys(keywords).reduce((acc, category) => {
- acc[category] = {};
- keywords[category].forEach((keyword) => {
- acc[category][keyword] = {
- selected: false,
- time: "08:00",
- days: []
- };
- });
- return acc;
- }, {})
+ Object.fromEntries(
+ Object.entries(keywords).map(([category, list]) => [
+ category,
+ Object.fromEntries(
+ list.map((keyword) => [
+ keyword,
+ { selected: false, time: "08:00", days: [] }
+ ])
+ )
+ ])
+ )
);
+ useEffect(() => {
+ const fetchData = async () => {
+ const token = getAccessToken();
+ if (!token) return;
+
+ try {
+ const response = await axios.get(
+ `${LOCAL_SPRING_API_URL}/all-basic-schedules`,
+ {
+ headers: { Authorization: `Bearer ${token}` },
+ }
+ );
+ console.log("확인하기");
+ const schedules = response.data.result;
+ console.log("JSON 파싱 직전");
+ console.log(schedules);
+
+ const updatedSelected = JSON.parse(JSON.stringify(selected));
+
+ schedules.forEach(item => {
+ const [categoryPrefix, keyword] = item.scheduleTitle.split("_");
+ const category = Object.keys(keywords).find(
+ c => c.replace(/\s/g, "") === categoryPrefix
+ );
+ if (!category || !keywords[category].includes(keyword)) return;
+
+ const time = item.startTime.slice(0, 5);
+ const days = item.days.map(d => reverseDayMap[d]).filter(Boolean);
+
+ updatedSelected[category][keyword] = {
+ selected: true,
+ time,
+ days
+ };
+ });
+
+ setSelected(updatedSelected);
+ } catch (error) {
+ console.error("기존 키워드 데이터를 불러오는 중 오류 발생:", error);
+ }
+ };
+
+ fetchData();
+ }, [id]);
+
const toggleSelection = (category, keyword) => {
- setSelected((prev) => ({
+ setSelected(prev => ({
...prev,
[category]: {
...prev[category],
@@ -100,14 +171,11 @@ const KeywordSelectionPage = () => {
};
const saveTimeAndDays = (category, keyword, newData) => {
- setSelected((prev) => ({
+ setSelected(prev => ({
...prev,
[category]: {
...prev[category],
- [keyword]: {
- ...prev[category][keyword],
- ...newData
- }
+ [keyword]: { ...prev[category][keyword], ...newData }
}
}));
};
@@ -118,37 +186,23 @@ const KeywordSelectionPage = () => {
alert("로그인이 필요합니다.");
return;
}
-
- // 요일 변환 맵
- const dayMap = {
- "월": "MONDAY",
- "화": "TUESDAY",
- "수": "WEDNESDAY",
- "목": "THURSDAY",
- "금": "FRIDAY",
- "토": "SATURDAY",
- "일": "SUNDAY"
- };
-
- // selected 가공
+
const payload = [];
-
- for (const category in selected) {
- for (const keyword in selected[category]) {
- const item = selected[category][keyword];
- if (item.selected && item.days.length > 0) {
+ for (const [category, options] of Object.entries(selected)) {
+ for (const [keyword, { selected, time, days }] of Object.entries(options)) {
+ if (selected && days.length > 0) {
payload.push({
- scheduleTitle: `${category} - ${keyword}`,
- startTime: item.time + ":00",
- days: item.days.map(day => dayMap[day])
+ scheduleTitle: `${category.replace(/\s/g, "")}_${keyword}`,
+ startTime: time + ":00",
+ days: days.map(d => dayMap[d])
});
}
}
}
-
+
try {
const response = await axios.post(
- `${ LOCAL_SPRING_API_URL }/basic-schedules`, // 실제 API 주소로 교체
+ `${LOCAL_SPRING_API_URL}/basic-schedules`,
payload,
{
headers: {
@@ -173,28 +227,14 @@ const KeywordSelectionPage = () => {
• {category}
{options.map((keyword) => (
-
-
- {selected[category][keyword].selected &&
- timeSettingKeywords[category]?.includes(keyword) && (
- saveTimeAndDays(category, keyword, data)}
- />
- )}
-
+
toggleSelection(category, keyword)}
+ onSave={(data) => saveTimeAndDays(category, keyword, data)}
+ />
))}
@@ -279,14 +319,6 @@ const styles = {
border: "1px solid #888",
cursor: "pointer",
fontSize: "12px",
- },
- saveButton: {
- fontSize: "12px",
- padding: "5px 10px",
- borderRadius: "5px",
- backgroundColor: "#DABEC9",
- border: "none",
- cursor: "pointer"
}
};
diff --git a/src/pages/LoginPage.js b/src/pages/LoginPage.js
index 644963c..53d7793 100644
--- a/src/pages/LoginPage.js
+++ b/src/pages/LoginPage.js
@@ -2,7 +2,7 @@ import React from "react";
import { useNavigate } from "react-router-dom";
import Button from "../components/Button";
import Logo from "../components/Logo";
-import { LOCAL_SPRING_API_URL } from "../../constants/api";
+import { LOCAL_SPRING_API_URL } from "../constants/api";
const LoginPage = () => {
const navigate = useNavigate();