Geocoder 안불러와짐, 좌표로 행정동 주소 정보를 요청하는 기능 자스로 못 쓰나요?

디벨로퍼스 앱 ID 829278
JS,React,TS 사용, 어플리케이션 내의 날씨 모달창을 작업중입니다
기상청 날씨 API로 현재 위치를 따면 nx, ny 이런식으로 나와서
그 위치를 토대로 역지오코딩을 통해 00시 00구 00동으로 현재 위치를 텍스트로 보이게 하려 합니다

https://apis.map.kakao.com/web/sample/coord2addr/
이 문서를 보고 구현해보았는데

// WeatherModal.tsx
import React, { useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTimes, faMapMarkerAlt } from "@fortawesome/free-solid-svg-icons";
import { useWeatherData } from "./useWeatherData";
import WeatherDisplay from "./WeatherDisplay";
import { useReverseGeocoding } from "./useReverseGeocoding";
import { useLocation } from "./useLocation";

interface WeatherModalProps {
  isOpen: boolean;
  onClose: () => void;
}

const WeatherModal: React.FC<WeatherModalProps> = ({ isOpen, onClose }) => {
  const {
    location,
    isLoading: isLocationLoading,
    error: locationError,
  } = useLocation(isOpen);
  const {
    weatherData,
    isLoading: isWeatherLoading,
    error: weatherError,
  } = useWeatherData(isOpen);

  const address = useReverseGeocoding(
    location?.latitude ?? 0,
    location?.longitude ?? 0
  );

  useEffect(() => {
    console.log("Location:", location);
    console.log("Address:", address);
  }, [location, address]);

  if (!isOpen) return null;

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
      <div className="bg-white p-6 rounded-lg max-w-sm w-full">
        <div className="flex justify-between items-center">
          <h2 className="text-xl font-bold">현재 날씨</h2>
          <button
            onClick={onClose}
            className="text-gray-500 hover:text-gray-700"
          >
            <FontAwesomeIcon icon={faTimes} />
          </button>
        </div>
        <div className="mt-2">
          <FontAwesomeIcon icon={faMapMarkerAlt} className="mr-2" />
          <span>
            {isLocationLoading
              ? "위치 불러오는 중..."
              : locationError
              ? locationError
              : address || "주소를 가져올 수 없습니다"}
          </span>
        </div>
        <WeatherDisplay
          weatherData={weatherData}
          isLoading={isWeatherLoading}
          error={weatherError}
        />
      </div>
    </div>
  );
};

export default WeatherModal;

// useReverseGeocoding.ts
import { useState, useEffect } from "react";
import { loadKakaoMapScript } from "../../utils/kakaoMapLoader";

declare global {
  interface Window {
    kakao: any;
  }
}

export const useReverseGeocoding = (latitude: number, longitude: number) => {
  const [address, setAddress] = useState<string>("");

  useEffect(() => {
    if (latitude && longitude) {
      loadKakaoMapScript()
        .then(() => {
          if (
            !window.kakao ||
            !window.kakao.maps ||
            !window.kakao.maps.services
          ) {
            console.error("Kakao Maps API is not fully loaded");
            setAddress("주소를 가져올 수 없습니다");
          }
          const geocoder = new window.kakao.maps.services.Geocoder();
          const coords = new window.kakao.maps.LatLng(latitude, longitude);

          geocoder.coord2RegionCode(
            coords.getLng(),
            coords.getLat(),
            (result: any, status: any) => {
              if (status === window.kakao.maps.services.Status.OK) {
                for (let i = 0; i < result.length; i++) {
                  if (result[i].region_type === "H") {
                    setAddress(
                      `${result[i].region_1depth_name} ${result[i].region_2depth_name} ${result[i].region_3depth_name}`
                    );
                    break;
                  }
                }
              } else {
                setAddress("주소를 가져올 수 없습니다");
              }
            }
          );
        })
        .catch((error) => {
          console.error("Error initializing Kakao Maps:", error);
          setAddress("주소를 가져올 수 없습니다");
        });
    }
  }, [latitude, longitude]);

  return address;
};

// kakaoMapLoder.ts
declare const kakao: any;

let isLoading = false;
let isLoaded = false;

export const loadKakaoMapScript = (): Promise<void> => {
  return new Promise((resolve, reject) => {
    if (isLoaded) {
      resolve();
      return;
    }

    if (isLoading) {
      const checkLoaded = setInterval(() => {
        if (isLoaded) {
          clearInterval(checkLoaded);
          resolve();
        }
      }, 100);
      return;
    }

    isLoading = true;

    const script = document.createElement("script");
    script.type = "text/javascript";
    script.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.REACT_APP_KAKAO_API}&libraries=services`;
    script.onload = () => {
      window.kakao.maps.load(() => {
        isLoaded = true;
        isLoading = false;
        console.log("Kakao Maps script loaded and initialized successfully");
        resolve();
      });
    };
    script.onerror = (error) => {
      isLoading = false;
      console.error("Error loading Kakao Maps script:", error);
      reject(
        new Error(
          `Failed to load Kakao Maps script. Please check your API key (${process.env.REACT_APP_KAKAO_API}) and network connection.`
        )
      );
    };

    document.head.appendChild(script);
  });
};

const displayCurrentLocation = (latitude: number, longitude: number) => {
  loadKakaoMapScript() // Kakao 지도 스크립트가 완전히 로드된 후 실행
    .then(() => {
      const geocoder = new window.kakao.maps.services.Geocoder();
      const coords = new window.kakao.maps.LatLng(latitude, longitude);

      // 좌표로 행정동 주소 정보를 요청
      geocoder.coord2RegionCode(
        coords.getLng(),
        coords.getLat(),
        (result: any, status: any) => {
          if (status === window.kakao.maps.services.Status.OK) {
            for (let i = 0; i < result.length; i++) {
              // 행정동(region_type === 'H') 주소만 가져옴
              if (result[i].region_type === "H") {
                const currentLocation = `${result[i].region_1depth_name} ${result[i].region_2depth_name} ${result[i].region_3depth_name}`;
                document.getElementById("current-location")!.innerText =
                  currentLocation;
                break;
              }
            }
          } else {
            console.error("Failed to fetch address");
          }
        }
      );
    })
    .catch((error) => {
      console.error("Error loading Kakao Maps script:", error);
    });
};

const getCurrentLocation = () => {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        displayCurrentLocation(latitude, longitude);
      },
      (error) => {
        console.error("Error fetching current location:", error);
      }
    );
  } else {
    console.error("Geolocation is not supported by this browser.");
  }
};

loadKakaoMapScript()
  .then(() => {
    getCurrentLocation();
  })
  .catch((error) => {
    console.error("Kakao Maps script failed to load:", error);
  });

이렇게 했을때 대체로 이 오류가 나타납니다
API 키와 플랫폼 도메인은 확인하였습니다

22Third-party cookie will be blocked in future Chrome versions as part of Privacy Sandbox.
kakaoMapLoader.ts:64 Error loading Kakao Maps script: TypeError: Cannot read properties of undefined (reading 'Geocoder')
    at eval (kakaoMapLoader.ts:45:51)

지오코더를 계속 불러오질 못해서 찾아보다
https://developers.kakao.com/docs/latest/ko/getting-started/scope-of-support#local
여기 API 지원 범위를 확인해 보니 이 기능은 REST API로만 제공하는것 같은데
지도 API 사이트에는 또 자바스크립트로 구현이 되어있으니 제가 잘못 코드를 짜고 있는건지,
지원이 안되는걸 계속 하고 있는건지 모르겠습니다 도와주십시오

스크립트(sdk 및 라이브러리 파일)가 모두 다운로드되기 전에 Geocoder 객체를 생성하려고 해서 생긴 오류로 보입니다.
kakao.maps.load API를 사용할 경우 스크립트 파라미터에 autoload=false로 설정해야 합니다.
https://apis.map.kakao.com/web/documentation/#load_load
해당 파라미터를 추가해서 다시 확인해주세요.

그리고 로드된 시점과 객체 생성 시점이 다르면 오류가 날 수 있기 때문에
스크립트가 모두 로드된 이후에 객체를 생성하고 있는지 시점 확인도 필요합니다.
이때 kakao.maps 객체에 services.Geocoder 객체도 포함되어 있는지 확인해주세요.

1개의 좋아요

사실 autoload=false로 설정하고 나서 또 다른 오류가 생기고 점점 미궁속으로 빠지는 느낌이라 코드를 갈아 엎어보았습니다

kakaoMapLoder.ts 파일을 삭제하고 useReverseGeocoding.ts 파일의 코드를 간소화 해보았는데 놀랍게도 동작합니다

기능이 안되는건가 보다~ 포기할 뻔했는데 어쨌든 답변보고 다시 시도해서 성공했습니다 감사합니다!

2개의 좋아요

@Goose

참고하시라고 하나 적어둡니다.

지도 JS SDK에서 제공하는 Geocoder 기능의 경우는

https://developers.kakao.com/docs/latest/ko/local/common#api-list

여기서 제공하는 REST API와 동일합니다.
리턴되는 값도 동일하나, 해당 값들을 조금더 이용하기 쉽도록 특정 객체로 랩핑하여 내리는 것만 다르니 참고해 주시기 바랍니다.

2개의 좋아요