[Vue.js] 지도 여러 번 그려지는 문제 문의 드립니다

Vue 3 환경에서 개발하고 있으며, 어떤 이유지는 모르겠으나 위 코드로 Local 환경에서는 지도가 여러번 그려지지 않는 것을 확인했습니다

문제는 npm run build 했을 때(빌드 파일)는 여러번 그려지는 부분을 막는 부분이 제대로 동작하지 않아 문의를 드리게 되었습니다

현재 public\index.html에는 따로 kakao map api script를 등록해 두지 않은 상태이며 kakaoMapPage.vue에다가 모든 코드를 작성한 상태입니다. 다른 컴포넌트 2개에서 kakaoMapPage.vue를 자식 컴포넌트로 두고 있습니다

kakaoMapPage.vue에서 지도 그리기 전에 this.map = null로 초기화 하는 방법으로도 시도해 보았지만 작동하지 않았습니다 :frowning:

구글링 하다가 kakao map api를 빌드하여 사용하려면 무조건 index.html에다가 선언해야 한다는 글을 보았는데, index.html에 <script src="//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=<%= process.env.VUE_APP_KAKAOMAP_KEY %>"></script> 이렇게 선언해 놓는다고 해도 그 다음에는 어떻게 접근해야 할 지 도저히 감이 오질 않아 도움 요청드립니다

아래는 코드 일부분 입니다. 불 필요한 부분은 삭제했습니다

<template>
  <div id="map"></div>
</template>

<script>
import FadeLoader from 'vue-spinner/src/FadeLoader.vue';
var imageSrc = require('@/assets/mapMarker.svg');

export default {
  props: ['cameraInfoList', 'newCameraInfoList'],
  components: {
    FadeLoader,
  },
  data() {
    return {
      cameraName: '',
      tempThumbailArr: [],
      thumnailArr: [], // base64 img
      totalThumbnailCount: 0,
      videoFlag: false,
      map: null, // 전역 map 변수 선언
      loadingFlag: false,
      tempCount: 0, // thumbnail count 용
      markers: [], // to remove markers
      overlays: [], // to remove overlays
      modalFlag: null,
      modal: null,
      lat: 36.368889,
      lon: 126.976667,
      zoom: 13,
      cameraSerial: null,
      equalPositionCameraList: [],
    };
  },
  mounted() {
    if (!window.kakao || !window.kakao.maps) {
      const script = document.createElement('script');
      script.src = `//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=${process.env.VUE_APP_KAKAOMAP_KEY}`;
      script.addEventListener('load', () => {
        console.log('Loaded.', window.kakao);
        kakao.maps.load(this.initMap);
      });
      document.head.appendChild(script);
    } else {
      console.log('It has already been loaded.', window.kakao);
      this.initMap();
    }
    this.$socket.client.emit('thumnail', this.$socket.client.id);
    console.log('emit');
  },
  methods: {
    initMap() {
      /* global kakao */
      const container = document.getElementById('map');
      const options = {
        mapTypeId: kakao.maps.MapTypeId.HYBRID,
        center: new kakao.maps.LatLng(this.lat, this.lon),
        level: this.zoom,
      };
      this.map = new kakao.maps.Map(container, options);
      this.map.setMaxLevel(13);

      this.mapMarkerMethods();
    },

    getLatLon(lat, lon) {
      this.map.setLevel(3);
      this.map.setCenter(new kakao.maps.LatLng(lat, lon));
    },

    getThumnail(vm) {
      this.$socket.client.on('reqthumnail', function (info) {
        vm.tempCount = 0; // 실제 배열에 들어간 값 count 를 위한 변수
        vm.tempThumbailArr.forEach((element) => {
          vm.tempCount++;
        });


        if (info.total === vm.tempCount) {
          // backend 에서 넘어온 totalCount 와 실제 값 count 같을 때,
          vm.thumnailArr = vm.tempThumbailArr;

          // Reset
          vm.markers.forEach((ele) => {
            ele.setMap(null);
          });
          vm.markers = [];
          vm.overlays.forEach((ele) => {
            ele.setMap(null);
          });
          vm.overlays = [];

          vm.mapMarkerMethods();
          vm.tempThumbailArr = [];
        }
      });
    },

    mapMarkerMethods() {
      var imageSize = new kakao.maps.Size(24, 35); // 에러 발생
      var markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize);
      var vm = this;

      // for (let i = 0; i < this.cameraInfoList.length; i++) {
      // 	var data = this.cameraInfoList[i];
      for (let i = 0; i < this.newCameraInfoList.length; i++) {
        var data = this.newCameraInfoList[i];
        var thumnail = this.thumnailArr[i];

        if (this.thumnailArr.length === 0) {
          displayMarker(data, undefined, this.map, i);
        } else {
          var temp = this.thumnailArr.find(
            (ele) => ele.url === this.newCameraInfoList[i].serial
          );

          displayMarker(data, temp.image, this.map, i);
        }
      }

      function displayMarker(data, thumnail, map, i) {
        vm.markers[i] = new kakao.maps.Marker({
          map,
          position: new kakao.maps.LatLng(data.lat, data.lon),
          image: markerImage,
        });

        vm.overlays[i] = new kakao.maps.CustomOverlay({
          yAnchor: 1.4,
          position: vm.markers[i].getPosition(),
        });

        var wrap = document.createElement('div');
        wrap.className = 'info card card-sm';

        var content = document.createElement('div');

        var tempSrc;

        if (thumnail === undefined) {
          tempSrc = require('@/assets/firewatcher/imgLoading.svg');
        } else {
          tempSrc = thumnail;
        }

        content.innerHTML = `
          <img src="${tempSrc}" id="thumbnail" onerror="this.onerror=null; this.src='${require('@/assets/firewatcher/imgLoading.svg')}'" width="170" height="90" style="border-radius: 8px" />
        `;
        wrap.appendChild(content);
        vm.overlays[i].setContent(wrap);
        vm.overlays[i].setMap(map);

        kakao.maps.event.addListener(vm.markers[i], 'click', function () {
          vm.equalPositionCameraList = [];

          vm.cameraInfoList.find((element) => {
            if (element.keyLonLat === data.keyLonLat) {
              vm.equalPositionCameraList.push(element);
            }
          });

          if (vm.equalPositionCameraList.length > 1) {
            vm.modal = new bootstrap.Modal('#equalPositionCameraModal', {
              backdrop: 'static',
              keyboard: false,
            });
            vm.modal.show();
          } else {
            vm.openVideoModal(data);
          }
        });

        // hover event
        kakao.maps.event.addListener(vm.markers[i], 'mouseover', function () {
          vm.markers[i].setZIndex(1);
          vm.overlays[i].setZIndex(1);
        });
        kakao.maps.event.addListener(vm.markers[i], 'mouseout', function () {
          vm.markers[i].setZIndex(0);
          vm.overlays[i].setZIndex(0);
        });
      }
    },
};
</script>
<style scoped>
#map {
  height: 100%;
}
</style>

현상이 재현되지 않아 확인 부탁드립니다.

다른 컴포넌트 2개에서 kakaoMapPage.vue를 자식 컴포넌트로 두고 있습니다

동일한 div요소에 지도를 2번 생성하면 2번 그려지는 건 당연한 일이라서 이렇게 지도를 2개 띄우고 있는지 확인해주세요.
만약 위와 같이 지도를 생성하고 있다면,
각 컴포넌트 표시될 지도 요소를 분리해서 지도를 생성한 다음 다시 확인 부탁드립니다.

그리고 this.map으로 지도의 좌표, 레벨등을 설정하고 있는데요.
아래 답변 참고하셔서 map 속성을 변경할 땐 toRaw(this.map)를 이용해서 지도 원본 객체로 사용해주세요.
https://devtalk.kakao.com/t/map-api/127239/6

1개의 좋아요

알려주신 방법대로 하니까 해결 되었습니다! 매번 도와주셔서 감사합니다 !!

1개의 좋아요