Skip to content

Commit

Permalink
[FE] Refactor/#642: 북마크 및 전체보기 페이지 API 로직 수정 및 React-Query 적용 (#644)
Browse files Browse the repository at this point in the history
* feat: Axios http 정의 및 get 메서드 구현

* refactor: Bookmarks 페이지 TopicCardList 분리 코드 합병

TopicCardList는 재사용을 위해 만들어진 코드이나 Axios, React Query 를 적용하면서 사용하기 애매해졌다.
이에 따라 TopicCardList를 제거하고 각 페이지에서 그 책임을 이어받는다.

* design: Skeleton 컴포넌트 스타일 변경 및 적용

* refactor: SkeletonBox 공통 컴포넌트 구현 및 convertCSS 유틸 함수 구현

* refactor: Skeleton 컴포넌트 Bookmark 페이지에 적용

* refactor: http api 수정 및 useGetBookmark isLoading 상태 사용

* refactor: SeeAllNearTopics 페이지 TopicCardList 분리 및 코드 합병

* refactor: API 로직 반환값 타입 지정

* refactor: Bookmark 스켈레톤 수정

* refactor: SeeAllLatestTopics 페이지 TopicCardList 분리 및 코드 합병

* refactor: SeeAllPopularTopics 페이지 TopicCardList 분리 및 코드 합병

* refactor: AllTopics Query key 수정 및 시맨틱 태그 수정

* rename: API 명세와 페이지 이름 통일화

* refactor: 전체보기 페이지 명칭 수정 router 적용

* feat: 리프레쉬 토큰 요청 기능 추가

운영서버에 머지해야 확인가능할 것 같습니다.
기존 로직을 사용하는 운영서버와 request 값은 동일한데 cookie와 도메인 설정 문제로 실패하는 것으로 확인됩니다.

* feat: query default option 설정

* 마운트 시 리페칭 해제
* 윈도우 포커스 시 리페칭 해제
* 일정 주기로 리페칭 해제
* 받아온 데이터 stale 시 리페칭 해제

* refactor: useSuspenseQuery 를 통한 선언적으로 로딩상태 처리

* fix: token 없을 때 Authorization 빈 객체로 세팅하여 비로그인 오류 해결

* refactor: withCredentials 옵션 잠시 보류

* refactor: 01.17 회의를 통한 변경

* TopicCardList 컴포넌트를 이전처럼 활용하도록 한다. 전체보기 및 즐겨찾기는 거의 동일한 형태이며 중복코드가 다량 발생하여 위와 같이 수정한다.
* url을 넘겨받음에 따라서 리액트 쿼리 훅, API 요청 로직 또한 하나의 훅으로 재사용한다.

---------

Co-authored-by: afds4567 <33995840+afds4567@users.noreply.github.com>
semnil5202 and jiwonh423 authored Jan 21, 2024
1 parent 1d5a01c commit f7d8897
Showing 16 changed files with 293 additions and 176 deletions.
14 changes: 14 additions & 0 deletions frontend/src/apiHooks/new/useGetTopics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useSuspenseQuery } from '@tanstack/react-query';

import { getTopics } from '../../apis/new';

const useGetTopics = (url: string) => {
const { data: topics, refetch } = useSuspenseQuery({
queryKey: ['topics', url],
queryFn: () => getTopics(url),
});

return { topics, refetch };
};

export default useGetTopics;
101 changes: 101 additions & 0 deletions frontend/src/apis/new/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import axios, {
AxiosInstance,
AxiosRequestConfig,
AxiosRequestHeaders,
} from 'axios';

const API_POSTFIX = 'api';
const BASE_URL = process.env.APP_URL || `https://mapbefine.com/${API_POSTFIX}`;
const token = localStorage.getItem('userToken');

const axiosInstance = axios.create({
baseURL: BASE_URL,
headers: token ? { Authorization: `Bearer ${token}` } : {},
// withCredentials: true,
});

let refreshResponse: Promise<Response> | null = null;

export interface HttpClient extends AxiosInstance {
get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T>;
post<T = unknown>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<T>;
patch<T = unknown>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<T>;
put<T = unknown>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<T>;
delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T>;
}

export const http: HttpClient = axiosInstance;

http.interceptors.response.use((res) => res.data);
http.interceptors.request.use(
async (config) => {
const userToken = localStorage.getItem('userToken');

if (userToken && isTokenExpired(userToken)) {
await updateToken(config.headers);
}
return config;
},
(error) => Promise.reject(error),
);

const isTokenExpired = (token: string) => {
const decodedPayloadObject = decodeToken(token);
return decodedPayloadObject.exp * 1000 < Date.now();
};

const decodeToken = (token: string) => {
const tokenParts = token.split('.');

if (tokenParts.length !== 3) {
throw new Error('토큰이 잘못되었습니다.');
}

const decodedPayloadString = atob(tokenParts[1]);

return JSON.parse(decodedPayloadString);
};

async function updateToken(headers: AxiosRequestHeaders) {
const response = await refreshToken(headers);
const responseCloned = response.clone();
const newToken = await responseCloned.json();

localStorage.setItem('userToken', newToken.accessToken);
}

async function refreshToken(headers: AxiosRequestHeaders): Promise<Response> {
if (refreshResponse !== null) {
return refreshResponse;
}

const accessToken = localStorage.getItem('userToken');
refreshResponse = fetch(`${BASE_URL}/refresh-token`, {
method: 'POST',
headers,
body: JSON.stringify({
accessToken,
}),
});

const responseData = await refreshResponse;
refreshResponse = null;

if (!responseData.ok) {
throw new Error('Failed to refresh access token.');
}

return responseData;
}
4 changes: 4 additions & 0 deletions frontend/src/apis/new/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { TopicCardProps } from '../../types/Topic';
import { http } from './http';

export const getTopics = (url: string) => http.get<TopicCardProps[]>(url);
54 changes: 11 additions & 43 deletions frontend/src/components/Skeletons/TopicCardSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,21 @@
import { keyframes, styled } from 'styled-components';

import Flex from '../common/Flex';
import Box from '../common/Box';
import Space from '../common/Space';
import SkeletonBox from './common/SkeletonBox';

function TopicCardSkeleton() {
return (
<Flex $flexDirection="row">
<SkeletonImg />
<Space size={2} />
<Flex $flexDirection="column">
<SkeletonTitle />
<Space size={5} />
<SkeletonDescription />
</Flex>
</Flex>
<Box>
<SkeletonBox width="100%" $maxWidth={212} ratio="1.6 / 1" />
<Space size={1} />
<SkeletonBox width={212} height={25} />
<Space size={5} />
<SkeletonBox width={100} height={25} />
<Space size={1} />
<SkeletonBox width={212} height={46} />
</Box>
);
}

const skeletonAnimation = keyframes`
from {
opacity: 0.1;
}
to {
opacity: 1;
}
`;

const SkeletonImg = styled.div`
width: 138px;
height: 138px;
border-radius: 8px;
background: ${({ theme }) => theme.color.lightGray};
animation: ${skeletonAnimation} 1s infinite;
`;

const SkeletonTitle = styled.div`
width: 172px;
height: 32px;
border-radius: 8px;
background: ${({ theme }) => theme.color.lightGray};
animation: ${skeletonAnimation} 1s infinite;
`;

const SkeletonDescription = styled(SkeletonTitle)`
height: 80px;
`;

export default TopicCardSkeleton;
40 changes: 27 additions & 13 deletions frontend/src/components/Skeletons/TopicListSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import { styled } from 'styled-components';

import Space from '../common/Space';
import SkeletonBox from './common/SkeletonBox';
import TopicCardSkeleton from './TopicCardSkeleton';

function TopicCardContainerSkeleton() {
function TopicListSkeleton() {
return (
<Wrapper>
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
</Wrapper>
<>
<Space size={5} />
<SkeletonBox width={160} height={32} />
<Space size={4} />
<Space size={5} />
<TopicCardWrapper>
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
</TopicCardWrapper>
<Space size={4} />
<TopicCardWrapper>
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
<TopicCardSkeleton />
</TopicCardWrapper>
</>
);
}

const Wrapper = styled.section`
const TopicCardWrapper = styled.section`
display: flex;
flex-wrap: wrap;
gap: 20px;
width: 1036px;
height: 300px;
width: 1140px;
`;

export default TopicCardContainerSkeleton;
export default TopicListSkeleton;
38 changes: 38 additions & 0 deletions frontend/src/components/Skeletons/common/SkeletonBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styled, { keyframes } from 'styled-components';

import { convertCSS } from '../../../utils/convertCSS';

interface Props {
width?: number | string;
height?: number | string;
$maxWidth?: number | string;
$maxHeight?: number | string;
ratio?: string;
radius?: number | string;
}

const skeletonAnimation = keyframes`
from {
opacity: 1;
}
50% {
opacity: 0.6;
}
to {
opacity: 1;
}
`;

const SkeletonBox = styled.div<Props>`
width: ${({ width }) => width && convertCSS(width)};
height: ${({ height }) => height && convertCSS(height)};
max-width: ${({ $maxWidth }) => $maxWidth && convertCSS($maxWidth)};
max-height: ${({ $maxHeight }) => $maxHeight && convertCSS($maxHeight)};
aspect-ratio: ${({ ratio }) => ratio};
border-radius: ${({ radius, theme }) =>
(radius && convertCSS(radius)) || theme.radius.small};
background: ${({ theme }) => theme.color.lightGray};
animation: ${skeletonAnimation} 1s infinite;
`;

export default SkeletonBox;
47 changes: 14 additions & 33 deletions frontend/src/components/TopicCardList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';

import { TopicCardProps } from '../../types/Topic';
import { ReactNode } from 'react';
import useGetTopics from '../../apiHooks/new/useGetTopics';
import Button from '../common/Button';
import Flex from '../common/Flex';
import Grid from '../common/Grid';
@@ -12,30 +10,26 @@ import useProfileList from '../../hooks/queries/useProfileList';

interface TopicCardListProps {
url: string;
errorMessage: string;
commentWhenEmpty: string;
pageCommentWhenEmpty: string;
routePageName: string;
routePage: () => void;
children?: React.ReactNode;
svgElementWhenEmpty?: ReactNode;
}

function TopicCardList({
url,
errorMessage,
commentWhenEmpty,
pageCommentWhenEmpty,
routePageName,
routePage,
children,
svgElementWhenEmpty,
}: TopicCardListProps) {
const { data: topics, refetch: refetchTopic } = useProfileList();

if (!topics) return null;
const { topics, refetch } = useGetTopics(url);

if (topics.length === 0) {
return (
<EmptyWrapper>
<Flex height="240px" $flexDirection="column" $alignItems="center">
<Flex $alignItems="center">
{children}
{svgElementWhenEmpty}
<Space size={1} />
<Text color="black" $fontSize="default" $fontWeight="normal">
{commentWhenEmpty}
@@ -44,14 +38,14 @@ function TopicCardList({
</Flex>
<Space size={5} />
<Button variant="primary" onClick={routePage}>
{pageCommentWhenEmpty}
{routePageName}
</Button>
</EmptyWrapper>
</Flex>
);
}

return (
<Wrapper>
<Flex $flexWrap="wrap" $gap="20px">
<Grid
rows="auto"
columns={5}
@@ -77,26 +71,13 @@ function TopicCardList({
bookmarkCount={topic.bookmarkCount}
isInAtlas={topic.isInAtlas}
isBookmarked={topic.isBookmarked}
getTopicsFromServer={refetchTopic}
getTopicsFromServer={refetch}
/>
</ul>
))}
</Grid>
</Wrapper>
</Flex>
);
}

const EmptyWrapper = styled.section`
height: 240px;
display: flex;
flex-direction: column;
align-items: center;
`;

const Wrapper = styled.section`
display: flex;
flex-wrap: wrap;
gap: 20px;
`;

export default TopicCardList;
12 changes: 12 additions & 0 deletions frontend/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ReactDOM from 'react-dom/client';
import ReactGA from 'react-ga4';
import { ThemeProvider } from 'styled-components';
@@ -9,6 +10,17 @@ import GlobalStyle from './GlobalStyle';
import NotFound from './pages/NotFound';
import theme from './themes';

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchInterval: false,
refetchOnReconnect: false,
},
},
});

const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Failed to find the root element');
const root = ReactDOM.createRoot(rootElement);
24 changes: 9 additions & 15 deletions frontend/src/pages/Bookmark.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { styled } from 'styled-components';

import FavoriteNotFilledSVG from '../assets/favoriteBtn_notFilled.svg';
@@ -7,22 +7,18 @@ import Flex from '../components/common/Flex';
import Space from '../components/common/Space';
import MediaSpace from '../components/common/Space/MediaSpace';
import MediaText from '../components/common/Text/MediaText';
import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicListSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicCardList from '../components/TopicCardList';
import { ARIA_FOCUS, FULLSCREEN } from '../constants';
import useNavigator from '../hooks/useNavigator';
import useSetLayoutWidth from '../hooks/useSetLayoutWidth';
import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight';

const TopicCardList = lazy(() => import('../components/TopicCardList'));

function Bookmark() {
const { routePage } = useNavigator();
useSetLayoutWidth(FULLSCREEN);
useSetNavbarHighlight('favorite');

const goToHome = () => {
routePage('/');
};
const { routingHandlers } = useNavigator();

return (
<Wrapper>
@@ -48,16 +44,14 @@ function Bookmark() {

<MediaSpace size={6} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicCardList
url="/members/my/bookmarks"
errorMessage="로그인 후 이용해주세요."
commentWhenEmpty="버튼으로 지도를 즐겨찾기에 담아보세요."
pageCommentWhenEmpty="메인페이지로 가기"
routePage={goToHome}
>
<FavoriteNotFilledSVG />
</TopicCardList>
routePageName="메인 페이지로 가기"
routePage={routingHandlers.home}
svgElementWhenEmpty={<FavoriteNotFilledSVG />}
/>
</Suspense>

<Space size={8} />
8 changes: 4 additions & 4 deletions frontend/src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import Banner from '../components/Banner';
import Space from '../components/common/Space';
import MediaSpace from '../components/common/Space/MediaSpace';
import SearchBar from '../components/SearchBar/SearchBar';
import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicListSkeleton from '../components/Skeletons/TopicListSkeleton';
import { FULLSCREEN } from '../constants';
import { MarkerContext } from '../context/MarkerContext';
import { SeeTogetherContext } from '../context/SeeTogetherContext';
@@ -54,7 +54,7 @@ function Home() {
<Banner />
<Space size={6} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicListContainer
url="/topics/bests"
containerTitle="인기 급상승할 지도?"
@@ -65,7 +65,7 @@ function Home() {

<MediaSpace size={9} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicListContainer
url="/topics/newest"
containerTitle="새로울 지도?"
@@ -76,7 +76,7 @@ function Home() {

<MediaSpace size={9} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicListContainer
url="/topics"
containerTitle="모두일 지도?"
4 changes: 2 additions & 2 deletions frontend/src/pages/Profile.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import Space from '../components/common/Space';
import MediaSpace from '../components/common/Space/MediaSpace';
import MediaText from '../components/common/Text/MediaText';
import MyInfo from '../components/MyInfo';
import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicListSkeleton from '../components/Skeletons/TopicListSkeleton';
import { ARIA_FOCUS, FULLSCREEN } from '../constants';
import useNavigator from '../hooks/useNavigator';
import useSetLayoutWidth from '../hooks/useSetLayoutWidth';
@@ -53,7 +53,7 @@ function Profile() {

<MediaSpace size={6} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicCardList
url="/members/my/topics"
errorMessage="로그인 후 이용해주세요."
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { styled } from 'styled-components';

import Box from '../components/common/Box';
import Space from '../components/common/Space';
import MediaSpace from '../components/common/Space/MediaSpace';
import MediaText from '../components/common/Text/MediaText';
import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicListSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicCardList from '../components/TopicCardList';
import { ARIA_FOCUS, FULLSCREEN } from '../constants';
import useNavigator from '../hooks/useNavigator';
import useSetLayoutWidth from '../hooks/useSetLayoutWidth';
import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight';

const TopicCardList = lazy(() => import('../components/TopicCardList'));

function SeeAllNearTopics() {
const { routePage } = useNavigator();
function SeeAllAllTopics() {
useSetLayoutWidth(FULLSCREEN);
useSetNavbarHighlight('home');

const goToHome = () => {
routePage('/new-topic');
};
const { routingHandlers } = useNavigator();

return (
<Wrapper as="section">
<Wrapper>
<Space size={5} />
<MediaText
as="h2"
@@ -38,13 +34,12 @@ function SeeAllNearTopics() {

<MediaSpace size={6} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicCardList
url="/topics"
errorMessage="로그인 후 이용해주세요."
commentWhenEmpty="지도가 없습니다. 추가하기 버튼을 눌러 지도를 추가해보세요."
pageCommentWhenEmpty="지도 만들러 가기"
routePage={goToHome}
commentWhenEmpty="생성된 지도가 없습니다. 지도를 만들어보세요."
routePageName="지도 만들러 가기"
routePage={routingHandlers.newTopic}
/>
</Suspense>

@@ -63,4 +58,4 @@ const Wrapper = styled(Box)`
}
`;

export default SeeAllNearTopics;
export default SeeAllAllTopics;
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { styled } from 'styled-components';

import Box from '../components/common/Box';
import Space from '../components/common/Space';
import MediaSpace from '../components/common/Space/MediaSpace';
import MediaText from '../components/common/Text/MediaText';
import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicListSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicCardList from '../components/TopicCardList';
import { ARIA_FOCUS, FULLSCREEN } from '../constants';
import useNavigator from '../hooks/useNavigator';
import useSetLayoutWidth from '../hooks/useSetLayoutWidth';
import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight';

const TopicCardList = lazy(() => import('../components/TopicCardList'));

function SeeAllTopics() {
const { routePage } = useNavigator();
function SeeAllAllTopics() {
useSetLayoutWidth(FULLSCREEN);
useSetNavbarHighlight('home');

const goToHome = () => {
routePage('/');
};
const { routingHandlers } = useNavigator();

return (
<Wrapper as="section">
<Wrapper>
<Space size={5} />
<MediaText
as="h2"
color="black"
$fontSize="extraLarge"
$fontWeight="bold"
tabIndex={ARIA_FOCUS}
aria-label="인기 급상승할 지도 전체보기 페이지 입니다."
aria-label="모두일 지도 전체보기 페이지 입니다."
>
인기 급상승할 지도?
새로울 지도?
</MediaText>

<MediaSpace size={6} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicCardList
url="/topics/bests"
errorMessage="로그인 후 이용해주세요."
commentWhenEmpty="즐겨찾기가 많이 된 지도가 없습니다."
pageCommentWhenEmpty="메인페이지로 가기"
routePage={goToHome}
commentWhenEmpty="생성된 지도가 없습니다. 지도를 만들어보세요."
routePageName="지도 만들러 가기"
routePage={routingHandlers.newTopic}
/>
</Suspense>

@@ -63,4 +58,4 @@ const Wrapper = styled(Box)`
}
`;

export default SeeAllTopics;
export default SeeAllAllTopics;
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { styled } from 'styled-components';

import Box from '../components/common/Box';
import Space from '../components/common/Space';
import MediaSpace from '../components/common/Space/MediaSpace';
import MediaText from '../components/common/Text/MediaText';
import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicListSkeleton from '../components/Skeletons/TopicListSkeleton';
import TopicCardList from '../components/TopicCardList';
import { ARIA_FOCUS, FULLSCREEN } from '../constants';
import useNavigator from '../hooks/useNavigator';
import useSetLayoutWidth from '../hooks/useSetLayoutWidth';
import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight';

const TopicCardList = lazy(() => import('../components/TopicCardList'));

function SeeAllLatestTopics() {
const { routePage } = useNavigator();
function SeeAllAllTopics() {
useSetLayoutWidth(FULLSCREEN);
useSetNavbarHighlight('home');

const goToHome = () => {
routePage('/');
};
const { routingHandlers } = useNavigator();

return (
<Wrapper as="section">
<Wrapper>
<Space size={5} />
<MediaText
as="h2"
color="black"
$fontSize="extraLarge"
$fontWeight="bold"
tabIndex={ARIA_FOCUS}
aria-label="새로울 지도 전체보기 페이지 입니다."
aria-label="모두일 지도 전체보기 페이지 입니다."
>
새로울 지도?
인기 급상승할 지도?
</MediaText>

<MediaSpace size={6} />

<Suspense fallback={<TopicCardContainerSkeleton />}>
<Suspense fallback={<TopicListSkeleton />}>
<TopicCardList
url="/topics/newest"
errorMessage="로그인 후 이용해주세요."
commentWhenEmpty="최근에 핀이 찍힌 지도가 없습니다."
pageCommentWhenEmpty="메인페이지로 가기"
routePage={goToHome}
commentWhenEmpty="생성된 지도가 없습니다. 지도를 만들어보세요."
routePageName="지도 만들러 가기"
routePage={routingHandlers.newTopic}
/>
</Suspense>

@@ -63,4 +58,4 @@ const Wrapper = styled(Box)`
}
`;

export default SeeAllLatestTopics;
export default SeeAllAllTopics;
29 changes: 15 additions & 14 deletions frontend/src/router.tsx
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { lazy, ReactNode, Suspense } from 'react';
import { createBrowserRouter } from 'react-router-dom';

import AuthLayout from './components/Layout/AuthLayout';
import TopicListSkeleton from './components/Skeletons/TopicListSkeleton';
import Home from './pages/Home';
import NotFound from './pages/NotFound';
import RootPage from './pages/RootPage';
@@ -11,9 +12,9 @@ import TopicListSkeleton from './components/Skeletons/TopicListSkeleton';
const SelectedTopic = lazy(() => import('./pages/SelectedTopic'));
const NewPin = lazy(() => import('./pages/NewPin'));
const NewTopic = lazy(() => import('./pages/NewTopic'));
const SeeAllPopularTopics = lazy(() => import('./pages/SeeAllPopularTopics'));
const SeeAllNearTopics = lazy(() => import('./pages/SeeAllNearTopics'));
const SeeAllLatestTopics = lazy(() => import('./pages/SeeAllLatestTopics'));
const SeeAllBestTopics = lazy(() => import('./pages/SeeAllBestTopics'));
const SeeAllAllTopics = lazy(() => import('./pages/SeeAllAllTopics'));
const SeeAllNewestTopics = lazy(() => import('./pages/SeeAllNewestTopics'));
const KakaoRedirect = lazy(() => import('./pages/KakaoRedirect'));
const Profile = lazy(() => import('./pages/Profile'));
const AskLogin = lazy(() => import('./pages/AskLogin'));
@@ -80,36 +81,36 @@ const routes: routeElement[] = [
{
path: 'see-all/popularity',
element: (
<SuspenseComp>
<SeeAllPopularTopics />
</SuspenseComp>
<Suspense fallback={<TopicListSkeleton />}>
<SeeAllBestTopics />
</Suspense>
),
withAuth: false,
},
{
path: 'see-all/near',
element: (
<SuspenseComp>
<SeeAllNearTopics />
</SuspenseComp>
<Suspense fallback={<TopicListSkeleton />}>
<SeeAllAllTopics />
</Suspense>
),
withAuth: false,
},
{
path: 'see-all/latest',
element: (
<SuspenseComp>
<SeeAllLatestTopics />
</SuspenseComp>
<Suspense fallback={<TopicListSkeleton />}>
<SeeAllNewestTopics />
</Suspense>
),
withAuth: false,
},
{
path: 'favorite',
element: (
<SuspenseComp>
<Suspense fallback={<TopicListSkeleton />}>
<Bookmark />
</SuspenseComp>
</Suspense>
),
withAuth: true,
},
5 changes: 5 additions & 0 deletions frontend/src/utils/convertCSS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const convertCSS = (property: number | string) => {
if (typeof property === 'number') return `${property}px`;

return property;
};

0 comments on commit f7d8897

Please sign in to comment.