장소 검색 결과를 카카오 지도에 나타내기

이전 포스팅에서는 카카오 지도API를 통해 화면에 띄웠었다. 이번에는 장소를 input 값으로 받아 해당 장소를 카카오 지도에 띄우고 마커로 표시해보자.

 

https://gingerkang.tistory.com/65

 

[React] 함수형 컴포넌트에서 카카오 지도 API 사용하기

함수형 컴포넌트에서 카카오 지도 API 사용하기 지도가 필요해서 카카오 지도 API를 활용해 개발을 하려고 했는데, 다른 대부분의 리액트 자료들이 그렇듯 클래스형 컴포넌트 기준으로 나와 있어

gingerkang.tistory.com


카카오 지도 라이브러리

 

장소 검색을 하기 위해 services 라이브러리를 사용할 것이다.

 

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=APIKEY&libraries=LIBRARY"></script>

 

지도 API를 불러오기 위해 head 태그 안에 삽입해놨던 script 태그에 위와 같이 libraries를 추가하여 준다. LIBRARY에 사용할 라이브러리 이름(services)을 넣어준다.

 

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=APIKEY&libraries=services"></script>

 

이제 장소 검색 서비스를 이용해보자. 장소 검색 객체를 생성하고 검색 결과 완료시 호출되는 콜백함수를 MapContainer.jsuseEffect 안에 넣어주자.

 

// 장소 검색 객체를 생성
const ps = new kakao.maps.services.Places(); 

// 키워드로 장소를 검색
ps.keywordSearch('입력 값', placesSearchCB); 

// 키워드 검색 완료 시 호출되는 콜백함수
function placesSearchCB (data, status, pagination) {
    if (status === kakao.maps.services.Status.OK) {

        // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
        // LatLngBounds 객체에 좌표를 추가
        let bounds = new kakao.maps.LatLngBounds();

        for (let i=0; i<data.length; i++) {
            displayMarker(data[i]);    
            bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
        }       

        // 검색된 장소 위치를 기준으로 지도 범위를 재설정
        map.setBounds(bounds);
    } 
}

 

그리고 지도에 입력 값에 대한 마커를 표시하기 위해 displayMarker 함수도 추가해준다.

 

// 지도에 마커를 표시하는 함수
function displayMarker(place) {
    
    // 마커를 생성하고 지도에 표시
    let marker = new kakao.maps.Marker({
        map: map,
        position: new kakao.maps.LatLng(place.y, place.x) 
    });

    // 마커에 클릭이벤트를 등록
    kakao.maps.event.addListener(marker, 'click', function() {
        // 마커를 클릭하면 장소명이 인포윈도우에 표출
        infowindow.setContent('<div style="padding:5px;font-size:12px;">' + place.place_name + '</div>');
        infowindow.open(map, marker);
    });
}

 

MapContainer.js

// MapContainer.js

import React, { useEffect } from 'react';

const { kakao } = window;

const MapContainer = () => {

    useEffect(() => {
        const container = document.getElementById('myMap');
		const options = {
			center: new kakao.maps.LatLng(33.450701, 126.570667),
			level: 3
		};
        const map = new kakao.maps.Map(container, options);
    
    	const ps = new kakao.maps.services.Places(); 

        ps.keywordSearch('입력 값', placesSearchCB); 

        function placesSearchCB (data, status, pagination) {
            if (status === kakao.maps.services.Status.OK) {

                let bounds = new kakao.maps.LatLngBounds();

                for (let i=0; i<data.length; i++) {
                    displayMarker(data[i]);    
                    bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
                }       

                map.setBounds(bounds);
            } 
        }

        function displayMarker(place) {
            let marker = new kakao.maps.Marker({
                map: map,
                position: new kakao.maps.LatLng(place.y, place.x) 
            });
        }
    }, []);

    return (
        <div id='myMap' style={{
            width: '500px', 
            height: '500px'
        }}></div>
    );
}

export default MapContainer;

 

 

// 마커에 클릭이벤트를 등록
kakao.maps.event.addListener(marker, 'click', function() {
    // 마커를 클릭하면 장소명이 인포윈도우에 표출
    infowindow.setContent('<div style="padding:5px;font-size:12px;">' + place.place_name + '</div>');
    infowindow.open(map, marker);
});

 

위의 코드는 마커를 클릭 했을 때 장소 이름을 나타내는 코드인데, 나는 필요 없어서 뺐다. 필요한 분들은 그냥 위의 코드 그대로 넣고 지도를 담는 container를 선언한 곳 위에 다음 코드만 작성해주면 된다.

 

let infowindow = new kakao.maps.InfoWindow({zIndex:1});

 

간단하게 지도에 입력 받은 값을 표시할 준비가 끝났다. 이제 input을 받을 component를 하나 만들어주자.

 

SearchPlace.js

// SearchPlace.js

import React, { useState } from "react";
import MapContainer from "./MapContainer";

const SearchPlace = () => {
  const [inputText, setInputText] = useState("");
  const [place, setPlace] = useState("");

  const onChange = (e) => {
    setInputText(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    setPlace(inputText);
    setInputText("");
  };

  return (
    <>
      <form className="inputForm" onSubmit={handleSubmit}>
        <input
          placeholder="Search Place..."
          onChange={onChange}
          value={inputText}
        />
        <button type="submit">검색</button>
      </form>
      <MapContainer searchPlace={place} />
    </>
  );
};

export default SearchPlace;

 

위와 같이 입력을 받고 그 값을 inputText에 저장을 한 후, 한눈에 보기 쉽게 submit이 되면 place에 담아 MapContainerprops로 전달한다. MapContainer에서 props를 받아오기 위해 아래와 같이 코드를 수정한다.

 

const MapContainer = ({ searchPlace }) => {
	...
}

 

근데 이때 문제가 있다. 이대로 실행을 시키면 useEffect의 두 번째 파라미터가 []로 되어 있어 렌더링이 되지 않을 것 이다. 그렇다고 []를 없애면 MapContainerSearchPlace 컴포넌트 안에 있어 inputText 상태가 바뀔 때 마다 렌더링이 계속 될 것이다. 이를 방지하기 위해 MapContainer 안에 있는 useEffect 두 번째 파라미터 [] 안에 searchPlace를 넣어준다. 그러면 지도는 검색 결과가 바뀔 때만 렌더링을 다시 할 것이다.

 

MapContainer.js

// MapContainer.js

...

const MapContainer = ({ searchPlace }) => {
  useEffect(() => {
    const container = document.getElementById("myMap");
    const options = {
      center: new kakao.maps.LatLng(33.450701, 126.570667),
      level: 3,
    };
    const map = new kakao.maps.Map(container, options);

    const ps = new kakao.maps.services.Places();

    ps.keywordSearch(searchPlace, placesSearchCB);

    function placesSearchCB(data, status, pagination) {
      if (status === kakao.maps.services.Status.OK) {
        let bounds = new kakao.maps.LatLngBounds();

        for (let i = 0; i < data.length; i++) {
          displayMarker(data[i]);
          bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
        }

        map.setBounds(bounds);
      }
    }

    function displayMarker(place) {
      let marker = new kakao.maps.Marker({
        map: map,
        position: new kakao.maps.LatLng(place.y, place.x),
      });
    }
  }, [searchPlace]);
  
...

 

 

 


생강강

,