diff --git a/package-lock.json b/package-lock.json index 6f9f933..85b8ca2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@mui/icons-material": "^5.15.6", "@mui/material": "^5.15.6", "@nextui-org/react": "^2.2.9", + "@react-stately/data": "^3.11.2", "@sentry/react": "^7.98.0", "@stomp/stompjs": "^7.0.0", "@tanstack/react-query": "^5.8.4", @@ -2893,6 +2894,18 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, + "node_modules/@react-stately/data": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/@react-stately/data/-/data-3.11.2.tgz", + "integrity": "sha512-yhK2upk2WbJeiLBRWHrh/4G2CvmmozCzoivLaRAPYu53m1J3MyzVGCLJgnZMbMZvAbNcYWZK6IzO6VqZ2y1fOw==", + "dependencies": { + "@react-types/shared": "^3.22.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@react-stately/flags": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.0.1.tgz", diff --git a/package.json b/package.json index 4d8adaa..f437705 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@mui/icons-material": "^5.15.6", "@mui/material": "^5.15.6", "@nextui-org/react": "^2.2.9", + "@react-stately/data": "^3.11.2", "@sentry/react": "^7.98.0", "@stomp/stompjs": "^7.0.0", "@tanstack/react-query": "^5.8.4", diff --git a/src/components/hotel/NearbyAmenitiesMap.js b/src/components/hotel/NearbyAmenitiesMap.js index 61565ec..b3c31cc 100644 --- a/src/components/hotel/NearbyAmenitiesMap.js +++ b/src/components/hotel/NearbyAmenitiesMap.js @@ -1,361 +1,392 @@ -import React, { useEffect, useRef, useState } from "react" +import React, {useEffect, useRef, useState} from "react" import { - Pagination, - Radio, - RadioGroup, - Table, - TableBody, - TableCell, - TableColumn, - TableHeader, - TableRow, + Pagination, + Radio, + RadioGroup, + Table, + TableBody, + TableCell, + TableColumn, + TableHeader, + TableRow, } from "@nextui-org/react" -import { CustomRadio } from "@/components/ui/CustomRadio" +import {CustomRadio} from "@/components/ui/CustomRadio" import axios from "axios" import calculateDistance from "@/util/calculateDistance" import TouristSpotSearch from "@/components/touristSpot/TouristSpotSearch" - -export default function NearbyAmenitiesMap({ hotel }) { - const mapRef = useRef(null) // 지도를 표시할 DOM 요소에 대한 참조 - const [map, setMap] = useState(null) // 지도 인스턴스 - const [address, setAddress] = useState(hotel?.address) - const [centerCoords, setCenterCoords] = useState() - const [nearAmenities, setNearAmenities] = useState([]) - const [markers, setMarkers] = useState([]) - const [category, setCategory] = useState("food") - const [distance, setDistance] = useState("100") - const [showTouristSpots, setShowTouristSpots] = useState(false) - const [touristSpots, setTouristSpots] = useState([]) - - const [page, setPage] = React.useState(1) - const rowsPerPage = 9 - const pages = Math.ceil(nearAmenities?.length / rowsPerPage) - - const items = React.useMemo(() => { - const start = (page - 1) * rowsPerPage - const end = start + rowsPerPage - - return nearAmenities?.slice(start, end) - }, [page, nearAmenities]) - - useEffect(() => { - // 네이버 지도 API 스크립트가 이미 로드되었는지 확인 - if (!map && window.naver && window.naver.maps) { - initMap() - } else if (!map) { - // 스크립트가 아직 로드되지 않았다면 로드를 기다림 - const checkIfNaverMapsIsLoaded = setInterval(() => { - if (window.naver && window.naver.maps) { - clearInterval(checkIfNaverMapsIsLoaded) - initMap() +import {useAsyncList} from "@react-stately/data"; + +export default function NearbyAmenitiesMap({hotel}) { + const mapRef = useRef(null) // 지도를 표시할 DOM 요소에 대한 참조 + const [map, setMap] = useState(null) // 지도 인스턴스 + const [address, setAddress] = useState(hotel?.address) + const [centerCoords, setCenterCoords] = useState() + const [nearAmenities, setNearAmenities] = useState([]) + const [markers, setMarkers] = useState([]) + const [category, setCategory] = useState("food") + const [distance, setDistance] = useState("100") + const [showTouristSpots, setShowTouristSpots] = useState(false) + const [touristSpots, setTouristSpots] = useState([]) + + const [page, setPage] = React.useState(1) + const rowsPerPage = 9 + const pages = Math.ceil(nearAmenities?.length / rowsPerPage) + + useEffect(() => { + // 네이버 지도 API 스크립트가 이미 로드되었는지 확인 + if (!map && window.naver && window.naver.maps) { + initMap() + } else if (!map) { + // 스크립트가 아직 로드되지 않았다면 로드를 기다림 + const checkIfNaverMapsIsLoaded = setInterval(() => { + if (window.naver && window.naver.maps) { + clearInterval(checkIfNaverMapsIsLoaded) + initMap() + } + }, 100) } - }, 100) - } - if (address && map) { - naver.maps.Service.geocode( - { - query: address, - }, - function (status, response) { - if (status !== naver.maps.Service.Status.OK) { - return alert("주소를 찾을 수 없습니다.") - } - - const result = response.v2.addresses[0] - const coords = new naver.maps.LatLng(result?.y, result?.x) - - setCenterCoords(coords) - - // 지도 중심 이동 - map.setCenter(coords) - - // 지도 확대 - map.setZoom(15) - - // 마커 생성 (선택적) - new naver.maps.Marker({ - position: coords, - map: map, - icon: { - content: `
+ if (address && map) { + naver.maps.Service.geocode( + { + query: address, + }, + function (status, response) { + if (status !== naver.maps.Service.Status.OK) { + return alert("주소를 찾을 수 없습니다.") + } + + const result = response.v2.addresses[0] + const coords = new naver.maps.LatLng(result?.y, result?.x) + + setCenterCoords(coords) + + // 지도 중심 이동 + map.setCenter(coords) + + // 지도 확대 + map.setZoom(15) + + // 마커 생성 (선택적) + new naver.maps.Marker({ + position: coords, + map: map, + icon: { + content: `
marker
`, - }, - }) + }, + }) + } + ) } - ) - } - }, [address, map]) - - // 지도 초기화 함수 - function initMap() { - // 지도를 생성할 때 필요한 옵션 설정 - const mapOptions = { - center: new naver.maps.LatLng(37.3595704, 127.105399), // 지도의 초기 중심 좌표 - zoom: 10, // 지도의 초기 확대 레벨 + }, [address, map]) + + // 지도 초기화 함수 + function initMap() { + // 지도를 생성할 때 필요한 옵션 설정 + const mapOptions = { + center: new naver.maps.LatLng(37.3595704, 127.105399), // 지도의 초기 중심 좌표 + zoom: 10, // 지도의 초기 확대 레벨 + } + + // 지도 생성 + const createdMap = new naver.maps.Map(mapRef.current, mapOptions) + + // 지도 상태 업데이트 + setMap(createdMap) } - // 지도 생성 - const createdMap = new naver.maps.Map(mapRef.current, mapOptions) - - // 지도 상태 업데이트 - setMap(createdMap) - } - - useEffect(() => { - if (category === "tourspot") { - clearMarkers() - setShowTouristSpots(true) - } else if (map && centerCoords && category === "food") { - setShowTouristSpots(false) - clearMarkers() - axios - .get( - `http://localhost:8080/api/v1/locations/${category}?latitude=${centerCoords.lat()}&longitude=${centerCoords.lng()}&distance=${distance}` - ) - .then((r) => { - setNearAmenities(r.data) - setPage(1) - map.setCenter(centerCoords) + useEffect(() => { + if (category === "tourspot") { + clearMarkers() + setShowTouristSpots(true) + } else if (map && centerCoords && category === "food") { + setShowTouristSpots(false) + clearMarkers() + axios + .get( + `http://localhost:8080/api/v1/locations/${category}?latitude=${centerCoords.lat()}&longitude=${centerCoords.lng()}&distance=${distance}` + ) + .then((r) => { + let newData = r.data.map((v) => { + return { + id: v.id, + name: v.name, + coord: {...v.coord}, + dist: calculateDistance(v.coord.y, v.coord.x, centerCoords?.lat(), centerCoords?.lng()), + } + }) + setNearAmenities(newData) + setPage(1) + map.setCenter(centerCoords) + }) + } + }, [centerCoords, category, distance]) + + const clearMarkers = () => { + markers.forEach((v) => { + v.setMap(null) }) + setMarkers([]) } - }, [centerCoords, category, distance]) - const clearMarkers = () => { - markers.forEach((v) => { - v.setMap(null) - }) - setMarkers([]) - } - - useEffect(() => { - if (nearAmenities.length !== 0) { - if (!showTouristSpots) { - if (markers.length !== 0) { - markers.forEach((v, index) => { - v.setMap(null) - }) - setMarkers([]) - } - nearAmenities.forEach((v) => { - let latLng = new naver.maps.LatLng(v.coord.y, v.coord.x) - - markers.push( - new naver.maps.Marker({ - position: latLng, - title: v.name, - map: map, - icon: { - content: `