스파르타코딩클럽 내일배움캠프 TIL75

한재창·2023년 2월 13일
0

최종프로젝트

kakaoMap 라이브러리

  • 카카오맵을 react-kakao-maps-sdk 라이브러리를 사용하여 다시 수정하였다.
  • 사용 이유
    • 공식 문서에 튜토리얼과 사용방법이 상세하게 나와있다.
    • 기존 코드보다 사용하기 편하고 직관적이다.
    • 카카오톡 api는 리액트를 사용함에도 불구하고 자바스크립트 용어를 써야했는데 라이브러리는 그 점이 없다.
// 기존 코드

import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import styled from 'styled-components';

// map를 호출해서 detailData.lat, detailData.lon의 위치를 마커표시해준다.

const DetailMap = () => {
  const MapRef = useRef(null);
  const { state: detailData } = useLocation();

  const detailLocation = new window.kakao.maps.LatLng(
    detailData.lat,
    detailData.lon,
  );

  useEffect(() => {
    let options = {
      //지도를 생성할 때 필요한 기본 옵션
      center: detailLocation, //지도의 중심좌표.
      level: 3, //지도의 레벨(확대, 축소 정도)
    };

    let map = new window.kakao.maps.Map(MapRef.current, options); //지도 생성 및 객체 리턴

    const zoomControl = new window.kakao.maps.ZoomControl();
    map.addControl(zoomControl, window.kakao.maps.ControlPosition.RIGHT);

    new window.kakao.maps.Marker({
      map,
      position: detailLocation,
    });
  }, []);

  return <DetailPageMap ref={MapRef}></DetailPageMap>;
};

export default DetailMap;

const DetailPageMap = styled.div`
  width: 500px;
  height: 500px;
`;
// 라이브러리 사용 코드

import { useLocation } from 'react-router-dom';
import styled from 'styled-components';
import { Map, MapMarker } from 'react-kakao-maps-sdk';

// map를 호출해서 detailData.lat, detailData.lon의 위치를 마커표시해준다.

const DetailMap = () => {
  const { state: detailData } = useLocation();

  const detailLocation = {
    lat: detailData.lat,
    lng: detailData.lon,
  };

  return (
    // Props center = 지도 중심
    <DetailPageMap center={detailLocation}>
      // Props position = 마커 표시 위치
      <MapMarker position={detailLocation}>
        <div>{detailData.title}</div>
      </MapMarker>
    </DetailPageMap>
  );
};

export default DetailMap;

const DetailPageMap = styled(Map)`
  width: 500px;
  height: 500px;
`;

지도로 주변위치 키워드 검색

  • 카카오톡 api에서 기본적으로 제공해주는 기능으로 '이태원 맛집' 이라는 키워드로 검색하면 그와 관련된 data를 배열값으로 받을 수 있다.
 // new kakao.maps.services.Places(); 키워드로 object를 반환해준다.
    const ps = new kakao.maps.services.Places();

    // ps.keywordSearch(검색어, (키워드 데이터 [], 검색 상태 OK 여부, total count, page 수))
    ps.keywordSearch('이태원 맛집', (data, status, _pagination) => {

네이버 api와 카카오맵 데이터 연동

  • 키워드 검색을 하면 카카오맵에서 data를 제공해준다고 위에서 말했는데 data에서 이미지 파일이 없어서 네이버 api와 연동하였다.
  • 함수 onSubmit 은 위치를 검색했을 때 지도에 data를 가져와 마커표시해주는 함수이다.
  • markers.push 를 해줄 때 네이버 api의 params의 query 값에 data의 상호명을 넣어 이미지 값을 검색하게 하였다.
  • 검색한 이미지 데이터 값을 markers.push 할 때 같이 넣어주었다.
  • 문제점
    • 맨 처음 api를 사용할 때 CORS Error가 발생하였다.
      • package.json 파일에서 "proxy" :"https://openapi.naver.com" 문구를 추가해주었다.
      • 배포할 땐 안된다고 해서 다른 방법을 찾아야한다.
    • for 문을 통해 여러번 요청하다 보니 과도한 요청이라는 Error 발생
      • 아직 해결하지 못했다.
import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Map, MapMarker } from 'react-kakao-maps-sdk';
import { useRecoilState, useRecoilValue } from 'recoil';
import styled from 'styled-components';
import { MapSearchValue } from '../../../atoms';
import MapModal from './MapModal';

interface IMap {
  position: {
    lat: string;
    lng: string;
  };
  content: string;
}

const Maps = () => {
  const [info, setInfo] = useState<any>();
  const [markers, setMarkers] = useState<any>([]);
  const [map, setMap] = useState<any>();
  const [searchValue, setSearchValue] = useRecoilState(MapSearchValue);

  const searchValueChangeHandler = (
    event: React.FormEvent<HTMLInputElement>,
  ) => {
    setSearchValue({ value: event.currentTarget.value });
  };

  const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    // new kakao.maps.services.Places(); 키워드로 검색하면 object를 반환해준다.
    const ps = new kakao.maps.services.Places();

    // ps.keywordSearch(검색어, (키워드 데이터 [], 검색 상태 OK 여부, total count, page 수))
    ps.keywordSearch(searchValue.value, (data, status, _pagination) => {
      if (status === kakao.maps.services.Status.OK) {
        // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
        // LatLngBounds 객체에 좌표를 추가합니다
        const bounds = new kakao.maps.LatLngBounds();
        let markers: any = [];
        console.log('ok');
        // 받은 데이터 중에 사용할 것들만(lat, lng, content, address, category, img(이미지가 없다.)) markers에 push 해준다.
        // useState로 관리해주고 있는 markers에 set 해준다.
        // img 해결 방법 : 네이버 api를 사용해서 search keyword와 같은 값의 img들을 가져와서 markers에 push 해준다.
        getSearchKeyWord().then(async () => {
          const NAVER_CLIENT_ID = 'ZDK5Gc_XwH219r8fwyIt';
          const NAVER_CLIENT_SECRET = 'VRu_0jKjhT';

          for (let i = 0; i < data.length; i++) {
            const {
              data: { items },
            } = await axios.get('/v1/search/image', {
              params: { query: data[i].place_name, start: 1, display: 1 },
              headers: {
                'X-Naver-Client-Id': NAVER_CLIENT_ID,
                'X-Naver-Client-Secret': NAVER_CLIENT_SECRET,
              },
            });
            console.log('items.link', items);
            // @ts-ignore
            markers.push({
              position: {
                lat: data[i].y,
                lng: data[i].x,
              },
              content: data[i].place_name,
              address: data[i].address_name,
              category: data[i].category_group_name,
              img: items[0].link,
            });
            // @ts-ignore
            bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
          }

          setMarkers(markers);

          // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
          map.setBounds(bounds);
        });
      }
    });
  };

  useEffect(() => {
    if (!map) return;
  }, []);

  const getSearchKeyWord = () => {
  };

  const [openModal, setOpenModal] = useState(false);

  return (
    <>
      {/* <Wrap id="map" ref={MapRef} /> */}
      {/* <button onClick={() => setOpenModal((prev) => !prev)}>모달</button>
      {openModal && <MapModal />} */}
      <form onSubmit={onSubmit}>
        <input
          onChange={searchValueChangeHandler}
          type="text"
          value={searchValue.value}
        />
      </form>

      <Map // 로드뷰를 표시할 Container
        center={{
          lat: 37.566826,
          lng: 126.9786567,
        }}
        style={{
          width: '100%',
          height: '350px',
        }}
        level={3}
        onCreate={setMap}
      >
        {markers.map((marker: any) => (
          <MapMarker
            key={`marker-${marker.content}-${marker.position.lat},${marker.position.lng}`}
            position={marker.position}
            onClick={() => setInfo(marker)}
          >
            {info && info.content === marker.content && (
              <div style={{ color: '#000' }}>{marker.content}</div>
            )}
          </MapMarker>
        ))}
      </Map>
    </>
  );
};

export default Maps;

const Wrap = styled.div`
  background-color: grey;
  width: 700px;
  height: 600px;
`;
profile
취준 개발자

0개의 댓글