diff --git a/frontend/src/api/http.ts b/frontend/src/api/http.ts new file mode 100644 index 000000000..da667bdaf --- /dev/null +++ b/frontend/src/api/http.ts @@ -0,0 +1,35 @@ +import axios, { AxiosInstance, AxiosRequestConfig } 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}` } : {}, +}); + +export interface HttpClient extends AxiosInstance { + get(url: string, config?: AxiosRequestConfig): Promise; + post( + url: string, + data?: any, + config?: AxiosRequestConfig, + ): Promise; + patch( + url: string, + data?: any, + config?: AxiosRequestConfig, + ): Promise; + put( + url: string, + data?: any, + config?: AxiosRequestConfig, + ): Promise; + delete(url: string, config?: AxiosRequestConfig): Promise; +} + +export const http: HttpClient = axiosInstance; + +http.interceptors.response.use((res) => res.data); diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 000000000..0f7951914 --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,4 @@ +import { TopicCardProps } from '../types/Topic'; +import { http } from './http'; + +export const getTopics = (url: string) => http.get(url); diff --git a/frontend/src/components/TopicCard/index.tsx b/frontend/src/components/TopicCard/index.tsx index 72aca615a..9d50213f4 100644 --- a/frontend/src/components/TopicCard/index.tsx +++ b/frontend/src/components/TopicCard/index.tsx @@ -1,4 +1,5 @@ -import { SyntheticEvent, useContext, useState } from 'react'; +import { QueryObserverResult, RefetchOptions } from '@tanstack/react-query'; +import { useContext, useState } from 'react'; import { styled } from 'styled-components'; import SeeTogetherSVG from '../../assets/seeTogetherBtn_filled.svg'; @@ -26,7 +27,9 @@ interface OnClickDesignatedProps { interface TopicCardExtendedProps extends TopicCardProps { cardType: 'default' | 'modal'; onClickDesignated?: ({ topicId, topicName }: OnClickDesignatedProps) => void; - getTopicsFromServer?: () => void; + getTopicsFromServer?: ( + options?: RefetchOptions | undefined, + ) => Promise>; } function TopicCard({ @@ -39,7 +42,6 @@ function TopicCard({ pinCount, bookmarkCount, isInAtlas, - isBookmarked, onClickDesignated, getTopicsFromServer, }: TopicCardExtendedProps) { diff --git a/frontend/src/components/TopicCardContainer/index.tsx b/frontend/src/components/TopicCardContainer/index.tsx index 3937fe702..48aa58f56 100644 --- a/frontend/src/components/TopicCardContainer/index.tsx +++ b/frontend/src/components/TopicCardContainer/index.tsx @@ -1,10 +1,8 @@ import { Swiper, Tab } from 'map-befine-swiper'; -import { useEffect, useState } from 'react'; import { styled } from 'styled-components'; -import useGet from '../../apiHooks/useGet'; +import useTopicsQuery from '../../hooks/api/useTopicsQuery'; import useKeyDown from '../../hooks/useKeyDown'; -import { TopicCardProps } from '../../types/Topic'; import Box from '../common/Box'; import Flex from '../common/Flex'; import Space from '../common/Space'; @@ -25,23 +23,8 @@ function TopicCardContainer({ containerDescription, routeWhenSeeAll, }: TopicCardContainerProps) { - const [topics, setTopics] = useState(null); + const { topics, refetchTopics } = useTopicsQuery(url); const { elementRef, onElementKeyDown } = useKeyDown(); - const { fetchGet } = useGet(); - - const setTopicsFromServer = async () => { - await fetchGet( - url, - '지도를 가져오는데 실패했습니다. 잠시 후 다시 시도해주세요.', - (response) => { - setTopics(response); - }, - ); - }; - - useEffect(() => { - setTopicsFromServer(); - }, []); return (
@@ -112,7 +95,7 @@ function TopicCardContainer({ bookmarkCount={topic.bookmarkCount} isInAtlas={topic.isInAtlas} isBookmarked={topic.isBookmarked} - getTopicsFromServer={setTopicsFromServer} + getTopicsFromServer={refetchTopics} /> diff --git a/frontend/src/hooks/api/useTopicsQuery.ts b/frontend/src/hooks/api/useTopicsQuery.ts new file mode 100644 index 000000000..bd871a286 --- /dev/null +++ b/frontend/src/hooks/api/useTopicsQuery.ts @@ -0,0 +1,13 @@ +import { useQuery } from '@tanstack/react-query'; + +import { getTopics } from '../../api'; + +const useTopicsQuery = (url: string) => { + const { data: topics, refetch: refetchTopics } = useQuery({ + queryKey: ['topics', url], + queryFn: () => getTopics(url), + }); + return { topics, refetchTopics }; +}; + +export default useTopicsQuery; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index d2999a20f..02f0ccae2 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -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'; @@ -21,11 +22,15 @@ if (process.env.REACT_APP_GOOGLE_ANALYTICS) { ReactGA.initialize(process.env.REACT_APP_GOOGLE_ANALYTICS); } +const queryClient = new QueryClient(); + root.render( - - - - - - , + + + + + + + + , );