Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #4. 키워드로 장소 검색하기에서 가져온 장소의 위치를 지도에 표시해줄 차례이다.
장소 검색 결과를 잘 받아온 후 장소의 위치를 지도에 찍어주고 싶다고 할 때,
Kakao API에서 사용할 수 있는 객체로는 마커, 인포윈도우, 커스텀오버레이 이렇게 3개가 있다.
기본 마커 | 인포윈도우 | 커스텀 오버레이 |
---|---|---|
Marker | InfoWindow | CustomOverlay |
(이미지 클릭 시 문서 이동) | (이미지 클릭 시 문서 이동) | (이미지 클릭 시 문서 이동) |
Kakao 지도 API 문서에서 설명하는 코드 예시를 보면 다음과 같이 구현할 수 있다.
// MAP 객체 생성 시 들어갈 설정
var mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표
level: 3 // 지도의 확대 레벨
};
// MAP 객체
var map = new kakao.maps.Map(mapContainer, mapOption);
var position = new kakao.maps.LatLng(33.450701, 126.570667);
var content = ''
// 마커 띄우기
var marker = new kakao.maps.Marker({
position
});
// 인포윈도우
var infowindow = new kakao.maps.InfoWindow({
position,
content
});
// 커스텀 오버레이
var customOverlay = new kakao.maps.CustomOverlay({
position,
content
});
marker.setMap(map); // 마커 띄우기
infowindow.open(map, marker);
customOverlay.setMap(map); // 커스텀 오버레이 띄우기
Map
객체 생성 / 혹은 생성되어 있음 전제position
값 가져오기 (코드에선 지정된 position
을 사용하고 있으나, 이후 장소 데이터 각각의 position
값을 불러올 것임.)Marker, InfoWindow, CustomOverlay
객체 생성setMap()
메서드를 통해 지도에 띄우기⭐ 객체 정의 +
setMap()
메서드를 사용하지 않고 parameter에map
을 넣어 객체를 '생성'만 하는 방식으로도 충분히 요소를 지도에 띄울 수 있다.new kakao.maps.Marker({ map, position });
⭐ 인포윈도우의 경우
- 마커와 함께 사용 시에는open()
메서드를 통해 띄운다.
위를 바탕으로 커스텀하기 위해 세개를 모두 사용해본 결과, 각각의 제한이 존재했다.
option
에서 Dom Element 삽입을 지원하지 않음. 단, markerImage
옵션이 존재해 이미지 파일을 적용하는 방식은 가능content
옵션을 통해 Dom Element 삽입이 가능하나, 이미지처럼 인포 윈도우 최상위 element에서 고정된 스타일이 있어 커스텀이 어려움(이미지 처럼 테두리 하얀 네모박스...못없앱니다...)결론적으로 커스텀을 하고 싶다면 인포윈도우 사용은 좋지 않았다.
- 마커 표시만 하고 싶다면, 마커를 사용하고,
- 마커 위에 장소의 정보를 띄우고 싶다면 커스텀 오버레이로 대체하되
열고 닫기 기능을 추가할 경우 마커에 이벤트를 등록하여 '클릭 시 커스텀 오버레이 생성'하는 방법으로 구현하는 것을 추천한다.
나의 경우 프로젝트에서 필요에 따라 각각 마커와 커스텀 오버레이를 사용하여 다음과 같이 장소 위치를 띄워주고 있는데, 그 방법을 얘기해보려 한다.
- Marker 사용 | -CustomOverlay 사용 |
---|
Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #4. 키워드로 장소 검색하기에 만들었던 useSearchPlaces
를 가져왔다.
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(filteredPlaces);
} 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;
1번은 #2 지도 띄우기를 구현할 때 mapContext에 미리 저장한 Map 객체를 활용하고,
2번은 displayPlaces
라는 함수를 만들어 객체 생성을 처리해볼 것이다.
그러면 아래와 같이 세팅 완!
const useSearchPlaces = () => {
const mapContext = useMap(); // mapContext 가져오기
// 장소 검색 함수
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) {
displayPlaces(filteredPlaces); // 마커 / 커스텀오버레이를 생성하는 함수 호출
} else {
if (status === kakao.maps.services.Status.ZERO_RESULT) {
return alert('검색 결과가 존재하지 않습니다.');
} else if (status === kakao.maps.services.Status.ERROR) {
return alert('검색 결과 중 오류가 발생했습니다.');
}
}
};
// 마커 / 커스텀오버레이 생성
const displayPlaces = (places: any[]) => {
const mapData : kakao.maps.Map = mapContext?.mapData;
// 만약 Map을 띄움과 동시에 마커를 찍을 거라면 샘플 코드처럼
// var map = new kakao.maps.Map(mapContainer, mapOption);를 정의해 사용
places.forEach((place, index) => {
const position = new kakao.maps.LatLng(place.y, place.x); // position 값 가져오기
// ...여기서 Marker,Customoverlay 등을 생성
}
}
return { searchPlaces };
};
export default useSearchPlaces;
사진처럼 내가 커스텀해 만든 마커를 띄우는 방법으로 다른 이미지로 마커 생성하기를 활용하면 되는데,
kakao.maps.Marker(options)
주어진 객체로 마커를 생성한다.
지도 뿐만 아니라 로드뷰 위에도 올릴 수 있다.var marker = new kakao.maps.Marker({ map: map, position: new kakao.maps.LatLng(33.450701, 126.570667) });
Parameters
options
Object
map
Map | Roadview : 마커가 올라갈 지도 또는 로드뷰position
LatLng | Viewpoint : 마커의 좌표 또는 로드뷰에서의 시점image
MarkerImage : 마커의 이미지title
String : 마커 엘리먼트의 타이틀 속성 값 (툴팁)draggable
Boolean : 드래그 가능한 마커, 로드뷰에 올릴 경우에는 유효하지 않다clickable
Boolean : 클릭 가능한 마커zIndex
Number : 마커 엘리먼트의 z-index 속성 값opacity
Number : 마커 투명도 (0-1)altitude
Number : 로드뷰에 올라있는 마커의 높이 값(m 단위)range
Number : 로드뷰 상에서 마커의 가시반경(m 단위), 두 지점 사이의 거리가 지정한 값보다 멀어지면 마커는 로드뷰에서 보이지 않게 된다
Marker 객체의 Parameters를 살펴보면 image
옵션을 이용해 마커 이미지를 지정할 수 있고,
MarkerImage
라는 객체를 받고 있다.
kakao.maps.MarkerImage(src, size[, options])
Parameters
src
String : 이미지 주소size
Size : 마커의 크기options
Obejct
alt
String : 마커 이미지의 alt 속성값을 정의한다.coords
String : 마커의 클릭 또는 마우스오버 가능한 영역을 표현하는 좌표값offset
Point : 마커의 좌표에 일치시킬 이미지 안의 좌표 (기본값: 이미지의 가운데 아래)shape
String : 마커의 클릭 또는 마우스오버 가능한 영역의 모양spriteOrigin
Point : 스프라이트 이미지 중 사용할 영역의 좌상단 좌표spriteSize
Size : 스프라이트 이미지의 전체 크기
MarkerImage의 src
, size
, options - offset
를 사용하여 객체 정의 후,
Marker 객체를 생성하면 된다.
// 마커 / 커스텀오버레이 생성
const displayPlaces = (places: any[]) => {
const mapData : kakao.maps.Map = mapContext?.mapData;
// 만약 Map을 띄움과 동시에 마커를 찍을 거라면 샘플 코드처럼
// var map = new kakao.maps.Map(mapContainer, mapOption);를 정의해 사용
places.forEach((place, index) => {
const position = new kakao.maps.LatLng(place.y, place.x); // position 값 가져오기
// MarkerImage parameter
const imageSrc = '/icons/icon-marker.svg',
imageSize = new kakao.maps.Size(56, 56),
imageOption = { offset: new kakao.maps.Point(27, 54) };
// MarkerImage
const markerImage = new kakao.maps.MarkerImage(
imageSrc,
imageSize,
imageOption,
);
// Marker
const marker = new kakao.maps.Marker({
position: position,
image: markerImage,
});
// 지도에 띄우기
marker.setMap(mapContext?.mapData as kakao.maps.Map);
}
}
커스텀 오버레이 사용 시 '장소 이름 데이터'를 사용하면서도 원하는 스타일링으로 장소 위치를 띄울 수 있다.
kakao.maps.CustomOverlay(options)
주어진 객체로 커스텀 오버레이를 생성한다.
var customOverlay = new kakao.maps.CustomOverlay({ map: map, clickable: true, content: '<div class="customOverlay"><a href="#">Chart</a></div>', position: new kakao.maps.LatLng(33.450701, 126.570667), xAnchor: 0.5, yAnchor: 1, zIndex: 3 });
Parameters
- `options Object
clickable
Boolean : true 로 설정하면 컨텐츠 영역을 클릭했을 경우 지도 이벤트를 막아준다.content
Node | String : 엘리먼트 또는 HTML 문자열 형태의 내용map Map
| Roadview : 커스텀 오버레이가 올라갈 지도 또는 로드뷰position
LatLng | Viewpoint : 커스텀 오버레이의 좌표 또는 로드뷰에서의 시점xAnchor
Number : 컨텐츠의 x축 위치. 0_1 사이의 값을 가진다. 기본값은 0.5yAnchor
Number : 컨텐츠의 y축 위치. 0_1 사이의 값을 가진다. 기본값은 0.5zIndex
Number : 커스텀 오버레이의 z-index
content
에 Node
를 받고 있어, 컴포넌트를 HTMLElement으로 변환해 넣어 줌으로써 커스텀이 가능하다.
content
에 들어갈 컴포넌트 만들기이미지와 같은 말풍선 모양의 마커를 구현하기 위해 장소 이름(placeName
)을 받는 MarkerInfo
라는 컴포넌트를 만들었다.
'use client';
import Image from 'next/image';
interface MarkerInfoProps {
placeName: string;
}
const MarkerInfo = ({ placeName }: MarkerInfoProps) => {
return (
<div className='speech-bubble flex w-auto gap-2 px-3 py-2 text-sm font-medium lg:text-lg'>
<span>{placeName}</span>
<Image
className='max-w-none'
width={20}
height={20}
src='/icons/icon-logo-mini(default).svg'
alt='로고 그림'
/>
</div>
);
};
export default MarkerInfo;
이를 String으로 변환해 CustomOverlay
객체 생성 시 content
에 지정해 주면 된다.
import MarkerInfo from '../_component/common/MarkerInfo';
//...
// 마커 / 커스텀오버레이 생성
const displayPlaces = (places: any[]) => {
const mapData : kakao.maps.Map = mapContext?.mapData;
// 만약 Map을 띄움과 동시에 마커를 찍을 거라면 샘플 코드처럼
// var map = new kakao.maps.Map(mapContainer, mapOption);를 정의해 사용
places.forEach((place, index) => {
const position = new kakao.maps.LatLng(place.y, place.x); // position 값 가져오기
// 커스텀오버레이 content에 들어갈 컴포넌트
const overlayContent = <MarkerInfo placeName={place.place_name} />;
// 컴포넌트를 string으로 전환 후 HTMLElement에 삽입
const overlay = document.createElement('div');
overlay.innerHTML = ReactDOMServer.renderToString(overlayContent);
// 커스텀 오버레이 생성
const newOverlay = new kakao.maps.CustomOverlay({
position: position,
content: overlay,
yAnchor: 1.3, // 높이 지정 (선택)
});
newOverlay.setMap(mapContext?.mapData as kakao.maps.Map);
}
}
생성과 동시에 클릭 이벤트를 등록해 줌으로써 해당 마커의 위치로 이동하거나, 장소 상세 페이지로 이동하는 등의 기능을 넣어줄 수 있다.
아래는 커스텀오버레이 클릭 시 지도를 해당 마커의 위치로 이동하는 이벤트를 등록한 방법이다.
import MarkerInfo from '../_component/common/MarkerInfo';
//...
// 마커 / 커스텀오버레이 생성
const displayPlaces = (places: any[]) => {
const mapData : kakao.maps.Map = mapContext?.mapData;
// 만약 Map을 띄움과 동시에 마커를 찍을 거라면 샘플 코드처럼
// var map = new kakao.maps.Map(mapContainer, mapOption);를 정의해 사용
places.forEach((place, index) => {
const position = new kakao.maps.LatLng(place.y, place.x); // position 값 가져오기
// 커스텀오버레이 content에 들어갈 컴포넌트
const overlayContent = <MarkerInfo placeName={place.place_name} />;
// 컴포넌트를 string으로 전환 후 HTMLElement에 삽입
const overlay = document.createElement('div');
overlay.innerHTML = ReactDOMServer.renderToString(overlayContent);
// HTMLElement에 이벤트 등록
overlay.addEventListener('click', () => {
mapContext?.mapData?.panTo(position);
});
// 커스텀 오버레이 생성
const newOverlay = new kakao.maps.CustomOverlay({
position: position,
content: overlay,
yAnchor: 1.3,
clickable: true, // 클릭 가능 여부 true로 설정
});
newOverlay.setMap(mapContext?.mapData as kakao.maps.Map);
}
}