일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- javascript
- 유데미
- Native select
- kakao map api
- input class
- MUI
- next/Image
- 백준 nodeJS
- react js
- OSI 7계층
- 자바스크립트
- 코드스테이츠
- nodejs
- 이벤트 루프
- 페이지네이션
- primitive type
- 백준
- 프론트엔드
- 자료구조
- 코드스테이츠 메인프로젝트
- 알고리즘
- 배열
- JavaScript Deep Dive
- sort
- 정규표현식
- Node js
- CSS
- 코딩테스트
- 버블정렬
- 재귀함수
- Today
- Total
신입 개발자에서 시니어 개발자가 되기까지
[메인 프로젝트] 메인프로젝트 - 위치검색 기능/검색 페이지(feat. 카카오 맵) 본문
검색 페이지 지도 기능 요구사항
- 현재위치 렌더링하기
- 위치정보 접근권한 비동의한 유저에게는 강남구 렌더링하기
- 위치 검색하면 검색한 위치를 렌더링하기(주소 -> 좌표로 변환)
- 지도 클릭시 클릭한 위치의 주소로 주소정보 업데이트(좌표 -> 법정 주소로 변환)
- 지도 클릭 후 다시 검색하면 검색한 위치로 되돌아가기
- 등록한 주소록 사용하여 빠른 검색 지원
요구사항을 구현하기 위해 사용한 지도 api는 kakao map api다.카카오 map api를 사용한 이유
- 무료다. 하루에 30만건의 api요청을 무료로 할 수 있다.
- 공식문서가 한글로 되어 있어서 이해하기에 쉬웠다.(다만 react버전은 아니어서 이건 알아서 바꿔야 한다)
- 공식적이
1. 현재 위치 렌더링하기
현재 위치는 위치정보 접근 권한 동의를 했을 때는 본인의 실시간 위치가 렌더링된다. 본인의 실시간 위치는 geoLocation api를 사용해서 좌표를 얻어온다.
geoLocation을 사용했을 때의 문제점은 web에서 내 위치 정보를 정확하게 받아오지 않는다는 문제가 있다.
다음은 geoLocation 공식문서에서 현재위치를 받아오는 api에 대한 설명이다
참고: getCurrentPosition()의 기본값에서는 최대한 빠르게 낮은 정밀도의 응답을 반환합니다. 정확하지
않더라도 빠른 정보가 필요한 상황에서 유용합니다. 예를 들어, GPS 기능을 가진 장비는 보정 과정에
수 분이 걸릴 수도 있으므로 IP 위치와 WiFi 등 정확하지 않은 출처에 기반한 위치 정보를 반환할 수 있습니다.
이러한 이유로 정밀도는 낮을 수 있다. 그래서 이것을 보완하기 위해 지도를 클릭하면 상세 주소를 설정할 수 있는 기능을 추가했다.(이건 2번에서 다룬다.)
export const getCurrentLocation = (setLocation: any, setLocationError: any) => {
//정확도를 높이는 옵션. 그러나 web에서는 적용되지 않는다.
const geoLocationOptions = { enableHighAccuracy: true };
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
let lat = position.coords.latitude;
let lng = position.coords.longitude;
const center = { lat, lng };
if (lat === 0 || lng === 0) return;
//setDefaultCoordsAndAddress는 좌표값으로 법정 주소정보를 받아오는 함수다.
setDefaultCoordsAndAddress(center, (result, status) => {
if (status === kakao.maps.services.Status.OK) {
let detailAddr = !!result[0].address.address_name
? result[0].address.address_name
: result[0].road_address.address_name;
//setLocation으로 주소와 좌표정보를 업데이트해서 좌표에 해당하는 위치를 지도에 보여준다.
//setLocation가 업데이트하는 대상은 search page에서 관리하는 좌표정보 state다.
setLocation({ ...center, address: detailAddr });
}
});
},
//위치정보 접근 권한 동의를 하지 않은 경우 setLocationError 함수가 호출된다.
setLocationError,
geoLocationOptions
);
}
};
다음은 위치정보 접근 동의를 하지 않았을 때 강남구가 렌더링 되는 모습이다.
2. 지도 클릭시 클릭한 위치의 주소로 주소정보 업데이트
지도를 클릭했을 때 클릭한 위치의 상세 주소로 주소정보를 업데이트 하는 기능이다. 지도를 클릭하면 파란색 marker가 해당 위치에 올라가고 상세 주소가 바뀌도록 구현했다.
api 코드
//type 지정
interface getMapAndMarkerPropsType {
center: {
lat: number;
lng: number;
mapLevel?: number;
address: string;
};
setTargetCoord: React.Dispatch<
SetStateAction<{ lat: number; lng: number; address: string }>
>;
}
//지도와 마커를 생성하고, 주소-좌표 변환을 해주는 함수. 지도 클릭 이벤트를 바인딩하는 코드도 들어있다.
//원래라면 하나의 함수가 하나의 역할을 해야하지만, kakao map api의 특성상 생성된 지도 위에서
//마커를 올리고, 클릭이벤트를 바인딩해야 해서 여러 역할을 하게 되었다.
export const exchangeCoordToAddress = async (
center: getMapAndMarkerPropsType['center'],
setTargetCoord: React.Dispatch<
React.SetStateAction<{
lat: number;
lng: number;
address: string;
}>
>
) => {
let mapContainer =
document.getElementById('map') || document.createElement('div'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(center.lat, center.lng), // 지도의 중심좌표
level: 5, // 지도의 확대 레벨
};
// 지도를 생성합니다
let map = new kakao.maps.Map(mapContainer, mapOption);
// 주소-좌표 변환 객체를 생성합니다
let geocoder = new kakao.maps.services.Geocoder();
let marker = new kakao.maps.Marker({ position: map.getCenter() }); // 클릭한 위치를 표시할 마커입니다
marker.setMap(map);
setTargetCoord(center);
// 지도를 클릭했을 때 클릭 위치 좌표에 대한 주소정보를 표시하도록 이벤트를 등록합니다
kakao.maps.event.addListener(map, 'click', function (mouseEvent: any) {
//좌표정보를 가지고 상세 주소를 가져오는 함수.
searchDetailAddrFromCoords(
mouseEvent.latLng,
function (result: any, status: any) {
if (status === kakao.maps.services.Status.OK) {
//법정 주소가 있으면 법정주소를 가져오고 없으면 도로명주소를 가져오도록한다.(이런 경우는 거의 없다)
let detailAddr = !!result[0].address.address_name
? result[0].address.address_name
: result[0].road_address.address_name;
// 마커를 클릭한 위치에 표시합니다
marker.setPosition(mouseEvent.latLng);
marker.setMap(map);
let latlng = mouseEvent.latLng;
//타겟의 좌표와 법정주소로 주소정보를 업데이트하고, 최종적으로 이 주소로 게시물을 검색한다.
setTargetCoord({
lat: latlng.getLat(),
lng: latlng.getLng(),
address: detailAddr,
});
}
}
);
});
//좌표정보로 상세 주소 정보를 요청하는 api
function searchDetailAddrFromCoords(
coords: any,
displayMarkerOnClick: (result: any, status: any) => void
) {
// 좌표로 법정동 상세 주소 정보를 요청합니다
geocoder.coord2Address(
coords.getLng(),
coords.getLat(),
displayMarkerOnClick
);
}
};
search page코드는 너무 길어서 지도기능과 관련된 코드만..!
const Search = () => {
const router = useRouter();
const [targetCoord, setTargetCoord] = useState<any>({
lat: 37.517331925853,
lng: 127.047377408384,
address: '서울 강남구',
});
const [isSearch, setIsSearch] = useState(false);
const [center, setCenter] = useState<any>({
lat: 37.517331925853,
lng: 127.047377408384,
address: '서울 강남구',
});
const [error, setError] = useState({ code: 0, message: '' });
const [searchAddress, setSearchAddress] = useState('');
const [selectedAddressBook, setSelectedAddressBook] = useState<any>({
address: '',
latitude: '',
locationId: -1,
locationName: '',
longitude: '',
memberId: 0,
nickName: '',
});
const handleSearchAddress = (e: {
target: { value: React.SetStateAction<string> };
}) => {
setSearchAddress(e.target.value);
};
//search 페이지 컴포넌트가 렌더링 되었을 때 실행되는 함수. 현재정보를 받아오는 api함수다.
//getCurrentLocation에 setCenter를 전달해서 위치 정보를 업데이트 한다.
useEffect(() => {
getCurrentLocation(setCenter, setError);
setIsSearch((prev) => !prev);
}, []);
//center의 좌표가 바뀔 때 혹은 위치 검색을 했을 때 실행되어 center의 위치정보에 따라
//지도와 marker를 렌더링해주고,최종적으로 사용할 위치 정보를 targetCoord에 담아놓는다.
useEffect(() => {
exchangeCoordToAddress(center, setTargetCoord);
}, [center.lat, center.lng, isSearch]);
...중략
return하는 부분
<FormButton
variant="contained"
className="bg-[#63A8DA] text-[white] ml-[10px] h-[52px] screen-maxw672:px-[0.625rem]
screen-maxw430:ml-0 screen-maxw430:w-[48%]"
content="주소검색"
onClick={() => {
setSelectedAddressBook({});
setIsSearch((prev) => !prev);
//검색 버튼 눌렀을 때 searchMap api 실행
//Input에도 onKeyDown이벤트를 바인딩해서 엔터를 입력해도 검색하도록 구현함
searchMap(searchAddress, setCenter);
}}
></FormButton>
검색 키워드를 이용해 검색한 위치를 보여주기
이번엔 검색을 하고 검색한 키워드를 기반으로 상세주소, 좌표를 받아온다. 방배동을 검색하면 서울 서초구 방배동 주소가 입력되는 것을 볼 수 있다.
//setCenter를 parameter로 받아 center 정보를 업데이트시켜 exchangeCoordToAddress 함수를 실행시켜
//지도와 마커를 렌더링한다. exchangeCoordToAddress는 어차피 setTargetCoord를 parameter로 받아
//center정보와 동일하게 targetCoord정보를 업데이트 시킨다.
export const searchMap = (searchAddress: string, setCenter: any) => {
const geocoder = new kakao.maps.services.Geocoder();
let switchLocationToCoordinate = function (result: any, status: any) {
if (status === kakao.maps.services.Status.OK) {
const newSearch = result[0];
setCenter({
lat: newSearch.y,
lng: newSearch.x,
address: newSearch.address_name,
});
}
};
geocoder.addressSearch(`${searchAddress}`, switchLocationToCoordinate);
};
주소록으로 빠른 검색
이건 마이페이지 주소록 등록 블로깅할 때 같이 쓰겠슴다..
정리
여기까지가 지도를 이용한 위치검색 기능이다. 이 검색페이지를 통해서 게시물을 검색할 수 있고, 검색결과로 게시물 리스트를 볼 수 있다. 다음 편은 내주변 목록페이지에서 구현한 지도기능!
'코드스테이츠' 카테고리의 다른 글
[메인 프로젝트] kakao map api 리팩토링하기(feat. 함수 추출하기) (6) | 2023.03.04 |
---|---|
[메인프로젝트] 지도 기능(마커 클러스터) - 내 주변 페이지(feat. kakao map) (3) | 2023.03.03 |
[메인 프로젝트] 메인프로젝트 회고 - 디자인 패턴(feat. atomic) (5) | 2023.02.14 |
[메인프로젝트] 메인프로젝트 회고 - 서비스 기획, 디자인, 마크업 (6) | 2023.02.08 |
[메인 프로젝트] 메인 프로젝트 회고 - 팀빌딩 (2) | 2023.02.05 |