React - 카카오 지도(kakao Map) API 적용

일상 코딩·2022년 5월 7일
0

React

목록 보기
13/45
post-custom-banner

01.카카오 지도 API Key 발급 받기

  • react로 개발하기 때문에 Web을 선택 합니다.

  • 왼쪽 하단 아래에 있는 키모양 버튼을 눌러서 키 발급 받습니다.

  • 애플리케이션 추가하기 클릭하여 해당 내용을 작성한 후 저장
  • 앱 이름과 사업자명은 아무거나 적어도 상관없음

  • 여기서 그대로 키를 설정하면 안되고 플랫폼 설정하기를 눌러서 주소를 설정해줘야 합니다.

  • 플렛폼 설정하기로 이동 후 Web 선택한 뒤 개발하는 웹 사이트 도메인을 등록합니다.
  • http://localhost:3000

02.카카오 지도 API 적용하기

  • index.html로 이동 (public 폴더 안에 있음)
  • head 영역에 해당 코드 추가 해줍니다.
    <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=이 자리에 본인이 입력받은 키 입력"></script>

Map.js

import React, { useEffect } from "react"

const { kakao } = window

const Map = () => {
  useEffect(() => {
    const container = document.getElementById("myMap")
    const options = {
      center: new kakao.maps.LatLng(33.450701, 126.570667),
      level: 3,
    }
    const map = new kakao.maps.Map(container, options)
  }, [])

  return (
    <div
      id="myMap"
      style={{
        position: "relative",
        top: "50px",
        left: "100px",
        width: "1540px",
        height: "650px",
      }}
</div>
  )
}

export default Map

결과

  • 사진의 줌정도나 지도의 보이는 위치(좌표)를 조절하고싶다면 let option 객체 코드에서
    centerlevel을 조절해주면된다.

줌 & 드래그 막기

map.setDraggable(false); //드래그 막기
map.setZoomable(false); //줌 막기
  • 지도의 줌을 막거나 드래그를 막고싶다면 해당 코드 적용

예시

let map = new window.kakao.maps.Map(container, options);
map.setDraggable(false); //드래그 막기
map.setZoomable(false); //줌 막기
  • let map = new window.kakao.maps.Map(container, options); 의 아래에 적용해주면된다.

03.키워드로 장소 검색 & 검색한 장소 마커 띄우기

프로젝트/src/components/page/api/Map.js

import React, { useEffect, useState } from "react"
import styled from "styled-components"

const { kakao } = window

const Divstyle = styled.div`
  display: flex;
  width: 100%;
`

const ResultStyle = styled.div`
  width: 350px;
  height: 730px;
  overflow: scroll;
  position: absolute;
  bottom: 10px;
  right: 20px;
  border-radius: 30px 0px 0px 0px;
  background-color: gray;
  opacity: 0.7;
  z-index: 1;
  font-size: 17px;
  color: #000000;
`

const Pagination = styled.div`
  margin-top: 3rem;
  a {
    color: black;
    font-size: 20px;
    text-decoration: none;
    margin: 0 10px;
    &on {
      color: lightblue;
      font-weight: bold;
    }
  }
`

const Map = ({ searchPlace }) => {
  // 검색결과 배열에 담아줌
  const [Places, setPlaces] = useState([])

  useEffect(() => {
    var infowindow = new kakao.maps.InfoWindow({ zIndex: 1 })
    var markers = []
    const container = document.getElementById("myMap")
    const options = {
      center: new kakao.maps.LatLng(33.450701, 126.570667),
      level: 3,
    }
    const map = new kakao.maps.Map(container, options)

    const ps = new kakao.maps.services.Places()

    ps.keywordSearch(searchPlace, placesSearchCB)

    function placesSearchCB(data, status, pagination) {
      if (status === kakao.maps.services.Status.OK) {
        let bounds = new kakao.maps.LatLngBounds()

        for (let i = 0; i < data.length; i++) {
          displayMarker(data[i])
          bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x))
        }

        map.setBounds(bounds)
       
        // 페이지 목록 보여주는 displayPagination() 추가
        displayPagination(pagination)
        setPlaces(data)
      }
    }

    // 검색결과 목록 하단에 페이지 번호 표시
    function displayPagination(pagination) {
      var paginationEl = document.getElementById("pagination"),
        fragment = document.createDocumentFragment(), i

      // 기존에 추가된 페이지 번호 삭제
      while (paginationEl.hasChildNodes()) {
        paginationEl.removeChild(paginationEl.lastChild)
      }

      for (i = 1; i <= pagination.last; i++) {
        var el = document.createElement("a")
        el.href = "#"
        el.innerHTML = i

        if (i === pagination.current) {
          el.className = "on"
        } else {
          el.onclick = (function (i) {
            return function () {
              pagination.gotoPage(i)
            }
          })(i)
        }

        fragment.appendChild(el)
      }
      paginationEl.appendChild(fragment)
    }

    function displayMarker(place) {
      let marker = new kakao.maps.Marker({
        map: map,
        position: new kakao.maps.LatLng(place.y, place.x),
      })

      kakao.maps.event.addListener(marker, "click", function () {
        infowindow.setContent(
          '<div style="padding:5px;font-size:12px;">' +
            place.place_name +
            "</div>"
        )
        infowindow.open(map, marker)
      })
    }
  }, [searchPlace])

  return (
    <Divstyle>
      <div
        id="myMap"
        style={{
          width: "1600px",
          height: "820px",
          borderRadius: "30px",
          position: "absolute",
          bottom: "0px",
          left: "0px",
        }}
</div>
      <ResultStyle>
        {Places.map((item, i) => (
          <div key={i} style={{ marginTop: "20px" }}>
            <span>{i + 1}</span>
            <div>
              <h5>{item.place_name}</h5>
              {item.road_address_name ? (
                <div>
                  <span>{item.road_address_name}</span>
                  <span>{item.address_name}</span>
                </div>
              ) : (
                <span>{item.address_name}</span>
              )}
              <span>{item.phone}</span>
            </div>
          </div>
        ))}
        <div id="pagination"></div>
      </ResultStyle>
    </Divstyle>
  )
}

export default Map

프로젝트/src/components/page/Map.js

import React, { useState } from "react"
import styled from "styled-components"
import Map from "./api/Map"

const Container = styled.div`
  width: 1740px;
  height: 970px;
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  background-color: ${(props) => props.theme.reviewPage.backgroundColor};
  @media screen and (min-width: 1920px) {
    width: 90%;
  }
`

const MapContainer = styled.div`
  width: 1600px;
  height: 820px;
  border-width: thick;
  border-color: pink;
  bordr-style: solid;
  border-radius: 30px;
  position: relative;
`

const Search = styled.input`
  width: 300px;
  height: 40px;
  position: absolute;
  top: 10px;
  right: 70px;
  border-radius: 30px 0px 0px 30px;
  z-index: 2;
  font-size: 20px;
`

const SearchForm = styled.form``

const SearchBtn = styled.button`
  width: 50px;
  height: 45px;
  position: absolute;
  top: 10px;
  right: 20px;
  border-radius: 0px 30px 30px 0px;
  z-index: 2;
  background-color: #fff;
  color: #333;
  &:hover {
    cursor: pointer;
    background-color: #333;
    color: #fff;
  }
`

function MapSection() {
  const [InputText, setInputText] = useState("")
  const [Place, setPlace] = useState("")

  const onChange = (e) => {
    setInputText(e.target.value)
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    setPlace(InputText)
    setInputText("")
  }

  return (
    <>
      <div
        className="section"
        style={{
          backgroundColor: `${(props) =>
            props.theme.questionPage.backgroundColor}`,
        }}
>

        <Container>
          <MapContainer>
            <SearchForm className="inputForm" onSubmit={handleSubmit}>
              <Search
                placeholder="검색어를 입력하세요"
                onChange={onChange}
                value={InputText}
              />
              <SearchBtn type="submit">검색</SearchBtn>
            </SearchForm>
            <Map searchPlace={Place} />
          </MapContainer>
        </Container>
      </div>
    </>
  )
}

export default MapSection

실행결과

profile
일취월장(日就月將) - 「날마다 달마다 성장하고 발전한다.」
post-custom-banner

0개의 댓글