Front-End/React

[React] 카카오맵(3) 맵 마커 비동기 출력 문제 해결하기

Olivia Kim 2023. 6. 14. 23:59
반응형

 

오류 내역

페이지가 로드될 때 카카오맵의 마커가 바로 생성되지 않고 어떤 추가 요청을 해야 나머지 마커를 불러오는 상황이 발생했다.

 

 

기존 코드 전체 보기

더보기
import React, { useState, useEffect } from 'react';
import { Map, MapMarker, CustomOverlayMap } from 'react-kakao-maps-sdk';
import { styled } from 'styled-components';
import { BiChevronRight } from 'react-icons/bi';
import { useNavigate } from 'react-router-dom';

// window.kakao 객체를 가져옴
const { kakao } = window;

const KakaoMap = ({ coords, storelist }) => {
  const [markers, setMarkers] = useState([]);

  // * 상세 페이지로 이동
  const navigate = useNavigate();
  const onOverlayClickHandler = (storeId) => navigate(`/StoreDetail/${storeId}`);

  // * 주소-좌표 변환 객체를 생성
  const geocoder = new kakao.maps.services.Geocoder();

  // * storelist 주소를 지도에 마킹할 수 있도록 x, y값 변환
  useEffect(() => {
    const addMarkers = [];

    storelist.forEach((item) => {
      geocoder.addressSearch(item.address, (result, status) => {
        if (status === kakao.maps.services.Status.OK && result[0]) {
          addMarkers.push({
            position: {
              lat: result[0].address.y,
              lng: result[0].address.x,
            },
            content: item.store,
            id: item.store_id,
            isOverlayVisible: false,
          });
        }
        setMarkers(addMarkers);
      });
    });
  }, [storelist]);

  // * 업장 아이콘 클릭 시 overlay visible true/false
  const onMarkerClickHandler = (markerId) => {
    setMarkers((prevMarkers) =>
      prevMarkers.map((marker) =>
        marker.id === markerId ? { ...marker, isOverlayVisible: !marker.isOverlayVisible } : marker,
      ),
    );
  };

  return (
    <MapSection>
      {/* 지도를 표시할 Container */}
      <Map
        center={{
          lat: coords.lat,
          lng: coords.lon,
        }}
        style={{
          width: '100%',
          height: '94%',
        }}
        level={5}
      >
        {markers &&
          markers.map((marker) => (
            <React.Fragment key={`marker_${marker.id}`}>
              <MapMarker
                position={marker.position}
                image={{
                  src: 'https://cdn-icons-png.flaticon.com/512/2722/2722538.png',
                  size: {
                    width: 24,
                    height: 35,
                  },
                  options: {
                    offset: {
                      x: 15,
                      y: -7,
                    },
                  },
                }}
                onClick={() => onMarkerClickHandler(marker.id)}
              />
              {marker.isOverlayVisible && (
                <CustomOverlayMap
                  value={marker.isOverlayVisible}
                  position={marker.position}
                  yAnchor={1}
                  onClick={() => onMarkerClickHandler(marker.id)}
                >
                  <OverlayDiv onClick={() => onOverlayClickHandler(marker.id)}>
                    {marker.content}
                    <BiChevronRight />
                  </OverlayDiv>
                </CustomOverlayMap>
              )}
            </React.Fragment>
          ))}
      </Map>
    </MapSection>
  );
};

export default KakaoMap;

 

 

 


오류가 발생한 원인

geocoder.addressSearch가 비동기도 동작하기 때문에 마커를 추가하는 반복문이 비동기적으로 실행되기 때문이었다!

 

 

 


오류 해결 방법

모든 주소의 좌표 변환 작업이 완료된 후 마커를 한꺼번에 추가할 수 있도록 하고, 이를 위해 비동기 작업들을 Promise와 Promise.all을 사용해 처리했다.

 

 

  // * 주소-좌표 변환 객체를 생성
  const geocoder = new kakao.maps.services.Geocoder();

  // * storelist 주소를 지도에 마킹할 수 있도록 x, y값 변환
  useEffect(() => {
  	// async 함수를 정의해 storelist에 있는 각 항목의 주소를 변환해 마커 데이터를 가져온다.
    const fetchMarkerData = async () => {
      // 마커 데이터를 저장하기 위한 빈 배열 변수
      const addMarkers = [];

	  // 해당 배열은 변환된 각 주소에 대한 Promise 객체들을 저장한다.
      const markerPromises = storelist.map((item) => {
        // 주소 변환 작업에 대해 새로운 Promise 객체들을 생성한다.
        // 이 Promise 객체는 addressSearch 메서드를 호출해 주소를 변환하고,
        // 변환된 결과를 처리하는 비동기 작업을 수행한다.
        return new Promise((resolve) => {
          geocoder.addressSearch(item.address, (result, status) => {
            if (status === kakao.maps.services.Status.OK && result[0]) {
              addMarkers.push({
                position: {
                  lat: result[0].address.y,
                  lng: result[0].address.x,
                },
                content: item.store,
                id: item.store_id,
                isOverlayVisible: false,
              });
            }
            // 주소 변환 작업이 완료되면 Promise 객체를 reslove하여 완료됐음을 알린다.
            resolve();
          });
        });
      });

	  // Promise.all 메서드를 사용해 markerPromises 배열 안의
      // 모든 Promise 객체들이 완료될 때까지 대기한다.
      await Promise.all(markerPromises);
      setMarkers(addMarkers);
    };

    fetchMarkerData();
  }, [storelist]);

 

 

 


[해당 코드 전체 보기]

https://github.com/dawhisky/dawhisky-FE/blob/develop/src/pages/KakaoMap.jsx

 

 

 

반응형