지도/로컬 API에 대한 문의게시판입니다.
next js 사용중입니다.
카카오 지도 키워드로 검색 시 하단 검색 리스트 부분 클릭이벤트를 등록하여 원하는 위치로 이동하고 싶습니다.
또한 검색결과 클릭시 검색결과의 데이터를 가져오고 싶습니다.
import { useEffect, useState } from "react";
declare const window: typeof globalThis & {
kakao: any;
};
export default function MarketMapContainer2() {
const [test, setTest] = useState("");
useEffect(() => {
const script = document.createElement("script");
script.src =
document.head.appendChild(script);
script.onload = () => {
window.kakao.maps.load(function () {
const MARKER_WIDTH = 33; // 기본, 클릭 마커의 너비
const MARKER_HEIGHT = 36; // 기본, 클릭 마커의 높이
const OFFSET_X = 12; // 기본, 클릭 마커의 기준 X좌표
const OFFSET_Y = MARKER_HEIGHT; // 기본, 클릭 마커의 기준 Y좌표
const OVER_MARKER_WIDTH = 40; // 오버 마커의 너비
const OVER_MARKER_HEIGHT = 42; // 오버 마커의 높이
const OVER_OFFSET_X = 13; // 오버 마커의 기준 X좌표
const OVER_OFFSET_Y = OVER_MARKER_HEIGHT; // 오버 마커의 기준 Y좌표
const SPRITE_MARKER_URL =
"https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/markers_sprites2.png"; // 스프라이트 마커 이미지 URL
const SPRITE_WIDTH = 126; // 스프라이트 이미지 너비
const SPRITE_HEIGHT = 146; // 스프라이트 이미지 높이
const SPRITE_GAP = 10; // 스프라이트 이미지에서 마커간 간격
const markerSize = new kakao.maps.Size(MARKER_WIDTH, MARKER_HEIGHT); // 기본, 클릭 마커의 크기
const markerOffset = new kakao.maps.Point(OFFSET_X, OFFSET_Y); // 기본, 클릭 마커의 기준좌표
const overMarkerSize = new kakao.maps.Size(
OVER_MARKER_WIDTH,
OVER_MARKER_HEIGHT
); // 오버 마커의 크기
const overMarkerOffset = new kakao.maps.Point(
OVER_OFFSET_X,
OVER_OFFSET_Y
); // 오버 마커의 기준 좌표
const spriteImageSize = new kakao.maps.Size(
SPRITE_WIDTH,
SPRITE_HEIGHT
); // 스프라이트 이미지의 크기
// 마커를 담을 배열입니다
let markers = [];
// 지도를 표시할 div
const container = document.getElementById("map");
const options = {
// 지도 중심 좌표
center: new window.kakao.maps.LatLng(38.2313466, 128.2139293),
// 지도 확대 레벨
level: 3,
};
// 지도 생성
const map = new window.kakao.maps.Map(container, options);
// 마커가 표시될 위치입니다
const markerPosition = new kakao.maps.LatLng(38.2313466, 128.2139293);
// 마커를 생성합니다
const marker = new kakao.maps.Marker({
position: markerPosition,
});
// 마커가 지도 위에 표시되도록 설정합니다
marker.setMap(map);
// 장소 검색 객체를 생성합니다
const ps = new window.kakao.maps.services.Places();
// 검색 결과 목록이나 마커를 클릭했을 때 장소명을 표출할 인포윈도우를 생성합니다
const infowindow = new window.kakao.maps.InfoWindow({ zIndex: 1 });
const searchForm = document.getElementById("form");
searchForm.addEventListener("submit", function (e) {
e.preventDefault();
// 키워드로 장소를 검색합니다
searchPlaces();
});
// 키워드 검색을 요청하는 함수입니다
function searchPlaces() {
const keyword = document.getElementById("keyword").value;
if (!keyword.replace(/^\s+|\s+$/g, "")) {
alert("키워드를 입력해주세요!");
return false;
}
// 장소검색 객체를 통해 키워드로 장소검색을 요청합니다
ps.keywordSearch(keyword, placesSearchCB);
}
// 장소검색이 완료됐을 때 호출되는 콜백함수 입니다
function placesSearchCB(data, status, pagination) {
if (status === window.kakao.maps.services.Status.OK) {
// 정상적으로 검색이 완료됐으면
// 검색 목록과 마커를 표출합니다
displayPlaces(data);
// 페이지 번호를 표출합니다
displayPagination(pagination);
const bounds = new window.kakao.maps.LatLngBounds();
for (let i = 0; i < data.length; i++) {
displayMarker(data[i]);
bounds.extend(new window.kakao.maps.LatLng(data[i].y, data[i].x));
}
// 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
map.setBounds(bounds);
} else if (status === window.kakao.maps.services.Status.ZERO_RESULT) {
alert("검색 결과가 존재하지 않습니다.");
return;
} else if (status === window.kakao.maps.services.Status.ERROR) {
alert("검색 결과 중 오류가 발생했습니다.");
return;
}
}
function displayMarker(place) {
// 마커를 생성하고 지도에 표시합니다
const marker = new window.kakao.maps.Marker({
map: map,
position: new window.kakao.maps.LatLng(place.y, place.x),
});
// 마커에 클릭이벤트를 등록합니다
window.kakao.maps.event.addListener(
marker,
"click",
function (mouseEvent) {
// 마커를 클릭하면 장소명이 인포윈도우에 표출됩니다
setTest(place);
infowindow.setContent(`안녕하세요`);
infowindow.open(map, marker);
const moveLatLon = new window.kakao.maps.LatLng(place.y, place.x);
map.panTo(moveLatLon);
}
);
}
// 마커리스트 이동
// const moveMap = function setCenter() {
// // 이동할 위도 경도 위치를 생성합니다
// const moveLatLon = new window.kakao.maps.LatLng(
// 33.452613,
// 126.570888
// );
// // 지도 중심을 이동 시킵니다
// map.setCenter(moveLatLon);
// };
// 검색 결과 목록과 마커를 표출하는 함수입니다
function displayPlaces(places) {
const listEl = document.getElementById("placesList");
const menuEl = document.getElementById("menu_wrap");
const fragment = document.createDocumentFragment();
const bounds = new window.kakao.maps.LatLngBounds();
const listStr = "";
// 검색 결과 목록에 추가된 항목들을 제거합니다
removeAllChildNods(listEl);
// 지도에 표시되고 있는 마커를 제거합니다
removeMarker();
for (let i = 0; i < places.length; i++) {
// 마커를 생성하고 지도에 표시합니다
const placePosition = new window.kakao.maps.LatLng(
places[i].y,
places[i].x
);
const marker = addMarker(placePosition, i);
const itemEl = getListItem(i, places[i]); // 검색 결과 항목 Element를 생성합니다
// 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
// LatLngBounds 객체에 좌표를 추가합니다
bounds.extend(placePosition);
// 마커와 검색결과 항목에 mouseover 했을때
// 해당 장소에 인포윈도우에 장소명을 표시합니다
// mouseout 했을 때는 인포윈도우를 닫습니다
(function (marker, title) {
window.kakao.maps.event.addListener(
marker,
"mouseover",
function () {
displayInfowindow(marker, title);
}
);
window.kakao.maps.event.addListener(
marker,
"mouseout",
function () {
infowindow.close();
}
);
itemEl.onmouseover = function () {
displayInfowindow(marker, title);
};
itemEl.onmouseout = function () {
infowindow.close();
};
})(marker, places[i].place_name);
fragment.appendChild(itemEl);
}
// 검색결과 항목들을 검색결과 목록 Element에 추가합니다
listEl.appendChild(fragment);
menuEl.scrollTop = 0;
// function (mouseEvent) {
// // 마커를 클릭하면 장소명이 인포윈도우에 표출됩니다
// const moveLatLon = new window.kakao.maps.LatLng(place.y, place.x);
// map.panTo(moveLatLon);
// }
// 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
map.panTo(bounds);
}
// 검색결과 항목을 Element로 반환하는 함수입니다
function getListItem(index, places) {
const el = document.createElement("li");
let itemStr =
'<span class="markerbg marker_' +
(index + 1) +
'"></span>' +
'<div class="info">' +
" <h5>" +
places.place_name +
"</h5>";
if (places.road_address_name) {
itemStr +=
" <span>" +
places.road_address_name +
"</span>" +
' <span class="jibun gray">' +
places.address_name +
"</span>";
} else {
itemStr += " <span>" + places.address_name + "</span>";
}
itemStr +=
' <span class="tel">' + places.phone + "</span>" + "</div>";
el.innerHTML = itemStr;
el.className = "item";
return el;
}
// 마커를 생성하고 지도 위에 마커를 표시하는 함수입니다
function addMarker(position, idx) {
const imageSrc =
"https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png"; // 마커 이미지 url, 스프라이트 이미지를 씁니다
const imageSize = new window.kakao.maps.Size(36, 37); // 마커 이미지의 크기
const imgOptions = {
spriteSize: new window.kakao.maps.Size(36, 691), // 스프라이트 이미지의 크기
spriteOrigin: new window.kakao.maps.Point(0, idx * 46 + 10), // 스프라이트 이미지 중 사용할 영역의 좌상단 좌표
offset: new window.kakao.maps.Point(13, 37), // 마커 좌표에 일치시킬 이미지 내에서의 좌표
};
const markerImage = new window.kakao.maps.MarkerImage(
imageSrc,
imageSize,
imgOptions
);
const marker = new window.kakao.maps.Marker({
position: position, // 마커의 위치
image: markerImage,
});
marker.setMap(map); // 지도 위에 마커를 표출합니다
markers.push(marker); // 배열에 생성된 마커를 추가합니다
return marker;
}
// 지도 위에 표시되고 있는 마커를 모두 제거합니다
function removeMarker() {
for (let i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
markers = [];
}
// 검색결과 목록 하단에 페이지번호를 표시는 함수입니다
function displayPagination(pagination) {
const paginationEl = document.getElementById("pagination");
const fragment = document.createDocumentFragment();
// i;
// 기존에 추가된 페이지번호를 삭제합니다
while (paginationEl.hasChildNodes()) {
paginationEl.removeChild(paginationEl.lastChild);
}
for (let i = 1; i <= pagination.last; i++) {
const 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);
}
// 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수입니다
// 인포윈도우에 장소명을 표시합니다
function displayInfowindow(marker, title) {
const content =
'<div style="padding:5px;z-index:1;">' + title + "</div>";
infowindow.setContent(content);
infowindow.open(map, marker);
}
// 검색결과 목록의 자식 Element를 제거하는 함수입니다
function removeAllChildNods(el) {
while (el.hasChildNodes()) {
el.removeChild(el.lastChild);
}
}
});
};
}, []);
const [aaa, setAaa] = useState("");
const onchangeTest = (event) => {
setAaa(event?.target.value);
};
return (
<div className="map_wrap">
<div
id="map"
style={{
width: "1000px",
height: "1000px",
position: "relative",
overflow: "hidden",
}}
></div>
<div id="menu_wrap" className="bg_white">
<div className="option">
<div>
<form id="form">
키워드 :{" "}
<input
type="text"
value={aaa}
id="keyword"
onChange={onchangeTest}
/>
<button type="submit">검색하기</button>
</form>
</div>
</div>
<ul id="placesList"></ul>
<div id="pagination"></div>
</div>
</div>
);
}
현재 검색 후 리스트 보여주기는 가능하며 리스트의 특정 키워드 클릭 시 마커가 지도 중앙에 표시될 수 있도록 움직이고 싶습니다.
아직 초보인 만큼 상세하게 알려주시면 감사하겠습니다.