/ REACT

Google Map API React에서 사용하기

1. 시작하기

지도를 사용해야하는 프로젝트가 있어서,

국내뿐아닌 해외도 보기 쉬운 지도 관련 API를 찾다보니

Google maps보다 더 좋은것을 찾기 힘들어

해당 API와 React를 같이 사용하여 작업해봤습니다.

저와 같은 분들을 위해 React로 Google Map API를 사용하는 방법을

작성 해 보겠습니다.

먼저 API를 사용하기 위해 KEY값이 필요하므로

Google Maps Plat form 사이트에 접속하여 KEY값을 생성합니다.

사이트에 접속 하여 다음과 같이 진행하여 키를 생성합니다.

(설정이 따로 되어있지않으면, 결제 페이지가 나옵니다) img

img

img

img

img

그 후 터미널에 다음과 같이 입력하여

React로 프로젝트를 생성후 Google Map API를 설치합니다.

  npm init react-app AppName
  npm install --save google-maps-react
  yarn install

그 후 컨테이너에서 google-maps-react를 import합니다.

  import { Map, GoogleApiWrapper, Marker } from 'google-maps-react';

렌더링할 내용은 다음과 같습니다.

  render() {
    const mapStyles = {
      width: '100%',
      height: '100%',
    };
    return (
      <Fragment>
        <Map
          google={this.props.google}
          zoom={18}
          style={mapStyles}
          initialCenter=
        >
        </Map>
      </Fragment>
    );
  }

또한 API를 사용하기 위한 KEY값을 설정하기 위해

export에 받아온 값을 저장합니다.

  export default GoogleApiWrapper({
    apiKey: 'YOUR GOOGLE MAPS API KEY 입력'
  })(YourCntName);

2. 사용 방법

  1. HandlerEvnet

기본적으로 핸들러들의 형태는

on(HandlerEventType) 의 형태로 되어있으며,

다음과 같이 이벤트를 추가 할수 있습니다.

  <Map
    google={this.props.google}
    onClick ={this.onEventChecker}
  />

파라미터는 총 3개입니다.

e는 html element

aug는 Map에 설정된 데이터 값

geo는 좌표값을 받을수 있는 함수를 넘겨줍니다.

  onEventChecker = (e, aug, geo) => {
    console.log(e);
    console.log(aug);
    console.log(geo);
  }

이러한 핸들러 이벤트를 활용하여

지도에 마커를 추가하는 함수를 생성해 봅시다.

  addMarkers = async (e, aug, geoData) => {
    // console.log(aug);
    const {stores} = this.state;
    let stateData = stores;
    let latLng;
    latLng = {latitude : geoData.latLng.lat(), longitude : geoData.latLng.lng()};
    stateData.push(latLng);
    await this.setState({
      stores: stateData
    });
  }

그리고 배열을 그려주는 함수를 생성하여

state의 배열이 추가될때마다 마커를 추가합니다.

  displayMarkers = () => {
    return this.state.stores.map((store, index) => {
      return <Marker key={index} id={index} position= label={store.title}
     onClick={() => this.removeMarkers(index)} />
    })
  }
  render() {
    const mapStyles = {
      width: '100%',
      height: '100%',
    };
    return (
      <Fragment>
        <Map
          google={this.props.google}
          zoom={18}
          style={mapStyles}
          initialCenter=
          onClick={this.addMarkers}
        >
          {this.displayMarkers()}
        </Map>
      </Fragment>
    );
  }

마커를 그려주는 함수를 잘 보시면,

마커를 클릭시 해당 마커를 지울수 있도록 생성된 걸

확인 할 수 있습니다.

해당 함수의 코드는 다음과 같습니다.

  removeMarkers = async (i) => {
    const {stores} = this.state;
    let stateData = stores;
    stateData.splice(i,1);
    await this.setState({
      stores: stateData
    })
  }

여기까지 작성한 예제 코드를 실행 한 후

화면은 아래와 같습니다. img

해당 화면에서 클릭 이벤트를 실행시.. img

img

  1. 주소 검색

먼저 검색을 할 input과 button을 렌더링합니다.

  주소/건물 : <input value={this.state.searchText} onChange={(e) => this.onChangeInput(e)} />
  <button onClick={()=>this.onSearchEvent()}>검색</button>

그 후 state에 검색할 데이터를

가지고 있는 searchText를 만들어주며,

검색 함수를 생성합니다.

  onChangeInput = (e) => {
    this.setState({
      searchText: e.target.value
    })
  }

  onSearchEvent = () => {
    const {google} = this.props;
    const {searchText} = this.state
    let geocoder = new google.maps.Geocoder();
    geocoder.geocode({
      address: searchText,
      region: 'ko'
    }, function (results, status) {
      if (status === google.maps.GeocoderStatus.OK) {
        console.log("RESULT OK::통신 성공");
        console.log(results);        
      } else if (status === google.maps.GeocoderStatus.ERROR) {
        console.log("Error::server 통신 에러");
      } else if (status === google.maps.GeocoderStatus.INVALID_REQUEST) {
        console.log("Invalid request::검색어없음");
      } else if (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
        console.log("Over query limit::너무 잦은 통신");
      } else if (status === google.maps.GeocoderStatus.REQUEST_DENIED) {
        console.log("Geo code denied::geoCode 이용불가 API KEY 확인");
      } else if (status === google.maps.GeocoderStatus.UNKNOWN_ERROR) {
        console.log("Unknown error::알수없는 에러 발생");
      } else if (status === google.maps.GeocoderStatus.ZERO_RESULTS) {
        console.log("Zero result::찾는 데이터와 일치하는 데이터 존재하지않음.");
      } else {
        console.log("unknown status error..");
      }
    })
  }

다음은 해당 함수를 통해

부평구를 검색했을때 콜백받은 result의 값입니다. img

formatted_address 값이 주소명이며,

좌표를 가져올 수 있는 함수 또한 포함 되어 있음을

확인 할 수 있습니다.

  1. InfoWindow

좌표에 설명하기 위한 라벨이아닌,

메모같은 정보창을 표시할 수 있는

InfoWindow가 존재합니다.

먼저 InfoWindow를 import 합니다.

  import { Map, GoogleApiWrapper, Marker, InfoWindow } from 'google-maps-react';

렌더링 될 때

엘리먼트로 넣어주면 사용이 가능합니다.

  <InfoWindow
    visible= {Boolean}
    position= 
    content= {String}
    onClose= {function}
  >
  </InfoWindow> 
  // 예제에 사용되는 매개변수와 타입입니다.

onClose 같은 경우에는 기본 함수가 있으며

visible의 값을 false 로 바꿔줍니다.

이번에 사용할 방법은 Marker를 클릭시

infoWindow를 활성화 시키며,

close시 비활성화 시킬수 있도록 만들어 보겠습니다.

먼저 Marker의 onClick과

InfoWindow의 onClose에 들어갈 함수를 생성합니다.

  visibleInfoWindow = async (i) => {
    const {stores} = this.state;
    let stateData = stores;
    stateData[i].bool = !stateData[i].bool 
    await this.setState({
      stores: stateData
    })
  }

기존에 state의 저장 했던

stores의 키값에는 visible에 들어갈

boolean 값이 없으므로 추가해줍니다.

  const latLng = {latitude:lat, longitude:lng, title: address, bool: false};

Marker를 클릭시 InfoWindow가 보여야하므로

onClick을 바꾸며,

InfoWindow의 경우

Marker 를 그리는 함수와 동일 한 방식으로

출력할 수 있도록 그리는 함수를 생성합니다.

  displayMarkers = () => {
    return this.state.stores.map((store, index) => {
      return (
        <Marker 
          key={index}
          position= 
          onClick={() => this.visibleInfoWindow(index)} 
        />
      );
    })
  }
  displayInfoWindows = () => {
    return this.state.stores.map((store, index) => {
      return (
        <InfoWindow
          key={index}
          visible={store.bool}
          position= 
          content={store.title}
          onClose={()=>this.visibleInfoWindow(index)}
        >
        </InfoWindow>
      );
    })
  }

그 후 렌더링 되는 Map 안에

다음과 같이 추가합니다

  {this.displayInfoWindows()}

예제 코드 실행시 아래와 같은 그림으로 실행됩니다. img

해당 마커를 클릭하면.. img

이렇게 InfoWindow가 출력되는 것을 확인할수 있습니다.

3. 확대/축소 이벤트

이번에는 ref를 활용하여 Map의 데이터를 가져온 후

마우스 휠의 이벤트를 감지하여

state의 값으로 저장하도록 만들어 보겠습니다.

먼저 이벤트를 감지할 수 있도록 세팅합니다.

  componentDidMount = () => {
    window.addEventListener("DOMMouseScrol", this.onWheelEvent, false);
    window.onmousewheel = document.onmousewheel = this.onWheelEvent;
  }

그 후 마우스가 감지되었을때 사용할 이벤트인

onWheelEvent 를 생성합니다.

  onWheelEvent = (event) =>{
    var delta = 0;
    if (!event) event = window.event;
    if (event.wheelDelta) {
        delta = event.wheelDelta/120;
        if (window.opera) delta = -delta;
    } else if (event.detail) delta = -event.detail/3;
    if (delta) this.onWheelHandler(delta);
  }

해당 이벤트가 감지되었을때 처리된 값을

받아줄 핸들러를 생성합니다.

  onWheelHandler = (delta) => {
    let map = this.mapRef.map;
    console.log("lat::"+map.center.lat());
    console.log("lng::"+map.center.lng());
    console.log("zoom::"+map.zoom);
    if (delta < 0) {
      console.log("wheel down");
    }
    else {
      console.log("wheel up");
    };
    this.setState({
      zoomIndex: map.zoom
    });
  }

참고로 리액트의 ref 사용법은

다음과 같이 사용됩니다

  ref={ref => this.mapRef = ref}

이런식으로 속성값을 태그에 넣어주시면 ref값이 저장되어

해당 엘리먼트를 확인 가능합니다.

이경우 결과 값은 다음 사진과 같습니다. img

다만 해당 방식으로 처리할 경우엔 스마트폰의 확대 및 축소

감지를 하지 못하게 되어 원래

사용되던 onZoomChanged 함수를 사용 하였으나,

작동되지않아 해당 방식으로 web에서나마

처리되도록 만들어 보았습니다.

JAVASCRIPT, REACT, NODE, GOOGLE, MAP