프로젝트 작업이 어느정도 진행된 상황에서 기록을 하려니 힘들구만...하지만 최대한 정리해 보았다.
이번엔 Kakao 지도 API를 사용하게 된 가장 큰 목적, <장소 검색>기능을 구현한 과정을 얘기해 보려 한다.
Kakao 지도 API 샘플 중 '키워드로 장소검색하고 목록으로 표출하기'를 사용할 것이다.
샘플 코드를 보면 목록 + 마커를 함께 생성하고 있는데, 장소 목록을 불러오는 과정까지 얘기하고 마커 생성은 다음 5탄에서 자세히 다뤄볼 예정!
나는 이 키워드 장소 검색 로직을 hook으로 만들어서
등의 기능에 재사용해볼 것이다.
구현 순서는 아래와 같이!
- 장소 검색 Input 만들기 (SearchForm)
- 장소 검색 Hook 만들기(useSearchPlaces)
- 검색 결과
- 위치 기준 검색하기
- 일정 영역 내에서 검색하기
import React, { useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import Image from 'next/image';
import useSearchPlaces from '@/app/_hooks/useSearchPlaces';
const SearchForm = () => {
const router = useRouter();
const { searchPlaces } = useSearchPlaces();
const [keyword, setKeyword] = useState('');
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
searchPlaces(keyword);
router.push(`/place/search/${keyword}`);
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setKeyword(e.currentTarget.value);
};
return (
<div className='border-olive-green border-b p-4'>
<form
onSubmit={onSubmit}
className='border-olive-green box-content flex h-9 justify-between gap-1 rounded-lg border border-solid bg-white py-1 pl-2 pr-3 shadow-md'
>
<input
type='text'
value={keyword}
onChange={handleInputChange}
size={15}
className='basis-full rounded-full pl-1 outline-none'
placeholder='장소 검색하기'
/>
<button
type='submit'
className='relative flex w-4 shrink-0 items-center justify-center'
>
<Image fill={true} src='/icons/icon-search.svg' alt='검색 아이콘' />
</button>
</form>
</div>
);
};
export default SearchForm;
useSearchPlaces - searchPlaces
useSearchPlaces훅에서 반환할 장소 검색 함수를 만든다.
const useSearchPlaces = () => {
// 장소 검색 함수
const searchPlaces = (keyword: string) => {
if (keyword !== '') {
const places = new kakao.maps.services.Places();
places.keywordSearch(keyword, searchPlacesCB);
}
};
//...
return { searchPlaces };
};
export default useSearchPlaces;
keyword
매개변수를 받고 빈 문자열인지 확인한다.kakao.maps.services.Places
객체를 생성한다. places.keywordSearch
메서드를 호출해 키워드로 장소를 검색한다. 두번째 인자에서 콜백 함수를 호출하는데, 콜백 함수를 만들어 검색 후의 결과를 처리하면 된다.useSearchPlaces - searchPlaceCB
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(data);
} 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;
data
: 검색된 장소의 결과 데이터status
: 검색의 성공 여부// kakao.maps.services.Status
export enum Status {
ERROR = "ERROR", // 서버 응답에 문제가 있는 경우
OK = "OK", //검색 결과 있음
ZERO_RESULT = "ZERO_RESULT", // 정상적으로 응답 받았으나 검색 결과는 없음
}
pagination
: 페이지네이션 정보로, 검색 결과가 여러 페이지에 걸쳐 있을 때 이를 처리하는 데 사용여기까지 작성 후 결과를 확인해 보자.
'공원'을 검색했을 때의 결과이다.
최대 15개의 목록 결과가 잘 불러와 진다.
그런데 검색 결과의 주소들을 자세히 보면 여러 지역에서 골고루 결과를 가져오고 있다.
중심 좌표
를 기준으로 검색하고 싶을 때영역
내의 검색 결과만 가져오고 싶을때의 경우에는 위 검색 결과가 아쉬울 수 있다.
이 때 지도 객체에서 중심좌표나, 영역 정보를 가져와 검색 옵션을 지정해 줄 수 있다.
Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #2 지도 띄우기에서 만든
useMap() Context
에서mapData
Map 객체(kakao.maps.Map
)를 가져와 활용해보자.
예시로 지도가 이미지와 같이 '서호공원'이라는 장소를 zoom해 보여지고 있고 지도의 중심좌표, 영역을 각각 지정해 주었을 때의 결과를 비교해 보자.
지도의 중심 좌표
를 기준으로 검색하기
places.keywordSearch
메서드를 다시 살펴 보자.
알고 보니 keyword
, 콜백 함수
, options
이렇게 3개의 인자를 받는다.
option 중 location
을 지정해 주면 된다.
import { useMap } from '../shared/contexts/Map';
const useSearchPlaces = () => {
const mapContext = useMap();
// 장소 검색 함수
const searchPlaces = (keyword: string) => {
if (keyword !== '') {
const { mapData } = mapContext!;
const location = mapData?.getCenter();
const places = new kakao.maps.services.Places();
places.keywordSearch(keyword, searchPlacesCB, {
location,
});
}
};
// keywordSearch 콜백 함수
// ...
};
export default useSearchPlaces;
mapData
라는 이름으로 저장했던 Map 객체의 내장메서드 getCenter()
를 통해 지도의 중심값을 불러와, keywordSearch의 3번째 인자에 넣어주면 끝!
지도에 보이는 '서호공원'과
주변 '공원'에 해당하는 검색 결과가 모두 불러와 졌다.
현재 내 위치의 좌표
를 기준으로 검색하기
Next.js + TS | Kakao 지도 API 프로젝트에 사용하기 #3 현재 위치 불러오기에서 만들어보았던
useGeolocation
훅을 여기에 활용할 수 있다.import { useState, useEffect } from 'react'; const useGeolocation = () => { const [location, setLocation] = useState<Latlng | null>(null); // 현재 위치를 저장할 상태 return { location }; }; export default useGeolocation;
// import { useMap } from '../shared/contexts/Map';
import useGeolocation from './useGeolocation';
const useSearchPlaces = () => {
// const mapContext = useMap();
const { location: myLocation } = useGeolocation();
// 장소 검색 함수
const searchPlaces = (keyword: string) => {
if (keyword !== '') {
// const { mapData } = mapContext!;
// const location = mapData?.getCenter();
const places = new kakao.maps.services.Places();
places.keywordSearch(keyword, searchPlacesCB, {
location: new kakao.maps.LatLng(
myLocation?.latitude as number,
myLocation?.longitude as number,
),
});
}
};
// keywordSearch 콜백 함수
// ...
return { searchPlaces };
};
export default useSearchPlaces;
'현재 내 위치에서 재검색' 과 같은 기능을 넣고 싶다면 이 로직을 활용하면 된다.
지도의 영역
을 기준으로 검색하기
영역은 places.keywordSearch
메서드의 option 중 bounds
을 지정해 주면 된다.
import { useMap } from '../shared/contexts/Map';
const useSearchPlaces = () => {
const mapContext = useMap();
// 장소 검색 함수
const searchPlaces = (keyword: string) => {
if (keyword !== '') {
const { mapData } = mapContext!;
const bounds = mapData?.getBounds();
// const location = mapData?.getCenter();
const places = new kakao.maps.services.Places();
places.keywordSearch(keyword, searchPlacesCB, {
bounds,
});
}
};
// keywordSearch 콜백 함수
// ...
return { searchPlaces };
};
export default useSearchPlaces;
지도 내 표시된 관련 장소만 불러오는 것을 확인할 수 있다.