Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #3 현재 위치 불러오기

dayannne·2024년 7월 28일
0
post-custom-banner

현재 위치 불러오기

이전에 임의로 location을 설정했다면 이번에는 처음 페이지 접속/실행 시 현재 위치를 기준으로 지도를 렌더링하는 훅을 구현해 보자.

useGeolocation 훅 작성하기

useGeolocation.ts라는 파일을 생성 한다.

1.1 상태 초기화

import { useState, useEffect } from 'react';
import { Latlng } from '../shared/types/map';

const useGeolocation = () => {
  const [location, setLocation] = useState<Latlng | null>(null); // 현재 위치를 저장할 상태

  
  return { location };
};

export default useGeolocation;

우선, useState를 사용하여 현재 위치 정보를 저장할 상태를 초기화한 다음 상태의 초기값은 null로 설정한다.

여기서 Latlng은 위도(latitude)와 경도(longitude)를 미리 설정한 타입이다.

export interface Latlng {
  latitude: number;
  longitude: number;
}

1.2 위치 정보 요청

컴포넌트가 처음 렌더링될 때 위치 정보를 요청해야 하므로, useEffect를 사용하여 위치 정보 요청 로직을 실행한다.

import { useState, useEffect } from 'react';
import { Latlng } from '../shared/types/map';

const useGeolocation = () => {
  const [location, setLocation] = useState<Latlng | null>(null); // 현재 위치를 저장할 상태

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(successHandler, errorHandler); // 성공시 successHandler, 실패시 errorHandler 함수가 실행된다.
  }, []);

  const successHandler = () => {};

  const errorHandler = () => {};

  return { location };
};

export default useGeolocation;

컴포넌트가 마운트될 때 usenavigator.geolocation.getCurrentPosition 메서드를 통해 브라우저의 Geolocation API를 사용하여 현재 위치를 가져올 수 있으며, 메소드는 순서대로 성공, 실패 시의 콜백 함수 2개를 인자로 받는다.

콜백 함수 작성

import { useState, useEffect } from 'react';
import { Latlng } from '../shared/types/map';

const useGeolocation = () => {
  const [location, setLocation] = useState<Latlng | null>(null); // 현재 위치를 저장할 상태

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(successHandler, errorHandler); // 성공시 successHandler, 실패시 errorHandler 함수가 실행된다.
  }, []);

  const successHandler = (response: {
    coords: { latitude: number; longitude: number };
  }) => {
    const { latitude, longitude } = response.coords;
    setLocation({ latitude, longitude });
  };

  const errorHandler = (error: GeolocationPositionError) => {
    console.log(error);
  };

  return { location };
};

export default useGeolocation;

successHandler

위치 정보 요청이 성공했을 때 호출되는 successHandler 함수는 response 객체를 인자로 받는다.
response.coords는 Geolocation API가 반환하는 객체로, 여기서 latitudelongitude를 추출해 setLocation을 통해 상태를 업데이트한다.

errorHandler

위치 정보 요청이 실패했을 때 호출되는 errorHandler 함수는 GeolocationPositionError 타입의 error 객체를 인자로 받는다.

적용

이전 2탄 지도 띄우기 포스트 글 작성 시 Map 객체 생성 시 임의의 위치값을 넣었었는데,
이번에는 useGeoLocation을 사용해 현재 위치 값을 넣어 Map 객체를 생성해 보자.

import useGeolocation from '@/app/_hooks/useGeolocation';

//...

const MapProvider: React.FC<MapProps> = ({ children }) => {
  const { location } = useGeolocation(); // 현재 위치 값 불러오기

  const mapRef = useRef<HTMLDivElement>(null);
  const [map, setMap] = useState<kakao.maps.Map | null>(null);
  const [markerClusterer, setMarkerClusterer] =
    useState<kakao.maps.MarkerClusterer | null>(null);
  const [overlays, setOverlays] = useState<kakao.maps.CustomOverlay[]>([]);
  const [prevKeyword, setPrevKeyword] = useState<string[]>([]);
  const [places, setPlaces] = useState<IPlace[]>([]);
  const [prevLocation, setPrevLocation] = useState<kakao.maps.LatLng | null>(
    null,
  );
  const [currLocation, setCurrLocation] = useState<kakao.maps.LatLng | null>(
    null,
  );

  useEffect(() => {
    const { kakao } = window;

    kakao?.maps.load(() => {
      const mapElement = mapRef.current;
      // 컴포넌트 mount 후 DOM 요소에 접근
      if (mapElement) {
        const options = {
          center: new kakao.maps.LatLng(
            location?.latitude as number,
            location?.longitude as number, // 현재 경도, 위도 적용
          ),
          level: 3,
          smooth: true,
          tileAnimation: false,
        };
        // 지도 생성
        const kakaoMap = new kakao.maps.Map(mapElement, options);

        // 현재 중심좌표 값 갱신
        kakao.maps.event.addListener(kakaoMap, 'dragend', function () {
          const latlng = kakaoMap.getCenter();
          setCurrLocation(latlng);
        });

        setMap(kakaoMap);
      }
    });
  }, [location?.latitude, location?.longitude]);
profile
☁️
post-custom-banner

0개의 댓글