milideal / client

[군 장병 AI · SW 역량강화 by KE] 하반기 프로젝트입니다.
https://milideal.site
1 stars 0 forks source link

지도 다중 생성 #16

Closed junglesub closed 9 months ago

junglesub commented 9 months ago

@i4song KakaoMap.tsx 파일에서 지도를 생성하는 코드입니다.

useEffect(() => {
    const mapContainer = document.getElementById("map") as HTMLElement,
      mapOption = {
        center: new kakao.maps.LatLng(y, x),
        level: 3,
      };

    const map = new kakao.maps.Map(mapContainer, mapOption); // 맵생성

    map.setDraggable(true);
    const markers = storeList?.map((store) => {
      return new kakao.maps.Marker({
        map: map,
        position: new kakao.maps.LatLng(store.coord.y, store.coord.x),
        clickable: true,
        title: store.name,
        image: new kakao.maps.MarkerImage(
          getMarkerImageSrc(store),
          new kakao.maps.Size(64, 63),
          { offset: new kakao.maps.Point(20, 46) }
        ),
      });
    });
    if (markers)
      markers.forEach((marker) => {
        kakao.maps.event.addListener(marker, "click", () => {
          map.panTo(marker.getPosition());
        });
      });

    map.setMaxLevel(6);
  }, [storeList]); // storeList 바뀔 시 다시 코드 실행

해당 useEffect 코드블럭은 실행될 때 마다 새로운 지도가 생성이 됩니다.

 const map = new kakao.maps.Map(mapContainer, mapOption);

하지만 아래 [storeList] 코드 부분이 있어서 단순히 storeList 핀을 생성하는 것이 아닌 지도 자체를 재생성하는 것 같은데 확인해봐야할 것 같습니다.

junglesub commented 9 months ago

image

실제로 useEffect 블럭에 console.log 를 추가해보면 dependencies list 가 비어 있어도 2번 생성 되는 것을 알 수 있었습니다. 결과론 적으로 줌이나 움직일때 뒤에 잔상이 남는 것을 확인할 수 있었습니다.

junglesub commented 9 months ago

방법1: map 변수 state 화

해결하기 위해 useEffect 안에 있는 map 변수를 state 으로 관리하는 방법으로 만약에 변수가 null 이 아니라면 생성하는 코드를 호출을 안하는 방식이 어떨까 싶습니다.

방법2: react-kakao-maps-sdk 라이브러리 사용

NPM: https://www.npmjs.com/package/react-kakao-maps-sdk 블로그: https://velog.io/@wlwl99/React-Kakao-Map-SDK-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

이 방법으로는 컴포넌트화 되어 있는 지도를 사용하는 방법입니다. 확인해보니 컴포넌트처럼 사용할 수 있어 핀도 컴포넌트로 꼽을 수 있고 무엇보다 ref 을 배정하여 관리할 수 있습니다.

지도를 많이 사용하는 프로젝트인 만큼 사용해보는 것도 좋을 것 같습니다.

o-bard-o commented 9 months ago

도입 초기에 저도 React-Kakao-Map-SDK를 사용해봤었습니다. 저희 프로젝트에 있는 kakao type도 해당 라이브러리에서 따왔습니다.

그런데 제가 직접 사용해본 결과 원인을 알 수 없는 버그가 많이 발생했습니다. 공식 라이브러리가 아니기 때문에 지원을 받기도 쉽지 않았고, 그래서 kakao api를 직접 사용하기로 결정했습니다.

ref를 배정하여 관리하는 건 해당 라이브러리 없이도 할 수 있고, 제가 kakao 샘플 코드를 복붙해서 사용하느라 document.getElementById가 남아있었어요. 이 부분은 아래 PR에서 적용했습니다.

useEffect 내부가 두 번 호출되는 건, React.StrictMode의 사용 때문에 발생하는 문제로, production 레벨에서는 발생하지 않습니다. development 환경에서 똑같이 재현되었고, 이는 추후 수정 여부를 결정해도 될 것 같습니다.

junglesub commented 9 months ago

네 맞습니다! useEffect dependencies 가 비어있을 때도 두번 실행되는 이유는 StrictMode 와 개발 환경때 발생합니다. 따라서, 실제 환경에서는 영향이 없습니다. 다만 개발 할 때 실제 환경과 비슷하게 하기 위해서 이미 지도가 만들어졌을 경우 다시 만들어지는 것을 방지하는 코드를 추가해도 좋을 것 같습니다.

제가 우려했던 점은 [storeList] 가 dependencies 으로 등록되어 있어 storeList 가 업데이트 될때마다 지도가 새로 그려진다는 것이 요점이었습니다. 하지만 최근 PR(#18) 에서 해결이 된 것 같아 특별한 문제가 없으면 이슈를 마감하겠습니다.

junglesub commented 9 months ago

useEffect 내부가 두 번 호출되는 건, React.StrictMode의 사용 때문에 발생하는 문제로, production 레벨에서는 발생하지 않습니다. development 환경에서 똑같이 재현되었고, 이는 추후 수정 여부를 결정해도 될 것 같습니다.

조금 더 조사해본 결과 React.StrictMode 에서 useEffect 가 두번 실행되는 이유는 컴포넌트가 순간적으로 2번 로딩되는 최악의 상황을 연출하기 위함이라고 합니다. StrictMode 에서 보여주는 것처럼 그런 상황이 발생하면 지도가 2번 로딩되는 등 sideeffect 가 발생한다는 점을 발견할 수 있었습니다.

해결 책으로는 컴포넌트가 언마운트 하기 전에 지도 객체를 삭제하는 방법이 있을 것 같습니다.

junglesub commented 9 months ago

src/pages/Map/hooks/useMap.ts

return () => {
      mapContainer.current!.innerHTML = ""; // 컴포넌트 해제시 지도 삭제
 };

마지막에 이런 코드를 붙여서 컴포넌트 언마운트시 기존 지도를 없애면 두번 로드 되는 것을 방지할 수 있습니다 https://github.com/milideal/client/issues/14#issuecomment-1746132047

_Originally posted by @junglesub in https://github.com/milideal/client/pull/18#discussion_r1345210588_