import { useState, useEffect, useRef } from "react"; import styles from "./kakao-map.module.scss"; const { kakao } = window; const KakaoMap = ({ keyword }) => { const kakaoMapDiv = useRef(null) const menu = useRef(null) const searchList = useRef(null) const page = useRef(null) const [places, setPlaces] = useState([]) let markers = [] useEffect(() => { const container = kakaoMapDiv.current 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(); const infowindow = new kakao.maps.InfoWindow({ zIndex: 1 }); searchPlaces(keyword) // 키워드 검색을 요청하는 함수입니다 function searchPlaces(keyword) { if (!keyword.replace(/^\s+|\s+$/g, '')) { alert('키워드를 입력해주세요.'); return false } // 장소검색 객체를 통해 키워드로 장소검색을 요청합니다 ps.keywordSearch(keyword, placesSearchCB); } // 장소검색이 완료됐을 때 호출되는 콜백함수 입니다 function placesSearchCB(data, status, pagination) { if (status === kakao.maps.services.Status.OK) { displayPlaces(data); displayPagination(pagination); // 페이지 번호를 표출합니다 } else if (status === kakao.maps.services.Status.ZERO_RESULT) { alert('검색 결과가 존재하지 않습니다.'); return } else if (status === kakao.maps.services.Status.ERROR) { alert('검색 결과 중 오류가 발생했습니다.'); return } } // 검색 결과 목록과 마커를 표출하는 함수입니다 function displayPlaces(places) { let listEl = searchList.current, menuEl = menu.current, fragment = document.createDocumentFragment(), bounds = new kakao.maps.LatLngBounds(); for (let i = 0; i < places.length; i++) { // 마커를 생성하고 지도에 표시합니다 let placePosition = new kakao.maps.LatLng(places[i].y, places[i].x), itemEl = getListItem(i, places[i]); // 검색 결과 항목 Element를 생성합니다 displayMarker(places[i], itemEl) // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해 LatLngBounds 객체에 좌표를 추가합니다 bounds.extend(placePosition); fragment.appendChild(itemEl); } // 검색결과 항목들을 검색결과 목록 Elemnet에 추가합니다 listEl.appendChild(fragment); menuEl.scrollTop = 0; // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다 map.setBounds(bounds); } function displayMarker(place, itemEl) { let marker = new kakao.maps.Marker({ map: map, position: new kakao.maps.LatLng(place.y, place.x), }) kakao.maps.event.addListener(marker, 'mouseover', function () { infowindow.setContent('
' + place.place_name + '
') infowindow.open(map, marker) }) kakao.maps.event.addListener(marker, 'mouseout', function () { infowindow.close(); }) itemEl.onmouseover = function () { itemEl.style.background = 'rgba(0, 0, 0, 0.075)'; itemEl.style.cursor = 'pointer'; infowindow.setContent('
' + place.place_name + '
') infowindow.open(map, marker) }; itemEl.onmouseout = function () { itemEl.style.background = '#fff'; infowindow.close(); }; } }, [keyword]) // 검색결과 목록 하단에 페이지번호를 표시는 함수입니다 function displayPagination(pagination) { let paginationEl = page.current, fragment = document.createDocumentFragment(), i; for (i = 1; i <= pagination.last; i++) { let el = document.createElement('a'); // el.href = "#"; el.innerHTML = i; if (i === pagination.current) { el.className = 'on'; } else { el.onclick = (function (i) { return function () { pagination.gotoPage(i); } })(i); } fragment.appendChild(el); } paginationEl.appendChild(fragment); } // 검색결과 항목을 Element로 반환하는 함수입니다 function getListItem(index, places) { let el = document.createElement('div'), itemStr = '
' + '
' + places.place_name + '
'; if (places.road_address_name) { itemStr += ' ' + places.road_address_name + '' + ' ' + places.address_name + ''; } else { itemStr += ' ' + places.address_name + ''; } itemStr += ' ' + places.phone + '' + '
'; el.innerHTML = itemStr; el.className = 'item'; return el; } return ( <>
) } export default KakaoMap