개요
이 튜토리얼에서는 @googlemaps/react-wrapper를 사용하여 React 애플리케이션에 지도와 마커를 추가하고 지도와 마커를 애플리케이션 상태에 통합하는 방법을 설명합니다.
@googlemaps/react-wrapper 설치
@googlemaps/react-wrapper 라이브러리를 설치하고 사용하여 구성요소가 렌더링될 때 Maps JavaScript API를 동적으로 로드합니다.
npm install @googlemaps/react-wrapper
이 라이브러리는 다음 코드를 사용하여 가져오고 사용할 수 있습니다.
import { Wrapper, Status } from "@googlemaps/react-wrapper";
이 구성요소의 기본 사용법은 Maps JavaScript API에 종속되는 하위 구성요소를 래핑하는 것입니다. Wrapper
구성요소는 구성요소를 로드하거나 Maps JavaScript API를 로드하는 중에 발생하는 오류를 처리하기 위한 render
속성을 허용합니다.
const render = (status: Status) => {
return <h1>{status}</h1>;
};
<Wrapper apiKey={"YOUR_API_KEY"} render={render}>
<YourComponent/>
</Wrapper>
지도 구성요소 추가
지도를 렌더링하는 기본 기능 구성요소는 useRef
, useState
, useEffect
React 후크를 사용할 가능성이 큽니다.
초기 지도 구성요소에는 다음과 같은 서명이 있습니다.
const Map: React.FC<{}> = () => {};
google.maps.Map
에는 Element
가 생성자 매개변수로 필요하므로 구성요소의 전체 기간 동안 지속되는 변경 가능한 객체를 유지하려면 useRef가 필요합니다. 다음 스니펫은 Map
구성요소 본문의 useEffect 후크 내에서 지도를 인스턴스화합니다.
TypeScript
const ref = React.useRef<HTMLDivElement>(null); const [map, setMap] = React.useState<google.maps.Map>(); React.useEffect(() => { if (ref.current && !map) { setMap(new window.google.maps.Map(ref.current, {})); } }, [ref, map]);
자바스크립트
const ref = React.useRef(null); const [map, setMap] = React.useState(); React.useEffect(() => { if (ref.current && !map) { setMap(new window.google.maps.Map(ref.current, {})); } }, [ref, map]);
위의 useEffect
후크는 ref
가 변경된 경우에만 실행됩니다. 이제 Map
구성요소가 다음을 반환합니다.
return <div ref={ref} />
추가 속성을 사용하여 지도 구성요소 확장
기본 지도 구성요소는 지도 옵션, 이벤트 리스너, 지도가 포함된 div에 적용되는 스타일을 위한 추가 속성을 사용하여 확장할 수 있습니다. 다음 코드는 이 기능 구성요소의 확장된 인터페이스를 보여줍니다.
interface MapProps extends google.maps.MapOptions {
style: { [key: string]: string };
onClick?: (e: google.maps.MapMouseEvent) => void;
onIdle?: (map: google.maps.Map) => void;
}
const Map: React.FC<MapProps> = ({
onClick,
onIdle,
children,
style,
...options
}) => {}
style
객체는 직접 전달하고, 렌더링된 div
에서 속성으로 설정할 수 있습니다.
return <div ref={ref} style={style} />;
onClick
, onIdle
, google.maps.MapOptions
에는 google.maps.Map
에 업데이트를 반드시 적용하기 위한 useEffect
후크가 필요합니다.
TypeScript
// because React does not do deep comparisons, a custom hook is used // see discussion in https://github.com/googlemaps/js-samples/issues/946 useDeepCompareEffectForMaps(() => { if (map) { map.setOptions(options); } }, [map, options]);
자바스크립트
// because React does not do deep comparisons, a custom hook is used // see discussion in https://github.com/googlemaps/js-samples/issues/946 useDeepCompareEffectForMaps(() => { if (map) { map.setOptions(options); } }, [map, options]);
속성으로 전달된 핸들러가 업데이트된 경우 기존 리스너를 지우려면 이벤트 리스너에 약간 더 복잡한 코드가 필요합니다.
TypeScript
React.useEffect(() => { if (map) { ["click", "idle"].forEach((eventName) => google.maps.event.clearListeners(map, eventName) ); if (onClick) { map.addListener("click", onClick); } if (onIdle) { map.addListener("idle", () => onIdle(map)); } } }, [map, onClick, onIdle]);
자바스크립트
React.useEffect(() => { if (map) { ["click", "idle"].forEach((eventName) => google.maps.event.clearListeners(map, eventName) ); if (onClick) { map.addListener("click", onClick); } if (onIdle) { map.addListener("idle", () => onIdle(map)); } } }, [map, onClick, onIdle]);
마커 구성요소 빌드
마커 구성요소는 useEffect
및 useState
후크가 있는 지도 구성요소와 유사한 패턴을 사용합니다.
TypeScript
const Marker: React.FC<google.maps.MarkerOptions> = (options) => { const [marker, setMarker] = React.useState<google.maps.Marker>(); React.useEffect(() => { if (!marker) { setMarker(new google.maps.Marker()); } // remove marker from map on unmount return () => { if (marker) { marker.setMap(null); } }; }, [marker]); React.useEffect(() => { if (marker) { marker.setOptions(options); } }, [marker, options]); return null; };
자바스크립트
const Marker = (options) => { const [marker, setMarker] = React.useState(); React.useEffect(() => { if (!marker) { setMarker(new google.maps.Marker()); } // remove marker from map on unmount return () => { if (marker) { marker.setMap(null); } }; }, [marker]); React.useEffect(() => { if (marker) { marker.setOptions(options); } }, [marker, options]); return null; };
google.maps.Map
이 DOM 조작을 관리하므로 구성요소에서 null을 반환합니다.
지도의 하위 구성요소로 마커 추가
지도에 마커를 추가하기 위해 Marker
구성요소가 다음과 같이 특수 children
속성을 사용하여 Map
구성요소에 전달됩니다.
<Wrapper apiKey={"YOUR_API_KEY"}>
<Map center={center} zoom={zoom}>
<Marker position={position} />
</Map>
</Wrapper>
google.maps.Map
객체를 추가 하위 속성으로 모든 하위 요소에 전달하려면 Map
구성요소의 출력을 약간 변경해야 합니다.
TypeScript
return ( <> <div ref={ref} style={style} /> {React.Children.map(children, (child) => { if (React.isValidElement(child)) { // set the map prop on the child component // @ts-ignore return React.cloneElement(child, { map }); } })} </> );
자바스크립트
return ( <> <div ref={ref} style={style} /> {React.Children.map(children, (child) => { if (React.isValidElement(child)) { // set the map prop on the child component // @ts-ignore return React.cloneElement(child, { map }); } })} </> );
지도 및 애플리케이션 상태 연결
onClick
및 onIdle
콜백에 위의 패턴을 사용하면 애플리케이션을 확장하여 지도 클릭 또는 화면 이동과 같은 사용자 작업을 완전히 통합할 수 있습니다.
TypeScript
const [clicks, setClicks] = React.useState<google.maps.LatLng[]>([]); const [zoom, setZoom] = React.useState(3); // initial zoom const [center, setCenter] = React.useState<google.maps.LatLngLiteral>({ lat: 0, lng: 0, }); const onClick = (e: google.maps.MapMouseEvent) => { // avoid directly mutating state setClicks([...clicks, e.latLng!]); }; const onIdle = (m: google.maps.Map) => { console.log("onIdle"); setZoom(m.getZoom()!); setCenter(m.getCenter()!.toJSON()); };
자바스크립트
const [clicks, setClicks] = React.useState([]); const [zoom, setZoom] = React.useState(3); // initial zoom const [center, setCenter] = React.useState({ lat: 0, lng: 0, }); const onClick = (e) => { // avoid directly mutating state setClicks([...clicks, e.latLng]); }; const onIdle = (m) => { console.log("onIdle"); setZoom(m.getZoom()); setCenter(m.getCenter().toJSON()); };
이러한 후크는 위도 입력과 같이 다음 패턴을 사용하여 양식 요소에 통합할 수 있습니다.
<label htmlFor="lat">Latitude</label>
<input
type="number"
id="lat"
name="lat"
value={center.lat}
onChange={(event) =>
setCenter({ ...center, lat: Number(event.target.value) })
}
/>
마지막으로 애플리케이션은 클릭을 추적하고 각 클릭 위치에서 마커를 렌더링할 수 있습니다.
{clicks.map((latLng, i) => (<Marker key={i} position={latLng} />))}
코드 살펴보기
아래의 온라인 코드 플레이그라운드를 통하거나 git 저장소를 클론하여 전체 샘플 코드를 살펴볼 수 있습니다.
샘플 사용해 보기
샘플 클론
이 샘플을 로컬로 실행하려면 Git 및 Node.js가 필요합니다. 다음 안내에 따라 Node.js와 NPM을 설치합니다. 다음 명령어는 샘플 애플리케이션을 클론하고 종속 항목을 설치하고 시작합니다.
git clone -b sample-react-map https://github.com/googlemaps/js-samples.git
cd js-samples
npm i
npm start
sample-SAMPLE_NAME
으로 시작하는 분기로 전환하면 다른 샘플을 사용해 볼 수 있습니다.
git checkout sample-SAMPLE_NAME
npm i
npm start