본문 바로가기
프론트엔드 관련/프로젝트

[React] 카카오맵 도로명 주소를 통한 위치 정보 검색(존재하지 않는 위치정보 포함)

by ash9river 2024. 12. 11.

기술 환경

React, Vite, Typescript, react-kakao-maps-sdk, Maps Javascript API(Google)

카카오맵 도로명 주소를 통한 위치 정보 검색

세간에 나와있는 도로명 주소를 통한 위치 정보 검색은 다음과 같다.

물론 매개변수 타입은 따로 정의되어있지 않아, 내가 찾아보면서 따로 추가하였다.

function getPositionFromAddress(searchAddress: string) {
  const geocoder = new kakao.maps.services.Geocoder();
  const callback = function (
    result: Array<{
      address_name: string;
      address_type: 'REGION' | 'ROAD' | 'REGION_ADDR' | 'ROAD_ADDR';
      x: string; //longitude
      y: string; //latitude
      address: kakao.maps.services.Address;
      road_address: kakao.maps.services.RoadAaddress;
    }>,
    status: kakao.maps.services.Status,
    pagination: kakao.maps.Pagination,
  ) {
    if (status === kakao.maps.services.Status.OK) {
      console.log(result[0].x);
    }
  };
  return geocoder.addressSearch(searchAddress, callback);
}

export default getPositionFromAddress;

 

도로명 주소로 위치정보 찾기는 REST API 키가 필요하기에 react-kakao-maps-sdk를 사용하는 사람은 밑의 코드를 활용하면 된다.

export async function getPositionFromAddressV2(
  address: string,
  signal?: AbortSignal,
) {
  const url = `https://dapi.kakao.com/v2/local/search/address.json?query=${address}&analyze_type=similar`;
  try {
    const response = await axios.get(url, {
      headers: {
        Authorization: `KakaoAK ${KAKAO_REST_API_KEY}`,
      },
      signal,
    });
    console.log(response);

    if (response.data.meta.total_count === 1) {
      const position = {
        lat: response.data.documents[0].y,
        lng: response.data.documents[0].x,
      };
      return position;
    } else {
      return null;
    }
  } catch (err: unknown) {
    if (!(err instanceof CanceledError)) console.log(err);
  }
}

 

함수가 잘 작동된다.

그런데 막상 다음과 같은 주소를 검색하면, 정보가 존재하지 않다고 나온다.

서울특별시 종로구 난계로29가길 12

 

위치정보를 무조건 활용해야 하는 경우, 구글맵을 활용해보자!

 

카카오맵에서 위치 정보를 반환하지 못하는 경우에만 구글맵을 활용하면 된다.

export async function getPositionFromAddressV2(
  address: string,
  signal?: AbortSignal,
) {
  const url = `https://dapi.kakao.com/v2/local/search/address.json?query=${address}&analyze_type=similar`;
  try {
    const response = await axios.get(url, {
      headers: {
        Authorization: `KakaoAK ${KAKAO_REST_API_KEY}`,
      },
      signal,
    });
    console.log(response);

    if (response.data.meta.total_count === 1) {
      const position = {
        lat: response.data.documents[0].y,
        lng: response.data.documents[0].x,
      };
      return position;
    } else {
      const googleResponse = await getPositionFromAddressWithGoogle(address);
      const position = {
        lat: googleResponse?.lat.toString(),
        lng: googleResponse?.lng.toString(),
      };
      return position;
    }
  } catch (err: unknown) {
    if (!(err instanceof CanceledError)) console.log(err);
  }
async function getPositionFromAddressWithGoogle(
  address: string,
): Promise<{ lat: number; lng: number } | null> {
  const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
    address,
  )}&key=${VITE_GOOGLE_MAP_API_KEY}`;

  try {
    const response = await axios.get(url);

    if (response.data.status === 'OK') {
      const location = response.data.results[0]?.geometry?.location;
      if (location) {
        return {
          lat: location.lat,
          lng: location.lng,
        };
      }
    } else {
      // 에러 핸들링
    }
  } catch (err: unknown) {
    // 에러 핸들링
  }

  return null;
}

 

 

리액트 쿼리와의 결합

 

React-Query(Tanstack-Query)를 사용하여 주소를 불러내면 로직을 좀 더 효율적으로 만들어낼 수 있다.

 

현재 로직은 이렇다.

 

1. 주소를 검색한다

2-1. 카카오맵에서 주소가 존재하고, 그 주소를 이용하여 위치 정보를 반환한다.

2-2. 카카오맵에서 주소가 존재하지 않아서, 구글맵 API를 호출하고 그 결과를 반환한다.

3. 반환된 위치 정보를 이용한다.

 

리액트 쿼리(탄스택 쿼리)를 사용하면, 이전에 호출한 결과값이면 API를 호출하지 않고 캐싱된 결과값을 반환한다.

 

import { useQuery } from '@tanstack/react-query';
import { getPositionFromAddressV2 } from '../Utils/getPositionFromAddress';

function useAddressQuery(address: string) {
  return useQuery({
    queryKey: ['address', address],
    queryFn: ({ signal }) => getPositionFromAddressV2(address, signal),
  });
}

export default useAddressQuery;

 

상당히 간단하다.

 

이제 아까의 도로명 주소를 검색하면 다음과 같은 위치 정보 결과를 성공적으로 반환할 수 있다.

 

리액트 쿼리를 활용하여, 동일한 요청에 대해서는 이전에 호출된 응답을 캐싱한 데이터로 반환하였고, 이를 통해 비동기 처리 효율을 향상시킬 수 있다.

 

 

끝.