React에서 더 쉽게 kakao-maps-sdk 사용하기 - 검색기능을 개선해보자

gigi·2023년 4월 27일
1

저번 내용에 이어서 검색기능을 개선해 보았다.

일단 내가 원했던건 검색을 통해 특정위치로 이동해 음식점을 검색해보고난 후 지도를 확대해서 드래그 해가며 주변에 있는 다른 음식점 정보도 제공 받고싶다 였다.

먼저 kakao-maps-sdk 설명에서 카테고리 검색에 관한 내용을 못찾았었기 때문에 검색어로 찾는 방향으로 진행을 했었다. 맵을 드래그 했을때는 맵 중앙의 좌표를 주소로 변환하고 재검색하는 방향을 생각해봤다.

주소검색으로도 해당 주소 주변 음식점이 잘 나오길래 써먹을 수 있겠구나했다. 먼저 이전 코드를 보면

// dragEnd 이벤트로인한 중앙 좌표(position)의 상태가 업데이트 될때마다 실행
useEffect(() => {
    var geocoder = new kakao.maps.services.Geocoder();
    geocoder.coord2Address(position.lng, position.lat, displayCenterInfo);
  }, [position]);


// 좌표를 이용해 실제 주소를 받는 콜백함수.
  function displayCenterInfo(result, status) {
    if (status === kakao.maps.services.Status.OK) {
      let detailAddr = !!result[0].road_address
        ? result[0].road_address.address_name
        : "";
      detailAddr += result[0].address.address_name;
      setKeyword(detailAddr);
    }
  }

위와같은 방법으로 좌표를 주소로 변환해 검색 keyword를 업데이트 해준다. 만약 "기흥역"에 대한 검색을 하면 "기흥역 음식점" 에 대한 결과를 보여주고 맵을 드래그하면 "경기 용인시 기흥구 상갈동 123-22 음식점"에 대한 검색을 하게 된다.

한식이나 일식, 중식 처럼 카테고리를 바꾼다면?

  • "경기 용인시 기흥구 상갈동 123-22 일식"
  • "경기 용인시 기흥구 상갈동 123-22 한식"

이런식으로 검색을 하게 된다.
전혀 안되는것은 아니지만 검색 디폴트가 정확도순 이기 때문에 결과가 없거나 드래그로 맵 이동을 했지만 결과가 똑같은 경우도 많이 발생한다.

무엇보다 세 경우 모두 검색마다 지도 범위를 재설정하게 되는데 검색 결과의 영향인지 맵의 확대 Level이 들쑥날쑥해서 보기 너무 불편했다.
특히나 좀 더 잦은 검색을 하게 되는 드래그, 메뉴탭의 경우 조금 움직였는데 갑자기 지도가 확대됐다가 축소됐다가 하면서 정신이 없었고, 그러다보니 몇번 드래그하고 메뉴탭을 눌러보면 내가 처음 보고자 했던 위치에서 벗어난 장소를 보고있는 경우도 있다.


수정을 해보자

기존 keywordSearch만을 사용했던거에서 categorySearch 추가하고 하나의 함수로 묶었다. (한식, 중식, 양식 같은 type으로도 검색해야해서 총 3가지경우의 검색을 해야하는데 공통부분이 많아서 하나로 묶고 if문으로 분기했다)

Search

먼저 getSearchResult를 호출할때 type(상수)을 인자로 받아 if문을 나눠 검색의 타입과 옵션을 따로 지정했다. 공통옵션은 아래와같다.

  • 음식점("FD6") 만을 검색하는 category_group_code
  • 중심 좌표. 특정 지역을 기준으로 검색하는 location
// search
const getSearchResult = (type) => {
    if (!map) return;
  // 공통 옵션
    const options = {
      category_group_code: "FD6",
      location: new kakao.maps.LatLng(
        map.getCenter().getLat(),
        map.getCenter().getLng()
      ),
    };
  // category 검색(drag 검색에 사용)
    if (type === CATEGORY) {
      isUseBounds = false;
      const copyOptions = {
        ...options,
        sort: kakao.maps.services.SortBy.DISTANCE,
      };
      ps.categorySearch("FD6", kakaoMapCallback, copyOptions);
    }
  // selcet 검색 (메뉴탭 클릭 시 메뉴 종류를 keyword로 검색을 한다)
  	else if (type === SELECT) {
      isUseBounds = false;
      ps.keywordSearch(selectType, kakaoMapCallback, options);
    } 
  	// keyword 검색 (검색어 입력시 해당 위치를 keyword로 검색한다)
  	else {
      isUseBounds = true;
      ps.keywordSearch(searchInputValue, kakaoMapCallback, options);
    }
  };

category

  • ps.categorySearch를 이용한다.
  • isUseBounds 변수를 이용해 bounds 할지 안할지 정해주었다.(bounds=지도범위재설정 / 이렇게 하지 않고 options 이용해서도 할 수 있을거라 보는데 축소, 확대, drag 등 화면 영역의 좌표가 계속 변하는 상황에 어떻게 쓸 수 있을지 감이 안와서 아예 bounds를 막았다)
  • 확대해서 조금씩 주변을 검색한다는 가정하에 마커가 map 화면 밖으로 벗어나지 않게 sort : distance 옵션을 추가 해주었다(거리순 검색).

select

  • ps.keywordSearch를 이용한다.
  • CATEGORY와 마찬가지로 bounds 하지 않는다.
  • ps.keywordSearch의 검색어로 props로 전달받은 "한식", "중식", "일식" 같은 종류를 검색한다. (음식점을 kakao map api에서 제공하는 최대 페이지까지 데이터를 모아서 따로 필터링 하는것보다 기본적으로 음식점만을 검색하기 때문에 검색어로 필터링 하는게 더 낫다고 생각했다)

keyword

  • SELECT와 거의 동일하고 검색어는 input의 value를 사용한다.
  • bounds 한다. 예를 들면 기흥역을 보고있다가 강남역을 검색하면 지도 범위를 강남역으로 재설정한다.

CallBack

// callback
  const kakaoMapCallback = (data, status, _pagination) => {
    if (status === kakao.maps.services.Status.OK) {
      // 여기 data에 검색 결과가 들어있다.
      // console.log(data)
      getIsMapResult(true);
      const bounds = new kakao.maps.LatLngBounds();
      let markers = [];
      getPlace(data);
      for (var i = 0; i < data.length; i++) {
        markers.push({
          position: {
            lat: data[i].y,
            lng: data[i].x,
          },
          content: data[i].place_name,
          data: data[i],
        });
        bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
      }
      setMarkers(markers);
      // bounds!!!!!!!!!!!!!!!!!!!
      if (isUseBounds) map.setBounds(bounds);
    } else if (status === kakao.maps.services.Status.ZERO_RESULT) {
      getIsMapResult(false);
      setMarkers([]);
    } else if (status === kakao.maps.services.Status.ERROR) {
    }
  };

검색결과를 callback 함수로 받아올 수 있다. 검색결과를 잘 받았을때, 결과가 없을때, 에러가 났을때 처리할 로직을 작성하면 되고, sample에서 크게 달라지는 부분은 없다.
bounds라고 주석 처리한 부분만 참고하면 될듯 하다. isUseBounds = true 일때만 지도 범위를 재설정하게 만들어뒀다.


전체코드


// search 
const getSearchResult = (type) => {
    if (!map) return;
    const options = {
      category_group_code: "FD6",
      location: new kakao.maps.LatLng(
        map.getCenter().getLat(),
        map.getCenter().getLng()
      ),
    };
    if (type === CATEGORY) {
      isUseBounds = false;
      const copyOptions = {
        ...options,
        sort: kakao.maps.services.SortBy.DISTANCE,
      };
      ps.categorySearch("FD6", kakaoMapCallback, copyOptions);
    } else if (type === SELECT) {
      isUseBounds = false;
      ps.keywordSearch(selectType, kakaoMapCallback, options);
    } else {
      isUseBounds = true;
      ps.keywordSearch(searchInputValue, kakaoMapCallback, options);
    }
  };


// callback
  const kakaoMapCallback = (data, status, _pagination) => {
    if (status === kakao.maps.services.Status.OK) {
      // 여기 data에 검색 결과가 들어있다.
      // console.log(data)
      getIsMapResult(true);
      const bounds = new kakao.maps.LatLngBounds();
      let markers = [];
      getPlace(data);
      for (var i = 0; i < data.length; i++) {
        markers.push({
          position: {
            lat: data[i].y,
            lng: data[i].x,
          },
          content: data[i].place_name,
          data: data[i],
        });
        bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
      }
      setMarkers(markers);
      if (isUseBounds) map.setBounds(bounds);
    } else if (status === kakao.maps.services.Status.ZERO_RESULT) {
      getIsMapResult(false);
      setMarkers([]);
    } else if (status === kakao.maps.services.Status.ERROR) {
    }
  };

  useEffect(() => {
    getSearchResult(CATEGORY);
  }, [map, position]);

  useEffect(() => {
    getSearchResult(SELECT);
  }, [selectType]);

  const handleSearchButton = () => {
    getSearchResult();
  };

  • 처음 map이 그려질때 아무 음식점 리스트가 없으면 심심해보여서 우리동네 좌표를 기본값으로 categorySearch하여 리스트를 보여주게끔 해놨다.

  • dragEnd 이벤트로 position state가 변경되면 마찬가지로 categorySearch를 한다.

  • 메뉴탭에 음식종류 선택을 하면 음식종류(한식, 중식, 일식)를 value로 keywordSearch를 한다.

  • onClick 이벤트(검색버튼), onKeyPress 이벤트(엔터키) 에는 input value로 keywordSearch를 한다.


비교하기

(수정 전)

위치 검색
드래그 검색 (지도를 확대후에 드래그했는데 지도를 재구성하면서 축소되어서 나온다)
메뉴탭 검색 (마찬가지로 확대 level이 들쑥날쑥하다)

(수정 후)

위치 검색
드래그 검색
메뉴탭 검색

0개의 댓글