Education/Wanted Pre-Onboarding FE Course

Typescript, react-kakao-maps-sdk로 카카오 지도에 마커 찍기

SH_Roh 2022. 6. 4. 22:08
반응형

 

카카오 지도 api를 이용해 검색한 키워드와 관련된 결과를 지도에 마커로 찍어주는 기능을 만들어봤다.

 

이태원 맛집 검색!

 

1. 카카오 개발자 페이지에서 api key 발급

https://developers.kakao.com/

 

 

2. 도메인 등록

메뉴의 플랫폼 -> Web에서 기본 도메인으로 http://localhost:3000을 등록해준다.

 

3. .env 파일을 만들어 key값 따로 저장

이 때 변수명은 꼭 REACT_APP으로 시작하도록 지어줘야 한다.

 

그리고 자바스크립트 키를 넣어줘야 한다!

모든 권한을 갖고 있다길래 Admin 키로 했었는데 계속 안되서 설마하고 자바스크립트 키로 했더니 됐다.

REACT_APP_KAKAO_KEY=카카오 지도 API 자바스크립트 키

 

4. react-kakao-maps-sdk 설치

yarn add react-kakao-maps-sdk

 

5. script 태그 추가

public폴더 안의 index.html의 head태그 안에 아래 내용을 추가해준다.

appkey부분에 본인의 키를 넣어주면 되는데, 아까 .env 파일에 추가해줬기 때문에 %로 감싼 키 이름을 넣어주면 된다.

<script
  type="text/javascript"
  src="//dapi.kakao.com/v2/maps/sdk.js?appkey=%REACT_APP_KAKAO_KEY%&libraries=services,clusterer"
></script>

 

6. tsconfig.json의 compilerOptions안에 types 추가

"compilerOptions": {
  //...
  "types": [
    "kakao.maps.d.ts",
  ],
}

 

이제 본격적으로 시작!~!

기본적으로 이 사이트의 예제를 따라서 만들었는데 타입때문에 꽤나 고생을 많이 했다. (같이 보면서 안되는 부분만 참고해주시길 바랍니당)

https://react-kakao-maps-sdk.jaeseokim.dev/docs/sample/library/keywordBasic

 

index.tsx

const [info, setInfo] = useState<IMarker>()
const [markers, setMarkers] = useState<IMarker[]>()
const [map, setMap] = useState<kakao.maps.Map>()

 

address.d.ts

export interface IMarker {
  position: {
    lat: number
    lng: number
  }
  content: string
}

 

info와 markers를 따로 IMarker라는 타입을 추가해주고 useState의 타입으로 지정해주었다.

 

index.tsx

본격 맵을 그려주는 부분.

return (
    <div>
      <Map
        center={{
          lat: 37.566826,
          lng: 126.9786567,
        }}
        style={{
          width: '100%',
          height: '350px',
        }}
        level={3}
        onCreate={setMap}
      >
        {markers?.map((marker) => (
          <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>
      <form onSubmit={handleFormSubmit} style={{ marginLeft: '500px' }}>
        <input type='text' placeholder='장소를 입력하세요' value={inputVal} onChange={handleInputChange} />
        <button type='submit'>검색</button>
      </form>
    </div>
  )

 

input과 submit 이벤트 핸들러. submit이 되면 getSearchResults라는 함수를 실행해 검색 결과가 markers배열에 들어가도록 했다.

const [inputVal, setInputVal] = useState('')

const handleInputChange = (e: FormEvent<HTMLInputElement>) => {
  setInputVal(e.currentTarget.value)
}

const handleFormSubmit = (e: FormEvent) => {
  e.preventDefault()

  getSearchResults(inputVal)
}

 

제일 중요한 부분! 키워드 검색을 하는 부분이다.

  const getSearchResults = (searchWord: string) => {
    if (!map) return

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

    ps.keywordSearch(searchWord, (data, status, _pagination) => {
      if (status === kakao.maps.services.Status.OK) {
        const bounds = new kakao.maps.LatLngBounds()
        const tmpMarkers: IMarker[] = []

        data.forEach((d) => {
          tmpMarkers.push({
            position: {
              lat: Number(d.y),
              lng: Number(d.x),
            },
            content: d.place_name,
          })

          bounds.extend(new kakao.maps.LatLng(Number(d.y), Number(d.x)))
        })

        setMarkers(tmpMarkers)
        map.setBounds(bounds)
      }
    })
  }

 

 

admin키로 계속 삽질하고 타입때문에 계속 삽질한 덕분에 뭔가 진척이 없었는데 그래도 하고 싶었던 기능을 추가하게 되어서 뿌듯하다 😊😊😊

혹시 모르니 전체 코드!

더보기
import { useState, FormEvent } from 'react'
import { Map, MapMarker } from 'react-kakao-maps-sdk'

import { IMarker } from 'types/address'

const Home = () => {
  const [info, setInfo] = useState<IMarker>()
  const [markers, setMarkers] = useState<IMarker[]>()
  const [map, setMap] = useState<kakao.maps.Map>()
  const [inputVal, setInputVal] = useState('')

  const getSearchResults = (searchWord: string) => {
    if (!map) return

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

    ps.keywordSearch(searchWord, (data, status, _pagination) => {
      if (status === kakao.maps.services.Status.OK) {
        const bounds = new kakao.maps.LatLngBounds()
        const tmpMarkers: IMarker[] = []

        data.forEach((d) => {
          tmpMarkers.push({
            position: {
              lat: Number(d.y),
              lng: Number(d.x),
            },
            content: d.place_name,
          })

          bounds.extend(new kakao.maps.LatLng(Number(d.y), Number(d.x)))
        })

        setMarkers(tmpMarkers)
        map.setBounds(bounds)
      }
    })
  }

  const handleInputChange = (e: FormEvent<HTMLInputElement>) => {
    setInputVal(e.currentTarget.value)
  }

  const handleFormSubmit = (e: FormEvent) => {
    e.preventDefault()

    getSearchResults(inputVal)
  }

  return (
    <div>
      <Map
        center={{
          lat: 37.566826,
          lng: 126.9786567,
        }}
        style={{
          width: '100%',
          height: '350px',
        }}
        level={3}
        onCreate={setMap}
      >
        {markers?.map((marker) => (
          <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>
      <form onSubmit={handleFormSubmit} style={{ marginLeft: '500px' }}>
        <input type='text' placeholder='장소를 입력하세요' value={inputVal} onChange={handleInputChange} />
        <button type='submit'>검색</button>
      </form>
    </div>
  )
}

export default Home

 

반응형