useGeolocation 훅
import { useState, useEffect } from 'react';
interface locationType {
loaded: boolean; // 위치 정보 로드 여부
coordinates?: { lat: number; lng: number }; // 위도 및 경도 정보
error?: { code: number; message: string }; // 에러 정보
}
const useGeolocation = () => {
// 위치 정보 상태를 관리함. 초기 상태는 로드되지 않은 상태임
const [location, setLocation] = useState<locationType>({
loaded: false,
coordinates: { lat: 0, lng: 0 },
});
// 성공적으로 위치 정보를 가져왔을 때 실행될 콜백 함수
const onSuccess = (location: {
coords: { latitude: number; longitude: number };
}) => {
setLocation({
loaded: true, // 위치 정보 로드 상태를 true로 설정
coordinates: {
lat: location.coords.latitude, // 위도 설정
lng: location.coords.longitude, // 경도 설정
},
});
};
// 위치 정보를 가져오는데 실패했을 때 실행될 콜백 함수
const onError = (error: { code: number; message: string }) => {
setLocation({
loaded: true, // 위치 정보 로드 상태를 true로 설정 (시도는 했으나 실패함)
error, // 에러 정보 설정
});
};
// 컴포넌트가 마운트될 때 위치 정보를 요청하고, 언마운트 될 때 관찰을 취소함.
useEffect(() => {
let watcher: number | null = null; // 위치 정보를 관찰하는 watcher ID를 저장할 변수
if ('geolocation' in navigator) { // 브라우저가 위치 정보 API를 지원하는지 확인
watcher = navigator.geolocation.watchPosition(onSuccess, onError); // 위치 정보를 요청하고 변경 사항을 감시
} else {
onError({ // 지원하지 않는 경우, 에러 처리
code: 0,
message: 'Geolocation not supported',
});
}
return () => { // 컴포넌트 언마운트 시 실행될 정리 함수
if (watcher !== null) {
navigator.geolocation.clearWatch(watcher); // 위치 정보 감시를 취소
}
};
}, []);
return location; // 현재 위치 정보 상태를 반환
};
export default useGeolocation;
MapComponent
import { Box } from '@chakra-ui/react';
import { useState, useEffect } from 'react';
import { Container as MapDiv, NaverMap, useNavermaps } from 'react-naver-maps';
import GardensContainer from './GardensContainer';
import GardenMarker from './Marker/GardenMarker';
import MyMarker from './Marker/MyMarker';
import ShowGardensButton from './ShowGardensButton';
import MapSpinner from './Spinner';
import useGeolocation from '@/hooks/useGeolocation';
import { useGetEveryGardens } from '@/services/gardens/query';
const MapComponent = () => {
const [showGardens, setShowGardens] = useState(false);
// Naver 지도 API를 사용하기 위한 훅
const navermaps = useNavermaps();
// 사용자의 현재 위치를 가져옴.
const geolocation = useGeolocation();
const { data } = useGetEveryGardens();
const gardens: Garden[] = data?.gardenGetAllResponses;
// 정원 위치 데이터를 매핑함.
const locations = gardens?.map((garden) => ({
id: garden.gardenId,
lat: garden.latitude,
lng: garden.longitude,
}));
// 기본 위치를 설정함.
let position = {
lat: 37.3595704,
lng: 127.105399,
};
// 위치 정보가 로드되고 에러가 없으며 좌표가 있는 경우, 사용자의 위치로 설정함.
if (geolocation.loaded && !geolocation.error && geolocation.coordinates) {
position = {
lat: geolocation.coordinates.lat,
lng: geolocation.coordinates.lng,
};
}
// 사용자 위치 정보가 로드 되지 않았을 시 로딩 스피너를 표시
if (!geolocation.loaded) {
return <MapSpinner />;
}
// 로딩이 완료되면 지도와 관련 컴포넌트를 렌더링함
return (
<Box
position="relative"
overflow="hidden"
h={{ mobile: 'calc(100vh - 167px)', tablet: 'calc(100vh - 166px)' }}
zIndex="0"
>
<ShowGardensButton {...{ showGardens, setShowGardens }} />
<GardensContainer showGardens={showGardens} gardens={gardens} />
<MapDiv style={{ width: '100%', height: '100%' }}>
<NaverMap
defaultCenter={new navermaps.LatLng(position.lat, position.lng)}
defaultZoom={10}
zoomControl
zoomControlOptions={{
style: naver.maps.ZoomControlStyle.SMALL,
position: navermaps.Position.TOP_LEFT,
}}
>
{locations.map((location) => (
<GardenMarker
navermaps={navermaps}
position={new navermaps.LatLng(location.lat, location.lng)}
key={location.id}
onClick={() => {
setShowGardens(true);
}}
/>
))}
<MyMarker navermaps={navermaps} position={position} />
</NaverMap>
</MapDiv>
</Box>
);
};
export default MapComponent;