05
31

진짜 docs 100번 이상은 정독했다. 씁쓸하다.

1. 문제의 발단

  • 평소에 매우 자주 사용하는 지도 API다. (... 였다) 전투적으로 약 8달 이상은 사용했던 네이버 지도에서 쉽고도 어렵게 사용했던 Marker 시스템을 정리해 보려고 한다.

2. 기본적인 사용법

const loadScript = () => {
  return new Promise((resolve) => {
    if (document && document.querySelectorAll('#naver-map-sdk').length === 0) {
      const script = document.createElement('script');
      script.id = 'naver-map-sdk';
      script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=${NCP_CLIENT_ID}&submodules=geocoder,panorama`;
      script.onload = () => {
        resolve(true);
      };
      document.head.appendChild(script);
    } else {
      resolve(true);
      console.log('script already loaded');
    }
  });
};
  • 위와 같이 script를 심어준다. await로 지도 init시 호출하게 된다.
 const initMap = async (containerId: string, isActiveDistance: number) => {
	// 스크립트 로드
	await loadScript();

    // 지도 생성시 설정해 줄 옵션이다.
    const initialMapOptions = {
      center: [point.longitude, point.latitude],
      zoom: 16,
      minZoom: 12,
      maxZoom: 22,
      zoomOrigin: [point.longitude, point.latitude],
      pinchZoom: false,
      mapDataControl: false,
      disableDoubleClickZoom: false,
      disableDoubleTapZoom: false,
      logoControlOptions: {
        position: naver?.maps?.Position?.BOTTOM_LEFT,
      },
      mapDataControlOptions: {
        position: naver?.maps?.Position?.BOTTOM_CENTER,
      },
      scaleControlOptions: {
        position: naver?.maps?.Position?.BOTTOM_LEFT,
      },
    };

    const map = new naver.maps.Map(containerId, initialMapOptions) as any;

    new naver.maps.Circle({
      map: map,
      center: [point.longitude, point.latitude],
      radius: isActiveDistance + 100,
      fillColor: '#ffffff',
      fillOpacity: 0.2,
      strokeOpacity: 0,
    });

    // 마커생성은 함수로 만들어서 쓰고 있다.
    const pinMarker = makeIconMarker(map, [point.longitude, point.latitude], 'pin');

    // 지도 옵션 (마커나 지도에 커서를 바꿔줌)
    pinMarker.setCursor('default');
    map.setCursor('default');

    return map;
  };

  useEffect(() => {
    initMap('mainMap');
  }, []);
  • 이런 식으로 지도를 생성해 주고,  map이 들어갈 div에 id 값을 알맞게 준다.
  • 각종 지도 옵션 및 마커, 도형 등의 옵션은 docs를 참고한다. (https://navermaps.github.io/maps.js.ncp/docs/)

3. Marker에 대한 고찰

  • 지도에서 특정 마커를 지우거나 선택등을 하려면 그 특정 마커 요소 자체를 정확히 알고 가지고 있어야 한다.
  • 이때, state로 마커를 가지도 다닐 수는 없다.
  • 예를 들어, maker.setMap(null)로 특정 마커를 지울 수 있다. 이때, state에 마커를 넣어놓고 지우려고 하면 올바르게 동작하지 않는다.
  • 이때는 useRef를 사용한다.
const markerRef = useRef<naver.maps.Marker>();

    const marker = makeMarker(map, point, {
      value: 100
    });

markerRef.current = marker;

useEffect(() => {
    markerRef.current?.setMap(null);
    
    const marker = makeMarker(map, point, {
      value: 200
    });
    
    marker.setMap(mapRef.current);
    markerRef.current = marker;
    }
  }, [isActive]);
  • isActive가 변경될 때마다, 기존의 value 100의 마커는 사라지고, value 200을 가진 마커가 생성된다. (isActive가 단 한 번만 바뀐다는 전제 하에 이렇게 만들 수 있지만 급조한 코드라 어색하다)
  • 마찬가지로 마커 뿐 아니라 map 객체 자체나 도형들과 같은 지도 위 요소들도 다 useRef로 관리가 가능하고, 이를 list형태로 저장하여 사용할 수 도 있다.
COMMENT