Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions src/apis/user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios from 'axios';
import useLoginStore from '../stores/loginStore';
import qs from 'qs';
import useErrorStore from '@/stores/errorStore';

axios.defaults.paramsSerializer = (params) => qs.stringify(params, { arrayFormat: 'comma' });

Expand All @@ -9,6 +10,26 @@ export const publicAPI = axios.create({
baseURL: import.meta.env.VITE_API_URL,
});

publicAPI.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response) {
const errorType = { statusCode: error.response.status };
useErrorStore.getState().setError(errorType);
} else if (error.message === 'Network Error' || error.message === 'Network Error') {
console.log('λ„€νŠΈμ›Œν¬ μ—λŸ¬ λ°œμƒ');
const errorType = { statusCode: error.message };
useErrorStore.getState().setError(errorType);
} else {
const errorType = { statusCode: 'Unknown Error' };
useErrorStore.getState().setError(errorType);
}
return Promise.reject(error);
},
);

//인증이 ν•„μš”ν•œ μš”μ²­
export const privateAPI = axios.create({
baseURL: import.meta.env.VITE_API_URL,
Expand Down Expand Up @@ -89,21 +110,31 @@ privateAPI.interceptors.response.use(
originRequest.headers.Authorization = `Bearer ${newAccessToken}`;
console.log('μ›λž˜ μš”μ²­ : ', originRequest);
return axios(originRequest);
//λ¦¬ν”„λ ˆμ‹œ 토큰 μš”μ²­μ΄ μ‹€νŒ¨ν• λ•Œ(λ¦¬ν”„λ ˆμ‹œ 토큰도 λ§Œλ£Œλ˜μ—ˆμ„λ•Œ = 재둜그인 μ•ˆλ‚΄)
} else if (response.data.status === 401) {
//λ¦¬ν”„λ ˆμ‹œ 토큰 μš”μ²­μ΄ μ‹€νŒ¨ν• λ•Œ(λ¦¬ν”„λ ˆμ‹œ 토큰도 λ§Œλ£Œλ˜μ—ˆμ„λ•Œ = 재둜그인 μ•ˆλ‚΄)
loginStorage.clear();
alert('μ„Έμ…˜μ΄ λ§Œλ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. 재둜그인 ν•΄μ£Όμ„Έμš”.');
const errorType = { statusCode: 401 };
useErrorStore.getState().setError(errorType);
} else {
console.log('인터셉터 λ‚΄λΆ€ 기타 μ—λŸ¬ : ', error);
alert('μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. κ΄€λ¦¬μžμ—κ²Œ λ¬Έμ˜ν•΄μ£Όμ„Έμš”!');
const errorType = { statusCode: 'Unknown Error' };
useErrorStore.getState().setError(errorType);
}
} else {
// λ¦¬ν”„λ ˆμ‹œ 토큰이 μ—†λŠ” 경우 = 둜그인 μ•ˆλ‚΄
console.log(error);
alert('둜그인이 ν•„μš”ν•œ μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.');
return 'need login';
const errorType = { statusCode: 401 };
useErrorStore.getState().setError(errorType);
}
} else {
const errorType = { statusCode: 'Unknown Error' };
useErrorStore.getState().setError(errorType);
}
} else if (error.message === 'Network Error' || error.message === 'Network Error') {
const errorType = { statusCode: error.message };
useErrorStore.getState().setError(errorType);
} else {
const errorType = { statusCode: 'Unknown Error' };
useErrorStore.getState().setError(errorType);
}
return Promise.reject(error);
},
Expand Down
9 changes: 9 additions & 0 deletions src/components/AppLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import Header from './common/Header';
import { Outlet } from 'react-router-dom';
import Footer from './common/footer/Footer';
import LoginModal from './modal/LoginModal';
import APIErrorModal from './modal/APIErrorModal';
import ModalWrapper from './modal/ModalWrapper';
import { useErrorStore } from '../stores/errorStore';

export default function AppLayout() {
const { modalOpen } = useLoginModalStore();
const { hasError } = useErrorStore();

const [isMobile, setIsMobile] = useState(false);

Expand Down Expand Up @@ -48,6 +52,11 @@ export default function AppLayout() {
</main>
<Footer />
{modalOpen ? <LoginModal /> : null}
{hasError && (
<ModalWrapper>
<APIErrorModal />
</ModalWrapper>
)}
</>
)}
</ThemeProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function RecreationCardL({ content }) {
console.log(response.data);
}
} catch (error) {
throw new Error('FavBtn Error');
throw new Error('FavBtn Error', error);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function RecreationCardS({ content, refetch }) {
console.log(response.data);
}
} catch (error) {
throw new Error('FavBtn Error');
throw new Error('FavBtn Error', error);
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/createFlow/CustomRecreationItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default function CustomRecreationItem({ index, register, removeRecreation
</ErrorWrapper>
<KeywordBox
onClick={handleKeywordBoxClick}
data-tooltip-id={!!keywords.length ? KEYWORD_TOOLTIP_ID : ''}
data-tooltip-id={keywords.length ? KEYWORD_TOOLTIP_ID : ''}
>
{!keywords.length ? (
<span>이곳을 ν΄λ¦­ν•˜μ—¬ 3개의 ν‚€μ›Œλ“œλ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”.</span>
Expand Down
4 changes: 2 additions & 2 deletions src/components/createFlow/FavoriteRecreationsSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function FavoriteRecreationsSection({
};

fetchFavoriteRecreations();
}, [page]);
}, [page, totalPages]);

const incrementPage = () => setPage((prev) => prev + 1);

Expand All @@ -52,7 +52,7 @@ export default function FavoriteRecreationsSection({
<Wrapper>
{initialLoading ? (
<LoadingSpinner />
) : !!recreations.length ? (
) : recreations.length ? (
<RecreationCarousel
recreations={recreations}
onAddRecreationClick={onAddRecreationClick}
Expand Down
2 changes: 1 addition & 1 deletion src/components/main/PopularRecreationCarousel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function PopularRecreationCarousel() {
}
};
call();
}, []);
}, [isLoggedIn]);

return (
<div
Expand Down
102 changes: 102 additions & 0 deletions src/components/modal/APIErrorModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react';
import * as S from './modal.style';
import useErrorStore from '@/stores/errorStore';
import toKakaoLogin from '@/utils/kakaoLoginRedirectUtils';
import { useLocation } from 'react-router';

export default function APIErrorModal() {
const { status, resetError } = useErrorStore();
const { pathname } = useLocation();
const moveToHome = () => {
resetError();
window.location.href = '/';
};

if (status === 401) {
// μ„Έμ…˜ 만료
return (
<S.Modal>
<S.Content>
<S.TitleContainer>
<S.Title>μ„Έμ…˜μ΄ λ§Œλ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.</S.Title>
<S.SubTitle>재둜그인 ν•΄μ£Όμ„Έμš”.</S.SubTitle>
</S.TitleContainer>
<S.BtnContainer>
<S.ModalBtn
backgroundColor="main02"
color="main05"
onClick={() => toKakaoLogin(pathname)}
>
둜그인
</S.ModalBtn>
</S.BtnContainer>
</S.Content>
</S.Modal>
);
}
if (status >= 500) {
// μ„œλ²„ μ—λŸ¬
return (
<S.Modal>
<S.Content>
<S.TitleContainer>
<S.Title>μ„œλ²„ μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</S.Title>
<S.SubTitle>μž μ‹œ 후에 λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.</S.SubTitle>
</S.TitleContainer>
<S.BtnContainer>
<S.ModalBtn backgroundColor="main02" color="main05" onClick={moveToHome}>
ν™ˆμœΌλ‘œ μ΄λ™ν•˜κΈ°
</S.ModalBtn>
</S.BtnContainer>
</S.Content>
</S.Modal>
);
} else if (status === 'Network Error') {
// λ„€νŠΈμ›Œν¬ μ—λŸ¬
return (
<S.Modal>
<S.Content>
<S.TitleContainer>
<S.Title>λ„€νŠΈμ›Œν¬ μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</S.Title>
<S.SubTitle>인터넷 연결을 ν™•μΈν•΄μ£Όμ„Έμš”.</S.SubTitle>
</S.TitleContainer>
<S.BtnContainer>
<S.ModalBtn
onClick={() => window.location.reload()}
backgroundColor="main02"
color="main05"
>
μƒˆλ‘œκ³ μΉ¨
</S.ModalBtn>
</S.BtnContainer>
</S.Content>
</S.Modal>
);
} else {
// 기타 μ—λŸ¬
return (
<S.Modal>
<S.Content>
<S.TitleContainer>
<S.Title>μ•Œ 수 μ—†λŠ” μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</S.Title>
<S.SubTitle>κ΄€λ¦¬μžμ—κ²Œ λ¬Έμ˜ν•΄μ£Όμ„Έμš”.</S.SubTitle>
</S.TitleContainer>
<S.BtnContainer>
<S.ModalBtn backgroundColor="main02" color="main05" onClick={moveToHome}>
ν™ˆμœΌλ‘œ μ΄λ™ν•˜κΈ°
</S.ModalBtn>
<S.ModalBtn
onClick={() => window.location.reload()}
backgroundColor="main05"
color="grayscale03"
border
borderColor="grayscale03"
>
μƒˆλ‘œκ³ μΉ¨
</S.ModalBtn>
</S.BtnContainer>
</S.Content>
</S.Modal>
);
}
}
27 changes: 10 additions & 17 deletions src/components/modal/LoginModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,16 @@ import React from 'react';
import { Backdrop } from './modal.style';
import { useLocation } from 'react-router';
import styled from 'styled-components';
import characterImg from '../../assets/character/kakaoAvb.png';
import closeIcon from '../../assets/common/x.svg';
import characterImg from '@/assets/character/kakaoAvb.png';
import closeIcon from '@/assets/common/x.svg';

import useLoginModalStore from '../../stores/loginModalStore';
import useLoginModalStore from '@/stores/loginModalStore';
import Button from '../common/button/Button';
import toKakaoLogin from '@/utils/kakaoLoginRedirectUtils';

export default function LoginModal() {
const { pathname } = useLocation();
const { modalControl } = useLoginModalStore((state) => state);
const REST_API_KEY = import.meta.env.VITE_REST_API_KEY;
let REDIRECT_URI;

if (window.location.href.startsWith('http://localhost:3000/')) {
REDIRECT_URI = import.meta.env.VITE_REDIRECT_URL_LOCAL;
} else {
REDIRECT_URI = import.meta.env.VITE_REDIRECT_URL;
}

let kakaoURL = `https://kauth.kakao.com/oauth/authorize?&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code&state=${pathname}&prompt=select_account`;
const toKakaoLogin = () => {
window.location.href = kakaoURL;
};

return (
<Backdrop>
Expand All @@ -39,7 +27,12 @@ export default function LoginModal() {
κ°„νŽΈν•˜κ²Œ λ‘œκ·ΈμΈμ„ ν•˜κ³  <br />
<span style={{ fontWeight: '700' }}>성곡적인 λ ˆν¬λ ˆμ΄μ…˜</span>을 <br /> κ²½ν—˜ν•΄λ³΄μ„Έμš”!
</Comment>
<Button onClick={toKakaoLogin} backgroundColor="main01" color="main05" size="sm">
<Button
onClick={() => toKakaoLogin(pathname)}
backgroundColor="main01"
color="main05"
size="sm"
>
κ°„νŽΈ λ‘œκ·ΈμΈν•˜κΈ°
</Button>
</Text>
Expand Down
5 changes: 0 additions & 5 deletions src/components/myPage/MyInfoBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,6 @@ const WarnSpace = styled.div`
gap: 0.5rem;
`;

const WarnImg = styled.img`
width: 1rem;
margin-right: 0.5rem;
`;

const Warn = styled.div`
color: ${({ theme }) => theme.color.grayscale04};
${({ theme }) => theme.text.small};
Expand Down
1 change: 0 additions & 1 deletion src/components/recreation/RecreationInfoSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import PLACE from '../../constants/enum/place';
import GENDER from '../../constants/enum/gender';
import AGE from '../../constants/enum/age';
import PURPOSE from '../../constants/enum/purpose';
import theme from '@/styles/theme';

const RecreationInfoSection = forwardRef(({ recreationData }, ref) => {
const getPlaceText = () => recreationData.placeList.map((place) => PLACE[place].value).join(', ');
Expand Down
2 changes: 1 addition & 1 deletion src/components/recreation/RecreationMenuBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function RecreationMenuBar({ scrollRefs }) {
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [scrollRefs]);
}, [scrollRefs, offset]);

const handleButtonClick = (index) => {
const targetPosition = scrollRefs.current[index].current.offsetTop + offset;
Expand Down
2 changes: 1 addition & 1 deletion src/components/recreation/RecreationPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function RecreationPreview({ recreation, showViewCount = true })
console.log(response.data);
}
} catch (error) {
throw new Error('FavBtn Error');
throw new Error('FavBtn Error', error);
}
}
};
Expand Down
5 changes: 2 additions & 3 deletions src/components/recreation/RecreationReview.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from 'styled-components';
import React, { useState } from 'react';
import React from 'react';
import yellowStar from '../../assets/recreation/yellowStar.svg';
import greyStar from '../../assets/recreation/greyStar.svg';
import GoodIcon from '../../assets/recreation/good.svg?react';
Expand All @@ -11,7 +11,6 @@ import useLoginStore from '../../stores/loginStore';
import Button from '../common/button/Button';

export default function RecreationReview({ review, refetch }) {
const [recommendation, setRecommendation] = useState(review.recommendation);
const { modalControl } = useLoginModalStore();
const isLoggedIn = useLoginStore((state) => state.isLoggedIn);

Expand All @@ -33,7 +32,7 @@ export default function RecreationReview({ review, refetch }) {
modalControl();
}
} catch (error) {
throw new Error('POST Recreation Review Error');
throw new Error('POST Recreation Review Error', error);
}
};

Expand Down
Loading