diff --git a/FE/src/App.js b/FE/src/App.js index c135988..890b939 100644 --- a/FE/src/App.js +++ b/FE/src/App.js @@ -6,7 +6,6 @@ import { createGlobalStyle } from "styled-components"; import { useRecoilState, useSetRecoilState } from "recoil"; import { Route, Routes } from "react-router-dom"; import userAtom from "./atoms/userAtom"; -import diaryAtom from "./atoms/diaryAtom"; import Header from "./components/Header/Header"; import HomePage from "./pages/HomePage"; import MainPage from "./pages/MainPage"; @@ -42,7 +41,6 @@ const GlobalStyle = createGlobalStyle` function App() { const [userState, setUserState] = useRecoilState(userAtom); - const setDiaryState = useSetRecoilState(diaryAtom); useLayoutEffect(() => { let accessToken = localStorage.getItem("accessToken"); @@ -68,12 +66,6 @@ function App() { window.history.replaceState({}, "", "/"); } } - - window.onpopstate = (event) => { - if (event.state) { - setDiaryState(event.state); - } - }; }, []); return ( diff --git a/FE/src/components/Button/SwitchButton.js b/FE/src/components/Button/SwitchButton.js index 432beaf..a0b55e8 100644 --- a/FE/src/components/Button/SwitchButton.js +++ b/FE/src/components/Button/SwitchButton.js @@ -2,9 +2,7 @@ import React, { useState } from "react"; import styled from "styled-components"; function SwitchButton(props) { - // 위치 설정 관련 props const { bottom, right } = props; - // 버튼 내용 / 이벤트 관련 props const { leftContent, rightContent, leftEvent, rightEvent } = props; const [current, setCurrent] = useState(leftContent); diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index 7f0818d..c10bfa1 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -12,8 +12,11 @@ import Tag from "../../styles/Modal/Tag"; import leftIcon from "../../assets/leftIcon.svg"; import rightIcon from "../../assets/rightIcon.svg"; import logoNoText from "../../assets/logo-notext.svg"; +import handleResponse from "../../utils/handleResponse"; function DiaryAnalysisModal() { + const [userState, setUserState] = useRecoilState(userAtom); + const setDiaryState = useSetRecoilState(diaryAtom); const [buttonDisabled, setButtonDisabled] = useState(false); const [currentYear, setCurrentYear] = useState(dayjs("2023")); const [emotion, setEmotion] = useState({ @@ -22,8 +25,6 @@ function DiaryAnalysisModal() { neutral: 0, }); const [monthAnalysis, setMonthAnalysis] = useState(Array(12).fill(0)); - const [userState, setUserState] = useRecoilState(userAtom); - const setDiaryState = useSetRecoilState(diaryAtom); const [hoverData, setHoverData] = useState({ top: 0, left: 0, @@ -40,40 +41,24 @@ function DiaryAnalysisModal() { Authorization: `Bearer ${userState.accessToken}`, }, }, - ).then((res) => { - if (res.status === 200) { - return res.json(); - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - }); - } - return {}; - }); + ).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 200, + onSuccessCallback: () => res.json(), + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + }, + }), + ); } const { data: tagsRankData, refetch: tagsRankRefetch } = useQuery( @@ -195,14 +180,11 @@ function DiaryAnalysisModal() { {day} ))} - { - // dayjs로 1월 1일 이 무슨 요일인지 알아내서 그거에 맞게 빈칸 넣어주기 - Array.from({ length: currentYear.day() }, (v, i) => i + 1).map( - (day) => ( - - ), - ) - } + {Array.from({ length: currentYear.day() }, (v, i) => i + 1).map( + (day) => ( + + ), + )} {Array.from( { length: @@ -470,7 +452,6 @@ function ShapeRanking(props) { ); } -// 일기 나열 페이지와 중복되는 부분이 많아서 일단은 일기 나열 페이지를 재활용했습니다. const DiaryAnalysisModalWrapper = styled.div` width: 95%; height: 97.5%; diff --git a/FE/src/components/DiaryModal/DiaryCreateModal.js b/FE/src/components/DiaryModal/DiaryCreateModal.js index 1ad9e89..3d15295 100644 --- a/FE/src/components/DiaryModal/DiaryCreateModal.js +++ b/FE/src/components/DiaryModal/DiaryCreateModal.js @@ -12,14 +12,14 @@ import Calendar from "./Calendar"; import close from "../../assets/close.svg"; import getFormattedDate from "../../utils/utils"; import ModalBackground from "../ModalBackground/ModalBackground"; +import handleResponse from "../../utils/handleResponse"; function DiaryCreateModal(props) { const { refetch } = props; - const [isInput, setIsInput] = useState(false); const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const [userState, setUserState] = useRecoilState(userAtom); + const [isInput, setIsInput] = useState(false); - // TODO: 날짜 선택 기능 구현 const [diaryData, setDiaryData] = useState({ title: "", content: "", @@ -68,41 +68,24 @@ function DiaryCreateModal(props) { }, body: JSON.stringify(formattedDiaryData), }) - .then((res) => { - if (res.status === 201) { - return res.json(); - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - createDiary({ diaryData, accessToken: data.accessToken }); - }); - } - throw new Error("다이어리 생성에 실패했습니다."); - }) + .then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 201, + onSuccessCallback: () => res.json(), + on403Callback: () => + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })), + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + createDiary({ diaryData, accessToken }); + }, + }), + ) .then(() => { refetch(); setDiaryState((prev) => ({ @@ -112,11 +95,7 @@ function DiaryCreateModal(props) { }); } - const { - mutate: createDiary, - // isLoading: diaryIsLoading, - // isError: diaryIsError, - } = useMutation(createDiaryFn); + const { mutate: createDiary } = useMutation(createDiaryFn); return ( <> diff --git a/FE/src/components/DiaryModal/DiaryDeleteModal.js b/FE/src/components/DiaryModal/DiaryDeleteModal.js index 0f9f404..c625ca8 100644 --- a/FE/src/components/DiaryModal/DiaryDeleteModal.js +++ b/FE/src/components/DiaryModal/DiaryDeleteModal.js @@ -6,6 +6,7 @@ import diaryAtom from "../../atoms/diaryAtom"; import userAtom from "../../atoms/userAtom"; import lastPageAtom from "../../atoms/lastPageAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; +import handleResponse from "../../utils/handleResponse"; function DiaryDeleteModal(props) { const { refetch, pointsRefetch } = props; @@ -24,55 +25,35 @@ function DiaryDeleteModal(props) { }, }, ) - .then((res) => { - if (res.status === 204) { - return res; - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - deleteDiary({ - diaryUuid: diaryState.diaryUuid, - accessToken: data.accessToken, - }); + .then((res) => + handleResponse(res, data.accessToken, { + successStatus: 204, + onSuccessCallback: () => {}, + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + deleteDiary({ + diaryUuid: diaryState.diaryUuid, + accessToken, }); - } - throw new Error("일기 삭제 실패"); - }) + }, + }), + ) .then(() => { refetch(); pointsRefetch(); }); } - const { - mutate: deleteDiary, - // isLoading, - // error, - } = useMutation(deleteDiaryFn); + const { mutate: deleteDiary } = useMutation(deleteDiaryFn); return ( diff --git a/FE/src/components/DiaryModal/DiaryListModal.js b/FE/src/components/DiaryModal/DiaryListModal.js index 76906ac..755df85 100644 --- a/FE/src/components/DiaryModal/DiaryListModal.js +++ b/FE/src/components/DiaryModal/DiaryListModal.js @@ -1,6 +1,6 @@ /* eslint-disable */ -import React, { useEffect, useLayoutEffect } from "react"; +import React, { useEffect, useLayoutEffect, useState } from "react"; import styled from "styled-components"; import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; import DatePicker from "react-datepicker"; @@ -13,11 +13,11 @@ import zoomIn from "../../assets/zoomIn.svg"; import search from "../../assets/search.svg"; function DiaryListModal() { - const [selectedDiary, setSelectedDiary] = React.useState(null); const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const shapeState = useRecoilValue(shapeAtom); const setLastPageState = useSetRecoilState(lastPageAtom); - const [filterState, setFilterState] = React.useState({ + const [selectedDiary, setSelectedDiary] = useState(null); + const [filterState, setFilterState] = useState({ date: { start: null, end: null, @@ -30,7 +30,7 @@ function DiaryListModal() { shape: [], tag: [], }); - const [filteredDiaryList, setFilteredDiaryList] = React.useState([]); + const [filteredDiaryList, setFilteredDiaryList] = useState([]); useLayoutEffect(() => { if (diaryState.diaryList) { diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index fdbcade..1bbc183 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { useQuery } from "react-query"; import styled from "styled-components"; import { useRecoilState, useRecoilValue } from "recoil"; @@ -14,6 +14,7 @@ import editIcon from "../../assets/edit.svg"; import deleteIcon from "../../assets/delete.svg"; import close from "../../assets/close.svg"; import ModalBackground from "../ModalBackground/ModalBackground"; +import handleResponse from "../../utils/handleResponse"; async function getDiary(accessToken, diaryUuid, setUserState, setDiaryState) { return fetch(`${process.env.REACT_APP_BACKEND_URL}/diaries/${diaryUuid}`, { @@ -22,40 +23,24 @@ async function getDiary(accessToken, diaryUuid, setUserState, setDiaryState) { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, }, - }).then((res) => { - if (res.status === 200) { - return res.json(); - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - }); - } - return {}; - }); + }).then((res) => + handleResponse(res, accessToken, { + successStatus: 200, + onSuccessCallback: () => res.json(), + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + }, + }), + ); } function DiaryReadModal(props) { @@ -64,7 +49,8 @@ function DiaryReadModal(props) { const [userState, setUserState] = useRecoilState(userAtom); const [lastPageState, setLastPageState] = useRecoilState(lastPageAtom); const shapeState = useRecoilValue(shapeAtom); - const [shapeData, setShapeData] = React.useState(""); + const [shapeData, setShapeData] = useState(""); + const { data, isLoading, isError } = useQuery( ["diary", userState.accessToken], () => @@ -213,7 +199,6 @@ function DiaryReadModal(props) { ); } -// ToDo: 통합 필요 const DiaryModalHeader = styled.div` width: 100%; display: flex; diff --git a/FE/src/components/DiaryModal/DiaryUpdateModal.js b/FE/src/components/DiaryModal/DiaryUpdateModal.js index 4c6a593..b89b4e3 100644 --- a/FE/src/components/DiaryModal/DiaryUpdateModal.js +++ b/FE/src/components/DiaryModal/DiaryUpdateModal.js @@ -12,15 +12,15 @@ import Calendar from "./Calendar"; import close from "../../assets/close.svg"; import getFormattedDate from "../../utils/utils"; import ModalBackground from "../ModalBackground/ModalBackground"; +import handleResponse from "../../utils/handleResponse"; -// TODO: 일기 데이터 수정 API 연결 function DiaryUpdateModal(props) { const { refetch } = props; + const [userState, setUserState] = useRecoilState(userAtom); + const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const titleRef = useRef(null); const contentRef = useRef(null); const [isInput, setIsInput] = useState(true); - const [userState, setUserState] = useRecoilState(userAtom); - const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const [diaryData, setDiaryData] = useState({ uuid: diaryState.diaryUuid, title: "", @@ -33,11 +33,7 @@ function DiaryUpdateModal(props) { )?.shapeUuid, }); - const { - mutate: updateDiary, - // isLoading: diaryIsLoading, - // isError: diaryIsError, - } = useMutation(updateDiaryFn); + const { mutate: updateDiary } = useMutation(updateDiaryFn); const { data: originData, @@ -98,48 +94,34 @@ function DiaryUpdateModal(props) { Authorization: `Bearer ${data.accessToken}`, }, body: JSON.stringify(formattedDiaryData), - }).then((res) => { - if (res.status === 204) { - refetch(); - setDiaryState((prev) => ({ - ...prev, - isRead: true, - })); - } - if (res.status === 403) { - console.log("권한 없음"); - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - updateDiary({ - diaryData: diaryData, - accessToken: data.accessToken, - }); + }).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 204, + onSuccessCallback: () => { + refetch(); + setDiaryState((prev) => ({ + ...prev, + isRead: true, + })); + }, + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + updateDiary({ + diaryData: diaryData, + accessToken, }); - } - }); + }, + }), + ); } async function getDiary(accessToken, diaryUuid) { diff --git a/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js b/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js index dc3f3d5..946b6cb 100644 --- a/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js +++ b/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js @@ -5,6 +5,7 @@ import indicatorArrowIcon from "../../../assets/indicator-arrow.svg"; function DiaryEmotionIndicator({ emotion, width, text }) { const [isHover, setIsHover] = useState(""); + return ( diff --git a/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js b/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js index 4595d62..440dc04 100644 --- a/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js +++ b/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js @@ -2,7 +2,9 @@ import React from "react"; import styled from "styled-components"; import picket from "../../../assets/picket.svg"; -function EmotionPicket({ percent }) { +function EmotionPicket(props) { + const { percent } = props; + return ( {percent.toFixed(1)}% diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 46647a7..8927e83 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -13,11 +13,11 @@ import kakao from "../../assets/kakao.png"; import naver from "../../assets/naver.png"; function LoginModal() { + const setUserState = useSetRecoilState(userAtom); + const setHeaderState = useSetRecoilState(headerAtom); const [userId, setUserId] = useState(""); const [keepLogin, setKeepLogin] = useState(false); const [password, setPassword] = useState(""); - const setUserState = useSetRecoilState(userAtom); - const setHeaderState = useSetRecoilState(headerAtom); const errorRef = useRef(); const { mutate: login } = useMutation(() => { diff --git a/FE/src/components/PurchaseModal/PurchaseModal.js b/FE/src/components/PurchaseModal/PurchaseModal.js index 275d75c..6b9d327 100644 --- a/FE/src/components/PurchaseModal/PurchaseModal.js +++ b/FE/src/components/PurchaseModal/PurchaseModal.js @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { useQuery, useMutation } from "react-query"; import styled from "styled-components"; -import { useRecoilState } from "recoil"; +import { useRecoilState, useSetRecoilState } from "recoil"; import userAtom from "../../atoms/userAtom"; import leftIcon from "../../assets/leftIcon.svg"; import rightIcon from "../../assets/rightIcon.svg"; @@ -9,13 +9,17 @@ import oneStar from "../../assets/onestar.svg"; import twoStar from "../../assets/twostar.svg"; import threeStar from "../../assets/threestar.svg"; import fourStar from "../../assets/fourstar.svg"; +import diaryAtom from "../../atoms/diaryAtom"; +import handleResponse from "../../utils/handleResponse"; -function PurchaseModal() { - const [x, setX] = useState(0); +function PurchaseModal(props) { + const { premiumRefetch } = props; const [userState, setUserState] = useRecoilState(userAtom); + const setDiaryState = useSetRecoilState(diaryAtom); + const [x, setX] = useState(0); const { data: creditData, refetch: creditRefetch } = useQuery( - ["credit"], + ["credit", userState.accessToken], async () => fetch(`${process.env.REACT_APP_BACKEND_URL}/purchase/credit`, { method: "GET", @@ -23,34 +27,24 @@ function PurchaseModal() { "Content-Type": "application/json", Authorization: `Bearer ${userState.accessToken}`, }, - }).then(async (res) => { - if (res.status === 200) { - return res.json(); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - }); - } - return {}; - }), + }).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 200, + onSuccessCallback: () => res.json(), + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + }, + }), + ), ); const { mutate: purchase } = useMutation((data) => { @@ -61,7 +55,7 @@ function PurchaseModal() { method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, + Authorization: `Bearer ${data.accessToken}`, }, }; @@ -73,38 +67,30 @@ function PurchaseModal() { fetch( `${process.env.REACT_APP_BACKEND_URL}/purchase/${data.item}`, fetchData, - ).then(async (res) => { - if (res.status === 201) { - alert("구매가 완료되었습니다."); - creditRefetch(); - } - if (res.status === 400) { - alert((await res.json()).message); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); + ).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 201, + onSuccessCallback: () => { + alert("구매가 완료되었습니다."); + creditRefetch(); + premiumRefetch(); + }, + on400Callback: async () => { + alert((await res.json()).message); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + purchase({ + credit: data.credit, + item: data.item, + accessToken, }); - } - return {}; - }); + }, + }), + ); } }); @@ -139,7 +125,11 @@ function PurchaseModal() { { - purchase({ credit: 100, item: "premium" }); + purchase({ + credit: 350, + item: "premium", + accessToken: userState.accessToken, + }); }} > 광고 제거 diff --git a/FE/src/components/SignUpModal/SignUpModal.js b/FE/src/components/SignUpModal/SignUpModal.js index 781996c..13264c7 100644 --- a/FE/src/components/SignUpModal/SignUpModal.js +++ b/FE/src/components/SignUpModal/SignUpModal.js @@ -11,12 +11,12 @@ import ModalBackground from "../ModalBackground/ModalBackground"; import SignUpRuleGuide from "./SignUpRuleGuide"; function SignUpModal() { + const setHeaderState = useSetRecoilState(headerAtom); const [email, setEmail] = useState(""); const [userId, setUserId] = useState(""); const [nickname, setNickname] = useState(""); const [password, setPassword] = useState(""); const [passwordCheck, setPasswordCheck] = useState(""); - const setHeaderState = useSetRecoilState(headerAtom); const errorRef = useRef(); const { mutate: signUp } = useMutation(() => { diff --git a/FE/src/constants/constants.js b/FE/src/constants/constants.js deleted file mode 100644 index e69de29..0000000 diff --git a/FE/src/index.js b/FE/src/index.js index 2916fd3..9cedc6a 100644 --- a/FE/src/index.js +++ b/FE/src/index.js @@ -6,9 +6,18 @@ import { RecoilRoot } from "recoil"; import App from "./App"; const root = ReactDOM.createRoot(document.getElementById("root")); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, +}); + root.render( - + diff --git a/FE/src/pages/LoadingPage.js b/FE/src/pages/LoadingPage.js index 7619d12..3cf74a5 100644 --- a/FE/src/pages/LoadingPage.js +++ b/FE/src/pages/LoadingPage.js @@ -45,7 +45,6 @@ function LoadingPage() { ); } -// TODO: 배경 이미지 제거하고 영상으로 대체할 것 const LoadingPageWrapper = styled.div` height: 100vh; background-image: url(${homeBackground}); diff --git a/FE/src/pages/MainPage.js b/FE/src/pages/MainPage.js index b2de85e..1ab0c6a 100644 --- a/FE/src/pages/MainPage.js +++ b/FE/src/pages/MainPage.js @@ -17,6 +17,7 @@ import DiaryLoadingModal from "../components/DiaryModal/DiaryLoadingModal"; import PurchaseModal from "../components/PurchaseModal/PurchaseModal"; import StarPage from "./StarPage"; import RedirectToHomepage from "./Redirection"; +import handleResponse from "../utils/handleResponse"; function MainPage() { const [diaryState, setDiaryState] = useRecoilState(diaryAtom); @@ -34,41 +35,27 @@ function MainPage() { "Content-Type": "application/json", Authorization: `Bearer ${userState.accessToken}`, }, - }).then((res) => { - if (res.status === 200) { - setLoaded(true); - return res.json(); - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - }); - } - return []; - }); + }).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 200, + onSuccessCallback: () => { + setLoaded(true); + return res.json(); + }, + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + }, + }), + ); }, { onSuccess: (data) => { @@ -85,41 +72,31 @@ function MainPage() { const { refetch: pointsRefetch } = useQuery( ["points", userState.accessToken], - () => + async () => fetch(`${process.env.REACT_APP_BACKEND_URL}/lines`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${userState.accessToken}`, }, - }).then((res) => { - if (res.status === 200) { - return res.json(); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - }); - } - return []; - }), + }).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 200, + onSuccessCallback: () => res.json(), + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + }, + }), + ), { onSuccess: (data) => { data.forEach((point) => { @@ -146,34 +123,24 @@ function MainPage() { "Content-Type": "application/json", Authorization: `Bearer ${userState.accessToken}`, }, - }).then((res) => { - if (res.status === 200) { - return res.json(); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - }); - } - return {}; - }), + }).then((res) => + handleResponse(res, userState.accessToken, { + successStatus: 200, + onSuccessCallback: () => res.json(), + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + }, + }), + ), { onSuccess: (data) => { if (data?.premium === "TRUE") { diff --git a/FE/src/pages/StarPage.js b/FE/src/pages/StarPage.js index b12fccc..5e8f5b1 100644 --- a/FE/src/pages/StarPage.js +++ b/FE/src/pages/StarPage.js @@ -21,6 +21,7 @@ import hand from "../assets/hand.svg"; import stella from "../assets/stella.svg"; import arrow from "../assets/arrow.svg"; import paint from "../assets/paint.svg"; +import handleResponse from "../utils/handleResponse"; function StarPage({ refetch, pointsRefetch }) { const [diaryState, setDiaryState] = useRecoilState(diaryAtom); @@ -197,14 +198,13 @@ function Scene() { } function StarView({ refetch, pointsRefetch, setHoverData }) { - const { scene, raycaster, camera } = useThree(); const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const [userState, setUserState] = useRecoilState(userAtom); const [starState, setStarState] = useRecoilState(starAtom); - const { mode, selected } = starState; const shapeState = useRecoilValue(shapeAtom); const [texture, setTexture] = useState({}); - let clickedPoint = [0, 0, 0]; + const { scene, raycaster, camera } = useThree(); + const { mode, selected } = starState; async function updateDiaryFn(data) { setDiaryState((prev) => ({ @@ -219,56 +219,38 @@ function StarView({ refetch, pointsRefetch, setHoverData }) { Authorization: `Bearer ${data.accessToken}`, }, body: JSON.stringify(data.diaryData), - }).then((res) => { - if (res.status === 204) { - refetch(); - pointsRefetch(); - setStarState((prev) => ({ - ...prev, - selected: null, - })); - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - - updateDiary({ - accessToken: data.accessToken, - diaryData, - }); + }).then((res) => + handleResponse(res, data.accessToken, { + successStatus: 204, + onSuccessCallback: () => { + refetch(); + pointsRefetch(); + setStarState((prev) => ({ + ...prev, + selected: null, + })); + }, + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + updateDiary({ + accessToken, + diaryData, }); - } - }); + }, + }), + ); } - const { - mutate: updateDiary, - // isLoading: diaryIsLoading, - // isError: diaryIsError, - } = useMutation(updateDiaryFn); + const { mutate: updateDiary } = useMutation(updateDiaryFn); useEffect(() => { scene.children.forEach((child) => { @@ -294,18 +276,8 @@ function StarView({ refetch, pointsRefetch, setHoverData }) { setTexture(newTexture); }, [shapeState]); - // const shapeData = shapeState.find((shape) - // => shape.uuid === shapeUuid)?.data; - // const blob = new Blob([shapeData], { type: "image/svg+xml" }); - // const urlObject = URL.createObjectURL(blob); - - // const loader = new THREE.TextureLoader(); - // const svgTexture = loader.load(urlObject); - const material = new THREE.ShaderMaterial({ uniforms: { - // color1: { value: new THREE.Color("#656990") }, - // color2: { value: new THREE.Color("#182683") }, color1: { value: new THREE.Color("#454980") }, color2: { value: new THREE.Color("#182663") }, gradientStart: { value: 0.001 }, @@ -456,17 +428,18 @@ function StarView({ refetch, pointsRefetch, setHoverData }) { ); } -function Star({ - uuid, - title, - date, - position, - sentiment, - texture, - moveToStar, - refetch, - setHoverData, -}) { +function Star(props) { + const { + uuid, + title, + date, + position, + sentiment, + texture, + moveToStar, + refetch, + setHoverData, + } = props; const setDiaryState = useSetRecoilState(diaryAtom); const [userState, setUserState] = useRecoilState(userAtom); const [starState, setStarState] = useRecoilState(starAtom); @@ -506,45 +479,29 @@ function Star({ uuid2: data.uuid2, }), }) - .then(async (res) => { - if (res.status === 201) { - return res.json(); - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - createLine({ - uuid1: selected.uuid, - uuid2: uuid, - accessToken: data.accessToken, - }); + .then((res) => + handleResponse(res, data.accessToken, { + successStatus: 201, + onSuccessCallback: () => res.json(), + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + createLine({ + uuid1: selected.uuid, + uuid2: uuid, + accessToken, }); - } - throw new Error("라인 생성 실패"); - }) + }, + }), + ) .then(() => { refetch(); }); @@ -559,44 +516,28 @@ function Star({ Authorization: `Bearer ${data.accessToken}`, }, }) - .then((res) => { - if (res.status === 204) { - return res; - } - if (res.status === 403) { - setDiaryState((prev) => ({ - ...prev, - isRedirect: true, - })); - } - if (res.status === 401) { - return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - }) - .then((res) => res.json()) - .then((data) => { - if (localStorage.getItem("accessToken")) { - localStorage.setItem("accessToken", data.accessToken); - } - if (sessionStorage.getItem("accessToken")) { - sessionStorage.setItem("accessToken", data.accessToken); - } - setUserState((prev) => ({ - ...prev, - accessToken: data.accessToken, - })); - deleteLine({ - id, - accessToken: data.accessToken, - }); + .then((res) => + handleResponse(res, data.accessToken, { + successStatus: 204, + onSuccessCallback: () => res, + on403Callback: () => { + setDiaryState((prev) => ({ + ...prev, + isRedirect: true, + })); + }, + on401Callback: (accessToken) => { + setUserState((prev) => ({ + ...prev, + accessToken, + })); + deleteLine({ + id, + accessToken, }); - } - throw new Error("라인 삭제 실패"); - }) + }, + }), + ) .then(() => { refetch(); }); @@ -700,7 +641,7 @@ function Star({ new THREE.Matrix4().lookAt( new THREE.Vector3(), new THREE.Vector3(...position), - new THREE.Vector3(0, 1, 0), // Up vector + new THREE.Vector3(0, 1, 0), ), )} position={position} @@ -709,7 +650,6 @@ function Star({ e.object.scale.set(1.5, 1.5, 1.5); e.object.material.emissiveIntensity = 1; - // e.object 좌표를 스크린 좌표로 변환 const vector = e.object.position.clone(); vector.project(e.camera); const x = ((vector.x + 1) / 2) * window.innerWidth; @@ -747,7 +687,7 @@ function Star({ new THREE.Matrix4().lookAt( new THREE.Vector3(), new THREE.Vector3(...position), - new THREE.Vector3(0, 1, 0), // Up vector + new THREE.Vector3(0, 1, 0), ), )} position={position.map((p) => p * 1.01)} diff --git a/FE/src/utils/handleResponse.js b/FE/src/utils/handleResponse.js new file mode 100644 index 0000000..d79045c --- /dev/null +++ b/FE/src/utils/handleResponse.js @@ -0,0 +1,52 @@ +function setStorage(accessToken) { + if (localStorage.getItem("accessToken")) + localStorage.setItem("accessToken", accessToken); + if (sessionStorage.getItem("accessToken")) + sessionStorage.setItem("accessToken", accessToken); +} + +function handleResponse( + res, + accessToken, + options = { + successStatus: 200, + onSuccessCallback: () => {}, + on400Callback: () => {}, + on403Callback: () => {}, + on401Callback: () => {}, + }, +) { + const { + successStatus, + onSuccessCallback, + on400Callback, + on403Callback, + on401Callback, + } = options; + if (res.status === successStatus) { + return onSuccessCallback(); + } + if (res.status === 400) { + return on400Callback(); + } + if (res.status === 403) { + return on403Callback(); + } + if (res.status === 401) { + return fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/reissue`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, + }, + }) + .then((res) => res.json()) + .then((data) => { + setStorage(data.accessToken); + on401Callback(data.accessToken); + }); + } + throw new Error("error"); +} + +export default handleResponse;