Nextjs13 / useEffect / old CustomOverlay 안 지워지는 이슈

안녕하세요.

nextjs13을 이용해서 useEffect로 맵과 커스텀오버레이를 불러와 쓰고 있습니다.
select tag 옵션에 따라서 data가 갱신되면 맵에 표출될 커스텀오버레이 갯수도 바뀌도록 했는데요,
문제는 옛 커스텀오버레이가 맵에서 사라리지 않은 채 새로운 커스텀오버레이가 중첩된다는 것입니다.

let pins = [];
pin.setMap(kakaoMap);
pins.push(pin);
console.log(pins);

추이를 보기 위해 위와 같이 pins 정보를 콘솔로 찍어보면
빈 어레이가 되었다가 변경된 커스텀오버레이 갯수만큼만 나타나는데
왜 옛날 커스텀오버레이는 안 사라지는 걸까요…? ㅠㅠ

참고하실 수 있도록 아래에 코드를 공유드립니다

const [kakaoMap, setKakaoMap] = useState(null);
const [gu, setGu] = useState(0);
const [type, setType] = useState(0);
const [installYear, setInstallYear] = useState(0);
useEffect(() => {
    const kakaoMapScript = document.createElement("script");
    kakaoMapScript.async = false;
    kakaoMapScript.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_API_KEY_KAKAO_MAP}&autoload=false`;
    document.head.appendChild(kakaoMapScript);

    const onLoadKakaoAPI = () => {
      window.kakao.maps.load(() => {
        const container = document.getElementById("map");
        const options = {
          center: new window.kakao.maps.LatLng(
            35.824078559135316,
            127.14814325735101
          ),
          level: 8,
        };

        const map = new window.kakao.maps.Map(container, options);
        /* map.setZoomable(false); */
        setKakaoMap(map);
      });
    };
    kakaoMapScript.addEventListener("load", onLoadKakaoAPI);
  }, []);

  useEffect(() => {
    if (kakaoMap == null) {
      return;
    }
    const width = 10;
    data &&
      data.solar.map((solar) => {
        const pin = new window.kakao.maps.CustomOverlay({
          content: `
                <div>
                  <div style="
                    position: relative;
                    left: -50%;
                    transform: translateX(50%);
                    top: 50%;
                    width: ${width}px;
                    height: ${width}px;
                    border-radius: ${width / 2}px;
                    background-color: ${
                      solar.type == 1
                        ? "#e6233a99"
                        : solar.type == 2
                        ? "#d7770799"
                        : "#2680eb99"
                    };
                    border: solid ${width / 10}px #ffffff;
                    box-shadow: 1px 1px 3px 2px #00000020;
                  "></div>
                </div>
              `,
          position: new window.kakao.maps.LatLng(
            solar.latitude,
            solar.longitude
          ),
          xAnchor: 0.5,
          yAnchor: 0.5,
          zIndex: 3,
        });
        pin.setMap(kakaoMap);
        pins.push(pin);
      });
      console.log(pins);
  }, [data]);

※ map이 load되는 useEffect에 gu, type, installYear(selectbox를 통해 변하는 값들)을 넣어주면 얼추 원하는 대로 되는 것 같다가도 셀렉트박스를 빨리 변경하면 도루묵이 됨

코드에서 이전에 생성한 오버레이를 삭제하는 부분이 없습니다.
data가 변경될 때 pins에 담긴 오버레이를 overlay.setMap(null);을 이용해서 지도에서 삭제해주세요.
https://apis.map.kakao.com/web/documentation/#CustomOverlay_setMap

감사합니다 레아님!
아래와 같이 해결했습니다.
같은 문제로 고민하시는 분들도 있을 것 같아 아래에 공유해놓겠습니다.

… 인줄 알았는데,
셀렉트 input을 빨리 바꾸면 다시 겹치는 현상 발견했습니다.

  /* STEP1. pinArray State 생성 */
  const [pinArray, setPinArray] = useState([]);
  useEffect(() => {
    if (kakaoMap == null) {
      return;
    }
    /* STEP2. 핀 정보들을 임시로 담을 어레이 생성 */
    let pins = [] as any;
    const width = 15;
    data &&
      data.solar.map((solar) => {
        const pin = new window.kakao.maps.CustomOverlay({
          map: kakaoMap,
          content: ``,
          position: new window.kakao.maps.LatLng(
            solar.latitude,
            solar.longitude
          ),
          xAnchor: 0.5,
          yAnchor: 0.5,
          zIndex: 3,
        });
        /* STEP3. 핀 정보들을 임시 어레이에 담기 */
        pins.push(pin);
      });
      /* STEP4. 임시 어레이를 이용해 pinArray State 업데이트 */
      setPinArray(pins);
  }, [data]);

  /* STEP5. useEffect 신규 생성 : 셀렉트 State 변동 시, 맵에서 기존 핀 지우기 먼저 발동 */
  useEffect(() => {
    if (kakaoMap == null) {
      return;
    }
    pinArray.map((pin) => {
      pin.setMap(null);
    });
  }, [gu, type, installYear])

셀렉트 변경 동작이 빨라 핀이 중첩되는 문제 해결
: 셀렉트마다 disabled 속성을 isLoading 여부에 따라 활성화시켜 빠른 변동을 막았습니다.

const { data, isLoading } = useSWR<SolarResponse>(url, fetcher);
<select disabled={isLoading ? true : false}>...</select>

이제 진짜 케이스 종결해도 될 것 같습니다.

1개의 좋아요