카카오맵을 구현해보려고 하는데,  아래의 사진과 같은 에러가 뜨면서 렌더링이 되지 않았다!

콘솔에 찍힌 내용들을 차근 차근 읽어보고 검색해보아도, 별다른 해결책을 찾지 못했다.

 

코드는 아래와 같다.

import React, { useEffect, useState } from "react";

const KakaoMap = () => {
  const { kakao } = window;
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    if (!kakao.maps) {
      return;
    }
    // 카카오 지도 객체 생성 예제
    const container = document.getElementById("map"); // 지도를 표시할 div
    const options = {
      center: new kakao.maps.LatLng(37.5665, 126.978), // 초기 위치
      level: 3, // 초기 확대 레벨
    };

    const map = new kakao.maps.Map(container, options);

    // // 지도에 마커 추가
    const marker = new kakao.maps.Marker({
      position: new kakao.maps.LatLng(37.5665, 126.978),
    });
    marker.setMap(map);

    setIsLoaded(true);
  }, [kakao.maps]);

  return (
    <div>
      {isLoaded ? (
        <div id="map" style={{ width: "100%", height: "400px" }}></div>
      ) : (
        <p>Loading map...</p>
      )}
    </div>
  );
};

export default KakaoMap;

 

여기서 문제점은 무엇일까요?

  1. 전역 객체인 kakao가 제대로 읽어들여지지 않았다.
  2. useEffect의 어떤 원리 때문에, 시간 차가 발생하여 kakao는 결국 읽어졌으나 kakao.maps가 state가 아니므로 리렌더링이 유발되지 않는다.
  3. useEffect의 어떤 원리 때문에, 렌더링 되고 난 후 useEffect의 콜백 함수 실행 과정에서 문제가 생겼다.

정답은?

.

.

.

.

.

.

.

.

.

.

.

3번! 

 

사실 1,2,3번 모두 문제의 원인 후보에 들만한 사유들입니다. 

따라서 전부 콘솔로그로 찍어보고, 주석화도 해보면서 디버깅을 해봤어요!

 

1. 전역 객체로 선언된 kakao의 경우 잘 읽혀지고 있었고(어느 시점에서든! → 사실 <script> 태그를 통해 <head>에 먼저 가져오므로 당연히 잘 읽혀집니다. 순서가 꼬이는 일은 없어요! - 스크립트가 실행이 안될 수도는 있어도..)

 

2. useEffect의 dependancy로 state와 같이 변하는 값이 아닌 전역 객체의 값을 넣어둔 것 또한 리렌더링을 발생시키지 않기에, 굉장히 이상한 코드인 것은 맞습니다.

 

그러나 3번이 원인인 것은.. 

 return (
    <div>
      {isLoaded ? (
        <div id="map" style={{ width: "100%", height: "400px" }}></div>
      ) : (
        <p>Loading map...</p>
      )}
    </div>
  );
};

 

바로 이 부분 때문입니다 ! isLoaded가 false이면 <p> 태그가 렌더링되는데 그렇게 된다면,

useEffect(() => {
    if (!kakao.maps) {
      return;
    }
    // 카카오 지도 객체 생성 예제
    const container = document.getElementById("map"); // 지도를 표시할 div
    const options = {
      center: new kakao.maps.LatLng(37.5665, 126.978), // 초기 위치
      level: 3, // 초기 확대 레벨
    };

    const map = new kakao.maps.Map(container, options);

    // // 지도에 마커 추가
    const marker = new kakao.maps.Marker({
      position: new kakao.maps.LatLng(37.5665, 126.978),
    });
    marker.setMap(map);

    setIsLoaded(true);
  }, [kakao.maps]);

 

이 useEffect 함수의 콜백 함수 내에서 

const container = document.getElementById("map"); // 지도를 표시할 div
    const options = {
      center: new kakao.maps.LatLng(37.5665, 126.978), // 초기 위치
      level: 3, // 초기 확대 레벨
    };

const map = new kakao.maps.Map(container, options);

 

여기! getElementById 해오는 부분이 null이 되어버리고, 그럼 kakao.maps.Map의 첫번째 인자로 null을 넘겨버리니까 타입 에러가 나면서 렌더링이 안되는겁니다! 이해 되셨나요? 

 

 

원인을 찾았으니, 쓸데없는 로직들 제외하면서 코드를 더욱 깔끔하게 만들어봅시다 ~!

import React, { useEffect, useState } from "react";

const KakaoMap = () => {
  const { kakao } = window;
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    setIsLoaded(true); //다음 번에는 바로 로딩이 되도록 해당 위치에 정의
    if (!isLoaded) {
      //이래야 getElementById가 정상적으로 작동함
      return;
    }
    // 카카오 지도 객체 생성 예제
    const container = document.getElementById("map"); // 지도를 표시할 div
    const options = {
      center: new kakao.maps.LatLng(37.5665, 126.978), // 초기 위치
      level: 3, // 초기 확대 레벨
    };

    const map = new kakao.maps.Map(container, options);

    // // 지도에 마커 추가
    const marker = new kakao.maps.Marker({
      position: new kakao.maps.LatLng(37.5665, 126.978),
    });
    marker.setMap(map);
  }, [isLoaded]);

  return (
    <div>
      {isLoaded ? (
        <div id="map" style={{ width: "100%", height: "400px" }}></div>
      ) : (
        <p>Loading map...</p>
      )}
    </div>
  );
};

export default KakaoMap;
COMMENT