[TIL] 카카오맵 API 마커 오버레이 만들기

기성·2024년 9월 19일
1

TIL

목록 보기
58/81

우선 시작에 앞서 kakaomap api를 사용하는데에 리액트에 적용하기 위해서 스크립트 보다는 리액트에서 사용하기 좋게 만들어진 이 react-kako-maps-sdk를 사용하기로 했다. https://react-kakao-maps-sdk.jaeseokim.dev/

마커 찍기

우선 마커를 찍어줄 때 어떻게 진행했냐면 keywordSearch()를 통해서 신병교육대와 훈련소를 찾고 catergorySearch()를 통해 "FD6"이라는 코드로 음식점을 다 가져왔다.

const getPlacesPositionForMarkers = (data) => {
    let temp = [];
    for (let i = 0; i < data.length; i++) {
      temp.push(data[i]);
    }
    let addedReviewsAndBookMarks = temp.map((e) => {
      return { ...e, reviews: [], bookmark: 0 };
    });
    setRestaurants(addedReviewsAndBookMarks);
    setMarkers(temp);
  };

  const searchRestaurants = (bounds) => {
    places.categorySearch('FD6', getPlacesPositionForMarkers, {
      bounds: bounds,
      useMapBounds: true
    });
  };

  const keywordSearch = () => {
    if (!map) return;
    places.keywordSearch(campSearchWordConverter(paramId), (data, status, _pagination) => {
      if (status === window.kakao.maps.services.Status.OK) {
        const bounds = new window.kakao.maps.LatLngBounds();
        let markers = [];
        for (var i = 0; i < data.length; i++) {
          markers.push({
            position: {
              lat: data[i].y,
              lng: data[i].x
            },
            content: data[i].place_name
          });
          bounds.extend(new window.kakao.maps.LatLng(data[i].y, data[i].x));
        }
        // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
        map.setBounds(bounds);
        map.setLevel(5);
        searchRestaurants(map.getBounds());
      }
    });
  };

이렇게 마크를 markers라는 배열에 담아주고 나서

return(
	<Map
      center={{
        lat: 37.566826,
        lng: 126.9786567
      }}
      style={{
        width: '100%',
        height: '100%'
      }}
      level={5}
      onCreate={setMap}
    >
      {markers.map((marker) => {
        return (
          <EventMarkerContainer
            key={marker.id}
            position={{ lat: marker.y, lng: marker.x }}
            content={marker.place_name}
          />
        );
      })}
    </Map>
  )

EventMarkerContainer에다가 position과 오버레이에 넣어줄 컨텐츠를 넣어준다.
따로 제공되는 컴포넌트는 아니고 MapMarker를 묶어둔 컴포넌트이다.

오버레이 만들기

const EventMarkerContainer = ({ position, content }) => {
  const map = useMap();
  const [isVisible, setIsVisible] = useState(false);
  const [isOpen, setIsOpen] = useState(false);
  const handleMarkerInfo = (marker) => {
    map.panTo(marker.getPosition());
    setIsOpen(true);
  };

  return (
    <>
      <MapMarker
        position={position}
        clickable={true}
        onClick={(marker) => handleMarkerInfo(marker)}
        image={{ src: '/assets/markerLine2.png', size: { width: 60, height: 60 } }}
        // onMouseOver={() => setIsVisible(true)}
        // onMouseOut={() => setIsVisible(false)}
      />
      {(isVisible || isOpen) && (
        <CustomOverlayMap position={{ lat: String(Number(position.lat) + 0.0002), lng: position.lng }}>
          <div className="flex flex-col bg-white">
            <button onClick={() => setIsOpen(false)}>닫기</button>
            <div>{content}</div>
          </div>
        </CustomOverlayMap>
      )}
    </>
  );
};
export default EventMarkerContainer;

맨 처음에는 인포윈도우로 표시하려 했는데 스타일링이 잘 안되는 것 같았다. width나 height가 설정이 안되는 느낌이라 커스텀 오버레이를 만들기로 했다.

인포윈도우로 사용하기 위해서는 원래 MapMarker의 children으로 넣어주는 거였는데 오버레이는 그럴 필요가 없었다.

return (
  <MapMarker
    position={position}
    clickable={true}
    onClick={(marker) => handleMarkerInfo(marker)}
    image={{ src: '/assets/markerLine2.png', size: { width: 60, height: 60}}}>
    {content}
  </MapMarker>

요렇게 넣어주면 content가 인포윈도우로 자연스럽게 나가는데 커스텀 오버레이는 그냥 마커의 밖에서 선언을 하고 open여부를 결정해주면 된다. 닫기도 가능해야하기 때문에 state로 관리해서 열고 닫아주면 끝!

내일은 오버레이 css좀 건드려봐야겠다

profile
프론트가 하고싶어요

2개의 댓글

comment-user-thumbnail
2024년 9월 20일

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ^^7

1개의 답글