react-naver-maps로 현재 위치 맵에 표시하기 (React-Typescript)

Devinix·2024년 4월 9일
0
post-thumbnail

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;
profile
프론트엔드 개발

0개의 댓글