당근마켓의 내 동네 설정 기능을 보면,
하단 바를 움직일 때마다 지도에 표시된 영역의 넓이와 지도 스케일이 변하는 것을 볼 수 있다.
당근마켓처럼 지역 특성에 맞추어 자세한 다각형으로 표현하는 것은 무리고,
지역별로 중심 좌표를 정하고, 이를 중심으로 한 원을 그리는 것을 한 번 해보기로 했다.
0. Google Maps API 키 받기
가입해서 키를 발급 받으면 된다.
https://developers.google.com/maps?hl=ko
Google Maps Platform | Google for Developers
Google Maps Platform 설명
developers.google.com
1. 설치하기
Google Maps API Docs는 순수 javascript로 설명을 해주고 있기 때문에,
react나 next를 쓰고 있다면, 외부 패키지를 쓰는 것이 편하다.
나는 goolge-map-react 라는 패키지를 사용했다.
https://www.npmjs.com/package/google-map-react
google-map-react
Isomorphic component that allows rendering react components on a google map. Latest version: 2.2.1, last published: a month ago. Start using google-map-react in your project by running `npm i google-map-react`. There are 351 other projects in the npm regis
www.npmjs.com
2. 지도 삽입하기
굉장히 간단하다.
GoogleMapReact 컴포넌트에 props를 전달하는 방식으로 사용하면 된다.
- defaultCenter : 지도가 처음 로딩될 때, 중심점의 위/경도 기본 값
- defaultZoom : 지도가 처음 로딩될 때, 지도 스케일의 기본 값
- bootstrapURLKeys : API 키는 .env 파일에 보관하면 될 것 같다.
import React from "react";
import GoogleMapReact from "google-map-react";
export default function SimpleMap() {
const defaultProps = {
center: {
lat: 35.16818211103561,
lng: 126.88903526058037,
},
zoom: 15,
};
return (
<div style={{ height: "100vh", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{ key: "API 키" }}
defaultCenter={defaultProps.center}
defaultZoom={defaultProps.zoom}
/>
</div>
);
}
3. 원 그리기 (Circles)
Google Maps API가 로딩될 때 지도 위에 추가적인 스타일링 요소(ex. 다각형, 원, 마커 등)들이 반영되게 하기 위해서,
GoogleMapReact 컴포넌트 내 onGoogleApiLoaded에 스타일링 요소 설정 용 함수를 props로 전달해주었다.
const handleApiLoaded = ({ map }: any) => {
setMap(map);
const newCircle = new google.maps.Circle({
strokeOpacity: 0,
strokeWeight: 2,
fillColor: "orange",
fillOpacity: 0.3,
map,
center: center,
radius: radius,
});
setCircle(newCircle);
};
...
<GoogleMapReact
...
onGoogleApiLoaded={handleApiLoaded}
></GoogleMapReact>
4. 중심 마커 원하는 모양으로 변경하기 (Custom Markers)
왼쪽 당근마켓 지도(외에도 여러 지도)를 보면, 여러 모양의 마커들이 있는 것을 확인할 수 있었다. 하지만 구글 맵에서 기본적으로 제공해주는 마커는 오른쪽과 같은 모양이다. (그렇지만 이 모양은 못 생겼기에) 다른 모양의 마커를 쓰고 싶었다.
이를 위해서, 기본 마커 대신 사용할 마커의 이미지 파일을 url에 넣어주었다.
const handleApiLoaded = ({ map }: any) => {
...
new google.maps.Marker({
position: new google.maps.LatLng(center.lat, center.lng),
icon: {
url: "이미지 파일 경로",
scaledSize: new google.maps.Size(20, 20),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(10, 10),
},
map,
});
};
이 때 마커 이미지의 scaledSize에 따라 origin과 anchor를 잘 설정해주지 않으면, zoom level이 변화할 때 마커의 위치가 따로 노는 문제가 발생한다.
해당 문제 해결을 위해서는 Marker의 icon 항목을 설정해줄 때 변수 값들을 살펴볼 필요가 있다.
내가 지정해준 원형 마커의 사이즈는 20이니까 중심의 좌표는 (10,10) 이 될 것이다.
이를 anchor에 지정해주면 된다.
만약 원형이 아닌 모양의 마커를 쓴다면, 이미지 중심 좌표가 아닌, 좌표에 꽂히게 할 마커의 좌표를 파악해서 지정해주면 될 것 같다.
5. 사용자가 지정한 값에 따라, 원의 반경과 지도 스케일 바꾸기
리액트 훅인 useState와 useEffect를 사용하면 된다.
const [zoom, setZoom] = useState(14);
const [radius, setRadius] = useState(1000);
change 이벤트가 생길 때마다 setZoom과 setRadius를 이용해서 zoom level 과 원 반경의 값을 변경시킨다.
그리고 이 값이 바뀔 때마다 지도를 업데이트 해주기 위해 useEffect를 사용한다.
useEffect(() => {
if (map && circle) {
circle.setOptions({ radius });
}
}, [radius, map, circle]);
🔽 전체 코드
import React, { useEffect, useState } from "react";
import GoogleMapReact from "google-map-react";
type Props = {
zone: number;
};
const DisplayMap = ({ zone }: Props) => {
const [zoom, setZoom] = useState(14);
const [radius, setRadius] = useState(1000);
const [map, setMap] = useState<google.maps.Map | null>(null);
const [circle, setCircle] = useState<google.maps.Circle | null>(null);
const center = {
lat: 35.16818211103561,
lng: 126.88903526058037,
};
const mapOptions = {
scrollwheel: false,
};
useEffect(() => {
if (zone === 1) {
setZoom(14);
setRadius(1000);
} else if (zone === 2) {
setZoom(12);
setRadius(4000);
} else if (zone === 3) {
setZoom(11);
setRadius(9000);
} else if (zone == 4) {
setZoom(10);
setRadius(20000);
} else if (zone === 5) {
setZoom(9);
setRadius(30000);
}
}, [zone]);
useEffect(() => {
if (map && circle) {
circle.setOptions({ radius });
}
}, [radius, map, circle]);
const handleApiLoaded = ({ map }: any) => {
setMap(map);
const newCircle = new google.maps.Circle({
strokeOpacity: 0,
strokeWeight: 2,
fillColor: "orange",
fillOpacity: 0.1,
map,
center: center,
radius: radius,
});
setCircle(newCircle);
new google.maps.Marker({
position: new google.maps.LatLng(center.lat, center.lng),
icon: {
url: "이미지 경로",
scaledSize: new google.maps.Size(20, 20),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(10, 10),
},
map,
});
};
return (
<div style={{ height: "38rem", width: "100vw" }}>
<GoogleMapReact
bootstrapURLKeys={{ key: "구글 맵 api key" }}
center={center}
zoom={zoom}
options={mapOptions}
onGoogleApiLoaded={handleApiLoaded}
></GoogleMapReact>
</div>
);
};
export default DisplayMap;
'Library-Framework > Next.js' 카테고리의 다른 글
[Next.js] 번역 기능 (다국어) 적용하기 (i18next) (0) | 2023.05.16 |
---|---|
[Next.js] 프로젝트 생성하기 (0) | 2022.12.07 |