diff --git a/uniro_frontend/eslint.config.js b/uniro_frontend/eslint.config.js index 3b70bfc..9681bd7 100644 --- a/uniro_frontend/eslint.config.js +++ b/uniro_frontend/eslint.config.js @@ -4,11 +4,12 @@ import reactHooks from "eslint-plugin-react-hooks"; import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; import eslintPluginPrettier from "eslint-plugin-prettier"; +import pluginQuery from "@tanstack/eslint-plugin-query"; export default tseslint.config( { ignores: ["dist"] }, { - extends: [js.configs.recommended, ...tseslint.configs.recommended], + extends: [js.configs.recommended, ...tseslint.configs.recommended, pluginQuery.configs["flat/recommended"]], files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, diff --git a/uniro_frontend/package-lock.json b/uniro_frontend/package-lock.json index eef8d56..4e36f8e 100644 --- a/uniro_frontend/package-lock.json +++ b/uniro_frontend/package-lock.json @@ -11,6 +11,7 @@ "@googlemaps/js-api-loader": "^1.16.8", "@react-spring/web": "^9.7.5", "@tailwindcss/vite": "^4.0.0", + "@tanstack/react-query": "^5.66.0", "framer-motion": "^12.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -21,6 +22,7 @@ }, "devDependencies": { "@eslint/js": "^9.17.0", + "@tanstack/eslint-plugin-query": "^5.66.0", "@types/google.maps": "^3.58.1", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", @@ -1874,6 +1876,49 @@ "vite": "^5.2.0 || ^6" } }, + "node_modules/@tanstack/eslint-plugin-query": { + "version": "5.66.0", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.66.0.tgz", + "integrity": "sha512-CzZhBxicLDuuSJbkZ4nPcuBqWnhLu72Zt9p/7qLQ93BepVnZJV6ZDlBLBuN5eg7YRACwECPLsntnwo1zuhgseQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.18.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.66.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.66.0.tgz", + "integrity": "sha512-J+JeBtthiKxrpzUu7rfIPDzhscXF2p5zE/hVdrqkACBP8Yu0M96mwJ5m/8cPPYQE9aRNvXztXHlNwIh4FEeMZw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.66.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.66.0.tgz", + "integrity": "sha512-z3sYixFQJe8hndFnXgWu7C79ctL+pI0KAelYyW+khaNJ1m22lWrhJU2QrsTcRKMuVPtoZvfBYrTStIdKo+x0Xw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.66.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", diff --git a/uniro_frontend/package.json b/uniro_frontend/package.json index cedd9d5..31021a3 100644 --- a/uniro_frontend/package.json +++ b/uniro_frontend/package.json @@ -13,6 +13,7 @@ "@googlemaps/js-api-loader": "^1.16.8", "@react-spring/web": "^9.7.5", "@tailwindcss/vite": "^4.0.0", + "@tanstack/react-query": "^5.66.0", "framer-motion": "^12.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -23,6 +24,7 @@ }, "devDependencies": { "@eslint/js": "^9.17.0", + "@tanstack/eslint-plugin-query": "^5.66.0", "@types/google.maps": "^3.58.1", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", diff --git a/uniro_frontend/src/App.tsx b/uniro_frontend/src/App.tsx index c0face0..b3066ba 100644 --- a/uniro_frontend/src/App.tsx +++ b/uniro_frontend/src/App.tsx @@ -9,20 +9,25 @@ import NavigationResultPage from "./pages/navigationResult"; import ReportRoutePage from "./pages/reportRoute"; import ReportForm from "./pages/reportForm"; import ReportHazardPage from "./pages/reportHazard"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const queryClient = new QueryClient(); function App() { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); } diff --git a/uniro_frontend/src/pages/demo.tsx b/uniro_frontend/src/pages/demo.tsx index 96dc804..2ee40b0 100644 --- a/uniro_frontend/src/pages/demo.tsx +++ b/uniro_frontend/src/pages/demo.tsx @@ -6,19 +6,53 @@ import Map from "../component/Map"; import RouteInput from "../components/map/routeSearchInput"; import OriginIcon from "../assets/map/origin.svg?react"; import DestinationIcon from "../assets/map/destination.svg?react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import ReportButton from "../components/map/reportButton"; import { CautionToggleButton, DangerToggleButton } from "../components/map/floatingButtons"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { getFetch, postFetch, putFetch } from "../utils/fetch/fetch"; + +const getTest = () => { + /** https://jsonplaceholder.typicode.com/comments?postId=1 */ + return getFetch<{ postId: string }>("/comments", { + postId: 1, + }); +}; + +const postTest = (): Promise<{ id: string }> => { + return postFetch<{ id: string }, string>("/posts", { id: "test" }); +}; + +const putTest = (): Promise<{ id: string }> => { + return putFetch<{ id: string }, string>("/posts/1", { id: "test" }); +}; export default function Demo() { const [FailModal, isFailOpen, openFail, closeFail] = useModal(); const [SuccessModal, isSuccessOpen, openSuccess, closeSuccess] = useModal(); const [destination, setDestination] = useState("역사관"); + const { data, status } = useQuery({ + queryKey: ["test"], + queryFn: getTest, + }); + + const { data: postData, mutate: mutatePost } = useMutation<{ id: string }>({ + mutationFn: postTest, + }); + + const { data: putData, mutate: mutatePut } = useMutation<{ id: string }>({ + mutationFn: putTest, + }); + + useEffect(() => { + console.log(data); + }, [status]); + return ( <>
-
+
-
+
- {}} isActive={false} /> - {}} isActive={true} /> + { }} isActive={false} /> + { }} isActive={true} />
- {}} isActive={false} /> - {}} isActive={true} /> + { }} isActive={false} /> + { }} isActive={true} />
-
+
{}} + handleVoiceInput={() => { }} onLengthChange={(e: string) => { console.log(e); }} /> - {}} placeholder="출발지를 입력하세요"> + { }} placeholder="출발지를 입력하세요"> {}} + onClick={() => { }} placeholder="도착지를 입력하세요" value={destination} onCancel={() => setDestination("")} @@ -62,6 +96,14 @@ export default function Demo() {
+
+ + +

불편한 길 제보가 완료되었습니다!

diff --git a/uniro_frontend/src/utils/fetch/fetch.ts b/uniro_frontend/src/utils/fetch/fetch.ts new file mode 100644 index 0000000..20e44f4 --- /dev/null +++ b/uniro_frontend/src/utils/fetch/fetch.ts @@ -0,0 +1,54 @@ +export default function Fetch() { + const baseURL = import.meta.env.VITE_REACT_SERVER_BASE_URL; + + const get = async (url: string, params?: Record): Promise => { + const paramsURL = new URLSearchParams( + Object.entries(params || {}).map(([key, value]) => [key, String(value)]), + ).toString(); + + const response = await fetch(`${baseURL}${url}?${paramsURL}`, { + method: "GET", + }); + + if (!response.ok) { + throw new Error(`${response.status}-${response.statusText}`); + } + + return response.json(); + }; + + const post = async (url: string, body?: Record): Promise => { + const response = await fetch(`${baseURL}${url}`, { + method: "POST", + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error(`${response.status}-${response.statusText}`); + } + + return response.json(); + }; + + const put = async (url: string, body?: Record): Promise => { + const response = await fetch(`${baseURL}${url}`, { + method: "PUT", + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error(`${response.status}-${response.statusText}`); + } + + return response.json(); + }; + + return { + get, + post, + put, + }; +} + +const { get, post, put } = Fetch(); +export { get as getFetch, post as postFetch, put as putFetch };