React

 

 

구글맵 지도 기능 구현 순서

 

  •  

  • 1. Google Geocoding API 키 설정:

    • console.cloud.google.com에 접속하여 API 및 서비스로 이동합니다.
    • API 및 서비스 사용 설정을 선택한 후 geocode를 검색하여 활성화하고 API 키를 생성합니다.
    • 생성한 API 키를 .env 파일에 추가합니다. 예: NEXT_PUBLIC_GOOGLE_GEOCODING_API_KEY=YOUR_API_KEY
  •  
  • 2.새 컴포넌트 생성:

    • components 폴더에 PropertyMap.jsx 파일을 생성합니다.
    • PropertyMap 함수 컴포넌트를 만들고 property를 매개변수로 받습니다.
    • 기본적으로 div 태그에 "map" 텍스트를 넣습니다.
    • use client 지시자를 추가합니다.
  •  
  •  
  • 3.필요한 패키지 설치:

    • 터미널에서 다음 명령어를 실행하여 패키지를 설치합니다

 

npm install react-geocode mapbox-gl react-map-gl


 

 

4. 상태 설정:

  • useState를 사용하여 lat, lng, viewport, loading, geocodeError 상태를 정의합니다.
  •  
  • 초기 상태 값을 설정합니다.


 

 

5. Google Geocoding API 설정:
setDefaults 함수를 사용하여 API 키와 기본 설정을 추가합니다.
API 키는 환경 변수에서 가져옵니다: process.env.NEXT_PUBLIC_GOOGLE_GEOCODING_API_KEY

 


6.위도 및 경도 가져오기:
useEffect를 사용하여 컴포넌트가 마운트될 때 fromAddress 함수를 호출하여 주소로부터 위도와 경도를 가져옵니다.
비동기 함수 fetchCoords를 만들어 try-catch-finally 블록을 사용하여 API 요청을 처리합니다.
property 객체에서 주소 정보를 가져와 fromAddress 함수에 전달합니다.
응답에서 위도와 경도를 추출하여 상태를 업데이트합니다.

 


7.로딩 및 에러 처리:
loading 상태가 true일 경우 "loading..." 텍스트를 출력합니다.
geocodeError 상태가 true일 경우 "No location data found" 텍스트를 출력합니다.

 

 

8.PropertyMap 컴포넌트 활용:
상세 페이지 컴포넌트에서 PropertyMap 컴포넌트를 가져와 div 태그 대신 사용하고 property를 전달합니다.

 

 

"use client";
import { PropertyProps } from "@/app/properties/[id]/page";
import React from "react";
import { useEffect, useState } from "react";
import Geocode, { OutputFormat, setDefaults } from "react-geocode";
import Map, { Marker } from "react-map-gl";
import Image from "next/image";
import pin from "@/assets/images/pin.svg";
import Spinner from "@/components/Spinner";
import 'mapbox-gl/dist/mapbox-gl.css' 
import { PropertyCardProps } from "./PropertyCard";


type MarkerProps = {
  latitude: number;
  longitude: number;
  offsetLeft?: number;
  offsetTop?: number;
  draggable?: boolean;
  // Add other properties here
};


const PropertyGoogleMap: React.FC<PropertyCardProps> = ({ property }) => {
  const [lat, setLat] = useState<number | null>(null);
  const [lng, setLng] = useState<number | null>(null);
  const [viewport, setViewport] = useState({
    latitude: 0,
    longitude: 0,
    zoom: 12,
    width: "100%",
    height: "500px",
  });

  const [loading, setLoading] = useState(true);
  const [geocodeError, setGeocodeError] = useState(false);

  setDefaults({
    key: process.env.NEXT_PUBLIC_GOOGLE_GEOCODING_API_KEY,
    language: "ko",
    region: "KR",
    outputFormat: OutputFormat.JSON,
  });

  useEffect(() => {
    // Geocode.setKey(process.env.NEXT_PUBLIC_GOOGLE_GEOCODING_API_KEY as string);
    // Geocode.setLanguage("ko");
    // Geocode.setRegion("KR");
    // Geocode.setLocationType("ROOFTOP");

    const fetchCoords = async () => {
      setLoading(true);
      try {

        let addressText=`${property.location.address1} 
            ${property.location.address2} ${property.location.address3} ${property.location.zipcode}`;

        addressText= '경기도 수원시 덕영대로 535번길'
        const res = await Geocode.fromAddress(addressText);
        
        if (res.results.length === 0) {
          setGeocodeError(true);
          return;
        }

        const { lat, lng } = res.results[0].geometry.location;
        setLat(lat);
        setLng(lng);
        setViewport(prevViewport => ({
          ...prevViewport,
          latitude: lat,
          longitude: lng,
        }));
      } catch (error) {
        console.log("에러 =============>",error);
        setGeocodeError(true);
      } finally {
        setLoading(false);
      }
    };

    fetchCoords();
  }, [property]);

  if (loading) return <Spinner loading={loading} />;

  if (geocodeError) {
    return <div className="text-xl">No location data found</div>;
  }

  return (

   !loading && ( <Map
      mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN}
      // mapLib={mapboxgl}
      initialViewState={{
        longitude:lng!,
        latitude:lat!,
        zoom:15
      }}
      style={{ width: 600, height:400 }}
      mapStyle="mapbox://styles/mapbox/streets-v11"
     
    >
      {lat && lng && (
        <Marker latitude={lat} longitude={lng} offset={[-10,-20]}  >
          <Image src={pin} alt="Location Pin" width={40} height={40}/>
        </Marker>
      )}
    </Map>
   )
  );
};

export default PropertyGoogleMap;

 

 

 setDefaults({
    key: process.env.NEXT_PUBLIC_GOOGLE_GEOCODING_API_KEY,
    language: "ko",
    region: "KR",
    outputFormat: OutputFormat.JSON,
  });

 

setDefaults 함수는 react-geocode 라이브러리에 존재하지 않습니다.

대신 react-geocode는 setApiKey, setLanguage, setRegion, setLocationType과 같은 개별적인 설정 함수를 제공합니다.

따라서 setDefaults 대신 개별 설정 함수들을 사용해야 합니다.

 

 

 

 

 

 

 

 

2. Mapbox 지도 표시

 

Mapbox 설정


 

1)Mapbox API 키 설정:

Mapbox에 가입하고 대시보드에서 토큰을 생성합니다.
.env 파일에 NEXT_PUBLIC_MAPBOX_TOKEN으로 저장합니다.

 

NEXT_PUBLIC_MAPBOX_TOKEN=pk.abcdef1234567890

 


2)필요한 패키지 설치:

react-map-gl과 mapbox-gl 패키지를 이미 설치했습니다.

 

 

3)지도 컴포넌트 작성:

PropertyMap 컴포넌트를 수정하여 Mapbox 지도를 표시합니다.
useEffect에서 좌표를 가져와 상태를 업데이트합니다.

 

 

 

지도 렌더링 코드

1)필요한 모듈 가져오기:

"use client";
import { PropertyProps } from "@/app/properties/[id]/page";
import React, { useEffect, useState } from "react";
import Geocode from "react-geocode";
import Map, { Marker } from "react-map-gl";
import Image from "next/image";
import pin from "@/assets/images/pin.svg";
import Spinner from "@/components/Spinner";

 

 

2)PropertyMap 컴포넌트:

const PropertyMap: React.FC<PropertyProps> = ({ property }) => {
  const [lat, setLat] = useState<number | null>(null);
  const [lng, setLng] = useState<number | null>(null);
  const [viewport, setViewport] = useState({
    latitude: 0,
    longitude: 0,
    zoom: 12,
    width: "100%",
    height: "500px",
  });

  const [loading, setLoading] = useState(true);
  const [geocodeError, setGeocodeError] = useState(false);

  useEffect(() => {
    Geocode.setApiKey(process.env.NEXT_PUBLIC_GOOGLE_GEOCODING_API_KEY as string);
    Geocode.setLanguage("ko");
    Geocode.setRegion("KR");
    Geocode.setLocationType("ROOFTOP");

    const fetchCoords = async () => {
      try {
        const res = await Geocode.fromAddress(`${property.location.address1} ${property.location.address2} ${property.location.address3} ${property.location.zipcode}`);

        if (res.results.length === 0) {
          setGeocodeError(true);
          return;
        }

        const { lat, lng } = res.results[0].geometry.location;
        setLat(lat);
        setLng(lng);
        setViewport(prevViewport => ({
          ...prevViewport,
          latitude: lat,
          longitude: lng,
        }));
      } catch (error) {
        console.log(error);
        setGeocodeError(true);
      } finally {
        setLoading(false);
      }
    };

    fetchCoords();
  }, [property]);

  if (loading) return <Spinner />;

  if (geocodeError) {
    return <div className="text-xl">No location data found</div>;
  }

  return (
    <Map
      initialViewState={viewport}
      style={{ width: viewport.width, height: viewport.height }}
      mapStyle="mapbox://styles/mapbox/streets-v11"
      mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_TOKEN}
    >
      {lat && lng && (
        <Marker latitude={lat} longitude={lng} anchor="bottom">
          <Image src={pin} alt="location" width={40} height={40} />
        </Marker>
      )}
    </Map>
  );
};

export default PropertyMap;

 

 

3) CSS 파일 추가:

  • mapbox-gl의 CSS 파일을 임포트하여 스타일 문제를 해결합니다.

 

import 'mapbox-gl/dist/mapbox-gl.css';

 

요약

  • API 키 설정: .env 파일에 Google Geocoding과 Mapbox 키 추가.
  • 패키지 설치: react-geocode, react-map-gl 등 필요한 패키지 설치.
  • 지도 컴포넌트: PropertyMap 컴포넌트 작성, 좌표를 가져와 Mapbox 지도에 마커 표시.
  • CSS 추가: mapbox-gl CSS 파일 임포트.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

인간의 참된 가치는 그가 어느 정도까지 자기 자신에게서 해방될 수 있으며, 또 그가 자기 자신에게서 얻은 그 해방의 의미가 무엇인가에 의해서 주로 결정된다. -아인슈타인

댓글 ( 0)

댓글 남기기

작성