[OMT] 지도API사용

2023.12.31(일)


저번달에 개발을 완료했던 지도 API를 복습할겸 다시 포스팅해보려한다.

우선 나는 KAKAO map과 NAVER map 둘다 사용해보았다.

내가 원했던 건 현재 내 위치에서 가까운 영화관을 찾아주고 유저가 선택할 수 있게 만들고 싶었다. 두 API를 사용했을 때 내가 원하는 바를 좀 더 쉽게 이룰 수 있었던 건 KAKAO map이였다.

내가 원한 기능

  1. 현재 내 위치로 기본 설정 되기 ( Kakao, Naver 둘 다 적용 가능 )
  2. 전체 영화관 검색 후 띄우기 ( Naver로는 어려움을 겪고 Kakao에서는 비교적 간단하게 해결 )
  3. 영화관 별 구분해서 띄우기
  4. 지도가 로드되는 동안 로딩 띄우기
현재 완료되어 배포된 화면

스크린샷 2024-01-01 13 39 45

위 화면의 자세한 내용은 빠른예매 개발 포스팅에서 다뤄볼 예정이다. 오늘은 Map Api에 대해서만 설명해보려한다.

참고 페이지

스크린샷 2024-01-01 13 42 21

[https://apis.map.kakao.com/web/sample/keywordBasic/] 위 페이지를 참고해서 최대한 활용해보았다.

정말 다양한 방식으로 커스터마이징을 할 수 있는데 우측에 직접해보기를 통해서 내가 원하는 방식으로 코드를 수정해볼 수 있다는 점이 정말 편하고 좋았다.

Code

KakaoMap.tsx


const KakaoMap: React.FC = () => {
  const CLIENT_ID = process.env.NEXT_PUBLIC_KAKAO_MAP_KEY;
  const KAKAO_SDK_URL = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${CLIENT_ID}&autoload=false&libraries=services`;
  const [currentMarker, setCurrentMarker] = useState<any>(null);
  const [currentLocation, setCurrentLocation] = useState({ lat: 0, lng: 0 });
  const [map, setMap] = useState<any>(null);
  const [marker, setMarker] = useState<any>(null);
  //영화 티켓 데이터
  ...
  const router = useRouter();

  useEffect(() => {
   
    // 현재 위치 가져오기
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          const { latitude, longitude } = position.coords;
          setCurrentLocation({ lat: latitude, lng: longitude });
        },
        (error) => {
          console.error('Error getting current location:', error);
        }
      );
    } else {
      console.error('Geolocation is not supported by this browser.');
    }
  
  }, []);

  useEffect(() => {
    const script = document.createElement('script');
    script.src = KAKAO_SDK_URL;
    script.async = true;
    document.head.appendChild(script);

    script.onload = () => {
      kakao.maps.load(() => {
        const container = document.getElementById('map');
        const options = {
          center: new kakao.maps.LatLng(
            currentLocation.lat,
            currentLocation.lng
          ),
          level: 5
        };
        AOS.init();
        const mapInstance = new kakao.maps.Map(container, options);
        setMap(mapInstance);
        const ps = new kakao.maps.services.Places();

        const redMarkerImage = new kakao.maps.MarkerImage(
          '/png/영화관마커.png', // 마커 이미지 제공
          new kakao.maps.Size(30, 30), // 마커 이미지 사이즈 커스텀
          { offset: new kakao.maps.Point(15, 30) } 
        );

        // 마커 이미지로 마커를 추가
        const addMarkers = (data: any, status: any) => {
          if (status === kakao.maps.services.Status.OK) {
            data.forEach((place: any) => {
              const marker = new kakao.maps.Marker({
                position: new kakao.maps.LatLng(place.y, place.x),
                map: mapInstance,
                title: place.place_name,
                image: redMarkerImage
              });

              // 클릭 이벤트를 통한 현재 마커상태 업데이트
              kakao.maps.event.addListener(marker, 'click', function () {
                setCurrentMarker(place);
              });
            });
          }
        };

        ps.keywordSearch('CGV', addMarkers);
        ps.keywordSearch('메가박스', addMarkers);
        ps.keywordSearch('롯데시네마', addMarkers);

        // 현재 위치에 마커 추가하기 
        const markerInstance = new kakao.maps.Marker({
          position: new kakao.maps.LatLng(
            currentLocation.lat,
            currentLocation.lng
          )
        });

        markerInstance.setMap(mapInstance);
        setMarker(markerInstance);
      });
    };
  }, [currentLocation]);

  const handleXClick = () => {
    setCurrentMarker(null);
  };
  const handleNextClick = (id: string) => {
    localStorage.setItem('장소', id);
    setModalOpen(true);
  };

  const handleCurrentLocation = () => {
    if (map && marker) {
      map.panTo(
        new kakao.maps.LatLng(currentLocation.lat, currentLocation.lng)
      );

      marker.setPosition(
        new kakao.maps.LatLng(currentLocation.lat, currentLocation.lng)
      );
    }
  };

  return (
    <>
      <div className={styled.Map_Container}>
        <div
          id='map'
          style=
          className={styled.kakaoMap}
        ></div>

        <div
          className={styled.current_location}
          onClick={handleCurrentLocation}
        >
          <img src='/png/현재위치.png' alt='Current Location' />
        </div>
        {currentMarker && (
          <div
            className={styled.Theater_Marker}
            data-aos='fade-left'
            data-aos-duration='1000'
            id='Img'
          >
            <img
              src='/png/closed.png'
              onClick={handleXClick}
              className={styled.close}
            />

            <div className={styled.Theater_Content}>
              <div className={styled.Theater_Title}>
                {currentMarker.place_name}
              </div>
             .....

위 코드는 공식 페이지에 사용하기 쉽게 코드를 오픈해주어 어렵지 않게 작성할 수 있었다.

내가 애먹었던 부분은

  const KAKAO_SDK_URL = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${CLIENT_ID}&autoload=false&libraries=services`;

이 부분인데 libraries=services 이 내용을 추가해주지 않으면 내가 원했던 내 근처 영화관을 검색해서 표시하는 기능 즉


const ps = new kakao.maps.services.Places();  // 계속 오류 발생함 
const addMarkers = (data: any, status: any) => {
          if (status === kakao.maps.services.Status.OK) {
            data.forEach((place: any) => {
              const marker = new kakao.maps.Marker({
                position: new kakao.maps.LatLng(place.y, place.x),
                map: mapInstance,
                title: place.place_name,
                image: redMarkerImage
              });

              // 클릭 이벤트를 통한 현재 마커상태 업데이트
              kakao.maps.event.addListener(marker, 'click', function () {
                setCurrentMarker(place);
              });
            });
          }
        };

        ps.keywordSearch('CGV', addMarkers);
        ps.keywordSearch('메가박스', addMarkers);
        ps.keywordSearch('롯데시네마', addMarkers);

이 부분을 사용할 수 없다. ps 부분에서 계속 오류가 발생해서 왜인지 모르고 삽질을 계속 했었는데 libraries=services 를 추가해주지 않아서 생긴 오류였다..

이번에 지도 api를 사용해보면서 다시 한번 놀랐다.. 이게 대기업인가 .. 앞으로도 많은 api를 사용하게 될테지만 개발에 편리한 api가 계속해서 발전해 나가는 게 너무 좋다.. 내가 만들 수 있는 범위가 넓어지니까.. ㅎㅎㅎ