개발일기/Web

[React] 카카오 맵 커스텀을 해보았다. (feat. 마커 인포 꾸미기)

DongKeun2 2024. 11. 13. 19:30
 카카오 맵을 쓰면서 커스텀해야 할 부분이 너무 많은 것 같다. 내가 고생했던 부분과 해결방법들에 대해 공유해보려한다. 난 간단한 기능만 넣을 생각이었는데 구현은 그리 간단하지 않았다.

 

 

 일단 내가 원하는 기능을 정리하면 이랬다.

  1. 내가 원하는 장소를 지도에 포함시키고, 마커로 표시한다.
  2. 마커 위에 인포를 붙인다.
  3. 모바일에서 동작하길 기대하기 때문에 맵의 이동은 막는다.(static형식의 지도)
  4. 대신 맵 영역에서 기존 스와이프 동작은 유지한다.
  5. 마커와 인포에 클릭 이벤트를 붙인다.

 

여기까지 봤을 때 굉장히 심플하다. 그냥 맵 넣고 공식 문서에서 제공된 소스로 뚝딱 해결할 줄 알았다.

 

난 리액트를 사용하여 개발했기 때문에 react-kakao-map-sdk를 사용했다.

 

Hello from react-kakao-maps-sdk docs | react-kakao-maps-sdk docs

Description will go into a meta tag in <head />

react-kakao-maps-sdk.jaeseokim.dev

 

 

Static Map 적용, 마커 생성

 첫 시작은 당연히 지도의 인터렉션은 전부 제거할 생각이었기 때문에 sdk에서 제공하는 Static 맵을 사용했다

 

 <StaticMap
    center={{
      lat: 33.450701,
      lng: 126.570667,
    }}
    style={{
      width: '100%',
      height: '450px',
    }}
    marker={{
      lat: 33.4,
      lng: 126.570667,
    }}
    level={3}
  />

 

 

 하지만 Static 맵은 이름대로 굉장히 정적이라 marker에 자유가 없었다.

이럴거면 marker를 왜 뚫어둔건지 이해가 안되지만 center와 marker의 위도 경도값을 다르게 해도 결국 마커는 지도의 중심에 위치한다.

 

난 정적이지만 중심과 마커가 일치하지 않는 지도를 원했다.

그래서 과감하게 StaticMap을 버리기로 했다.

 

Map 적용, 드래그(스와이프) 막기

 지도의 드래그 이동을 막는 방법은 아주 간단하다. Map에 draggable을 넣어주면 된다.

 <Map
    ...
    draggable={false}
  />

이제 지도에서 드래그로 위치 이동이 불가해진다.

 

 

 

이렇게 쉽게 해결하나 싶었는데, 모바일 환경에서 테스트해보니 엄청나게 불편했다.

지도 영역에서 스와이프 동작이 아예 막혀버린 것이다...

 

지도가 영역을 잡아먹고 이벤트를 막아버려서 해결을 위해 지도 위를 덮는 div를 하나 만들어줬다.

 

const Mapshade = styled.div`
  position: absolute;
  z-index: 3;
  top: 0;
  width: 100%;
  height: 100%;
`;

 

같이 감싸준 뒤에 맵의 크기와 동일하게 만들어줬다.

Map에 포함된 z-index의  최대가 2여서 3으로 설정했더니 잘 동작했다.

 

 

인포 커스텀하기

 마커와 인포를 제공해준다. 근데 스타일이 엉망이다.

 <MapMarker
    position={{
      lat: 33.450701,
      lng: 126.570667,
    }}
  >
    <div style={{ padding: '5px', color: '#000' }}>
      Hello World! <br />
      ...
    </div>
  </MapMarker>
</Map>

 

 심지어 인포윈도우를 커스텀할 수 있는 선택자가 하나도 없었다. (내가 못 찾은걸수도 있음)

내부는 MapMarker 안에서 커스텀이 가능한데, 나는 좀 둥글둥글한 박스를 가지고 싶었다.

 

 어차피 Map이 센터를 기준으로 반응형으로 동작하는 것도 아니고 사용자가 뷰포트를 바꿀 일이 없다고 판단을 해서 인포 자리에 내가 만든걸 올려버리기로 했다.

// MapMarkerComponent
<div>
  <MarkerText>
    <span>Hello World!</span>
  </MarkerText>
  <MapMarker id='map-marker' position={{ lat: 33.450701, lng: 126.570667, }} />
</div>

 

 이런식으로 아까와 마찬가지로 적당한 위치를 찾아 박아주었다.

 

인포와 마커에 클릭 이벤트 설정하기

 나는 인포와 마커 영역을 클릭하면 카카오맵을 호출하는 기능을 넣고 싶었다.

이것도 원래 제공되는 기능인데,,, MapMarker에 clickable={true} 를 넣어주면 onClick 메서드를 사용할 수 있다.

인포는 내가 커스텀했으니 그냥 이벤트 등록만 하면 가능했다.

 

하지만 마커같은 경우에는 내가 스와이프를 사용하려고 지도 위를 덮어버렸기 때문에 마커에 클릭 이벤트를 설정해준다 한들 사용자가 이를 클릭할 수 있을리가 없었다.

 

그래서 적용한 방법이 인포와 마찬가지로 마커 위치에 클릭할 수 있는 div를 올려주는 것이었다.

// MapMarkerComponent
<div>
  <MarkerText>
    <a id='conventionurl' href='.'>
      <span>{config.hall.name}</span>
    </a>
  </MarkerText>
  <MarkerCllickBox onClick={handleMarkerClick} />
  <MapMarker position={postion} />
</div>

 

 이렇게 MarkerClickBox를 두어 마커 위치에 딱 올려주었다.

 

 

사용자 뷰포트에 따라 마커와 인포 위치 조정

나는 Map의 max-width를 720px로 두었고, 그 이하에서는 100%값을 가지게 해두었다.

또한 450px에서는 높이도 400px에서 300px로 바꿔주었다.

 

만약 사용자의 뷰포트가 변하면 지도의 중심도 바뀌게 된다.

 

마커와 인포 위치를 absolute left와 top으로 인위적으로 맞춰놨기 때문에 이런 변경사항이 생기면 올바른 위치에 있지 못한다. 그렇기 때문에 이 값도 비율에 맞춰 유동적으로 변할 수 있게 설정을 해주어야 한다.

 

화면 너비가 720px 이상인 경우

지도의 너비가 720px

top: 98px

left: 313px

 

 

 

 

화면 너비가 450px 이상 720px 이하인 경우

지도의 너비가 고정되지 않은 값

top: 98px

left: 313px - (720px - 100vw) / 2

 

 

 

화면 너비가 450px 이하인 경우

지도의 너비가 고정되지 않은 값

top: 48px

left: 313px - (720px - 100vw) / 2

 

 

 

이렇게 어떤 화면이든 마커와 인포가 정확한 곳에 위치할 수 있게 만들었다.

 

단순한 요구사항을 반영하는 건데 해야할 작업이 꽤 많아서 당황했지만 그래도 마무리할 수 있어서 다행이다.

 

 이번에는 정적인 지도로 만들어서 했기 때문에 이 방법이 통했지만,

드래그가 가능한 지도에서는 인포를 커스텀해서 넣을 방법을 아직 찾지 못했다는 점은 아쉽다.