Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #4. 키워드로 장소 검색하기

dayannne·2024년 8월 23일
0

프로젝트 작업이 어느정도 진행된 상황에서 기록을 하려니 힘들구만...하지만 최대한 정리해 보았다.

이번엔 Kakao 지도 API를 사용하게 된 가장 큰 목적, <장소 검색>기능을 구현한 과정을 얘기해 보려 한다.

Kakao 지도 API 샘플 중 '키워드로 장소검색하고 목록으로 표출하기'를 사용할 것이다.

Kakao 지도 API - 키워드로 장소검색하고 목록으로 표출하기

샘플 코드를 보면 목록 + 마커를 함께 생성하고 있는데, 장소 목록을 불러오는 과정까지 얘기하고 마커 생성은 다음 5탄에서 자세히 다뤄볼 예정!

나는 이 키워드 장소 검색 로직을 hook으로 만들어서

  • 위치 이동 시 재검색
  • 카테고리 버튼으로 재검색
  • 새로고침 시 재검색

등의 기능에 재사용해볼 것이다.

구현 순서는 아래와 같이!

  1. 장소 검색 Input 만들기 (SearchForm)
  2. 장소 검색 Hook 만들기(useSearchPlaces)
  3. 검색 결과
  4. 위치 기준 검색하기
  5. 일정 영역 내에서 검색하기

1. 장소 검색 input 만들기

SearchForm.tsx

import React, { useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import Image from 'next/image';

import useSearchPlaces from '@/app/_hooks/useSearchPlaces';

const SearchForm = () => {
  const router = useRouter();
  const { searchPlaces } = useSearchPlaces();

  const [keyword, setKeyword] = useState('');

  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    searchPlaces(keyword);
    router.push(`/place/search/${keyword}`);
  };

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setKeyword(e.currentTarget.value);
  };

  return (
    <div className='border-olive-green border-b p-4'>
      <form
        onSubmit={onSubmit}
        className='border-olive-green box-content flex h-9 justify-between gap-1 rounded-lg border border-solid bg-white py-1 pl-2 pr-3 shadow-md'
      >
        <input
          type='text'
          value={keyword}
          onChange={handleInputChange}
          size={15}
          className='basis-full rounded-full pl-1 outline-none'
          placeholder='장소 검색하기'
        />
        <button
          type='submit'
          className='relative flex w-4 shrink-0 items-center justify-center'
        >
          <Image fill={true} src='/icons/icon-search.svg' alt='검색 아이콘' />
        </button>
      </form>
    </div>
  );
};

export default SearchForm;

2. 장소 검색 Hook 만들기

useSearchPlaces.tsx

  • useSearchPlaces - searchPlaces
    useSearchPlaces훅에서 반환할 장소 검색 함수를 만든다.

    const useSearchPlaces = () => {
      
      // 장소 검색 함수
      const searchPlaces = (keyword: string) => {
        if (keyword !== '') {
          const places = new kakao.maps.services.Places();
          places.keywordSearch(keyword, searchPlacesCB);
        }
      };
    
      //...
    
      return { searchPlaces };
    };
    
    export default useSearchPlaces;
    • 검색 키워드에 해당하는 keyword 매개변수를 받고 빈 문자열인지 확인한다.
    • 카카오 지도 API의 장소 검색 서비스를 제공하는 kakao.maps.services.Places 객체를 생성한다.
    • places.keywordSearch 메서드를 호출해 키워드로 장소를 검색한다. 두번째 인자에서 콜백 함수를 호출하는데, 콜백 함수를 만들어 검색 후의 결과를 처리하면 된다.
  • useSearchPlaces - searchPlaceCB

    const useSearchPlaces = () => {
      
      // 장소 검색 함수
      const searchPlaces = (keyword: string) => {
        if (keyword !== '') {
          const places = new kakao.maps.services.Places();
          places.keywordSearch(keyword, searchPlacesCB);
        }
      };
      
      // keywordSearch 콜백 함수
      const searchPlacesCB = async (
        data: any,
        status: kakao.maps.services.Status,
        pagination: any,
      ) => {
        if (status === kakao.maps.services.Status.OK) {
          console.log(data);
        } else {
          if (status === kakao.maps.services.Status.ZERO_RESULT) {
            return alert('검색 결과가 존재하지 않습니다.');
          } else if (status === kakao.maps.services.Status.ERROR) {
            return alert('검색 결과 중 오류가 발생했습니다.');
          }
        }
      };
    
      return { searchPlaces };
    };
    
    export default useSearchPlaces;
    • data : 검색된 장소의 결과 데이터
    • status : 검색의 성공 여부
      // kakao.maps.services.Status
      export enum Status {
      ERROR = "ERROR", // 서버 응답에 문제가 있는 경우
      OK = "OK", //검색 결과 있음
      ZERO_RESULT = "ZERO_RESULT", // 정상적으로 응답 받았으나 검색 결과는 없음
      }
    • pagination : 페이지네이션 정보로, 검색 결과가 여러 페이지에 걸쳐 있을 때 이를 처리하는 데 사용

3. 검색 결과

여기까지 작성 후 결과를 확인해 보자.
'공원'을 검색했을 때의 결과이다.

최대 15개의 목록 결과가 잘 불러와 진다.

+ 장소 검색 옵션 설정하기

그런데 검색 결과의 주소들을 자세히 보면 여러 지역에서 골고루 결과를 가져오고 있다.

  • 현재 나의 위치, 혹은 지도가 띄워진 중심 좌표를 기준으로 검색하고 싶을 때
  • 지도가 띄워진 영역 내의 검색 결과만 가져오고 싶을때

의 경우에는 위 검색 결과가 아쉬울 수 있다.
이 때 지도 객체에서 중심좌표나, 영역 정보를 가져와 검색 옵션을 지정해 줄 수 있다.

Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #2 지도 띄우기에서 만든 useMap() Context에서 mapData Map 객체(kakao.maps.Map)를 가져와 활용해보자.

예시로 지도가 이미지와 같이 '서호공원'이라는 장소를 zoom해 보여지고 있고 지도의 중심좌표, 영역을 각각 지정해 주었을 때의 결과를 비교해 보자.

  • 지도의 중심 좌표를 기준으로 검색하기
    places.keywordSearch 메서드를 다시 살펴 보자.
    알고 보니 keyword, 콜백 함수, options 이렇게 3개의 인자를 받는다.
    option 중 location을 지정해 주면 된다.

    import { useMap } from '../shared/contexts/Map';
    
    const useSearchPlaces = () => {
      const mapContext = useMap();
      // 장소 검색 함수
      const searchPlaces = (keyword: string) => {
        if (keyword !== '') {
          const { mapData } = mapContext!;
          const location = mapData?.getCenter();
          const places = new kakao.maps.services.Places();
          places.keywordSearch(keyword, searchPlacesCB, {
            location,
          });
        }
      };
    
      // keywordSearch 콜백 함수
      // ...
      
    };
    
    export default useSearchPlaces;
    

    mapData라는 이름으로 저장했던 Map 객체의 내장메서드 getCenter()를 통해 지도의 중심값을 불러와, keywordSearch의 3번째 인자에 넣어주면 끝!
    지도에 보이는 '서호공원'과
    주변 '공원'에 해당하는 검색 결과가 모두 불러와 졌다.

  • 현재 내 위치의 좌표를 기준으로 검색하기

    Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #3 현재 위치 불러오기에서 만들어보았던 useGeolocation훅을 여기에 활용할 수 있다.

    import { useState, useEffect } from 'react';
    
    
     const useGeolocation = () => {
       const [location, setLocation] = useState<Latlng | null>(null); // 현재 위치를 저장할 상태
    
    
       return { location };
     };
    
     export default useGeolocation;
    // import { useMap } from '../shared/contexts/Map';
    import useGeolocation from './useGeolocation';
    
    const useSearchPlaces = () => {
      // const mapContext = useMap();
      const { location: myLocation } = useGeolocation();
    
      // 장소 검색 함수
      const searchPlaces = (keyword: string) => {
        if (keyword !== '') {
          // const { mapData } = mapContext!;
          // const location = mapData?.getCenter();
          const places = new kakao.maps.services.Places();
          places.keywordSearch(keyword, searchPlacesCB, {
            location: new kakao.maps.LatLng(
              myLocation?.latitude as number,
              myLocation?.longitude as number,
            ),
          });
        }
      };
    
      // keywordSearch 콜백 함수
      // ...
    
      return { searchPlaces };
    };
    
    export default useSearchPlaces;
    

    '현재 내 위치에서 재검색' 과 같은 기능을 넣고 싶다면 이 로직을 활용하면 된다.

  • 지도의 영역을 기준으로 검색하기
    영역은 places.keywordSearch메서드의 option 중 bounds을 지정해 주면 된다.

    import { useMap } from '../shared/contexts/Map';
    
    const useSearchPlaces = () => {
        const mapContext = useMap();
        // 장소 검색 함수
        const searchPlaces = (keyword: string) => {
          if (keyword !== '') {
            const { mapData } = mapContext!;
            const bounds = mapData?.getBounds();
            // const location = mapData?.getCenter();
            const places = new kakao.maps.services.Places();
            places.keywordSearch(keyword, searchPlacesCB, {
              bounds,
            });
          }
        };
    
      // keywordSearch 콜백 함수
      // ...
    
      return { searchPlaces };
    };
    
    export default useSearchPlaces;
    

    지도 내 표시된 관련 장소만 불러오는 것을 확인할 수 있다.

profile
☁️

0개의 댓글