Vue 3.0/99.ETC

[vue 3.0]카카오맵 사용하기

  • -
반응형

이번 포스트에서는 vue-cli 기반으로 카카오맵을 사용하는 방법에 대해서 알아보자.!

카카오맵에는 특별히 Vue에서 사용하는 방법 이런게 있지는 않다.Vue는 그냥 여러 framework 중 하나일 뿐이니까. 대신 이런 계열인 javascript에서 사용법에 대해 잘 정리되어있다. 

가끔 그냥 google 신에게 "vue 카카오맵" 이렇게 검색해서 사용하는 친구들에게 공식문서와 좀 더 친해지길 바라며 공식 문서를 읽으면서 Vue에는 어떻게 적용해야 하는지 살펴보고자 한다.

따라서 이번 글은  아래 문서를 바탕으로 한다. 문서의 동일 부분과 잘 비교하면서 해보자.

Kakao 지도 Web API 가이드

 

준비하기!

카카오 개발자 사이트에 가입하는것은 기본중의 기본!!

애플리케이션 생성

로그인을 완료하면 상단에 내 애플리케이션 메뉴가 있다.

클릭하고 들어가면 애플리케이션 추가하기 메뉴가 나오는데 다시 클릭하고 들어가면 새로운 애플리케이션을 추가할 수 있다.

원하는 앱의 이름과 사업자 명을 입력해준다. 실제 서비스되는건 아니니까 사업자명은 그냥 적으면 된다.

 

웹 플랫폼 추가하기

위에서 생성한 애플리케이션에 들어가보면 앱 키가 있다. 여기서는 javascript에서 사용할 꺼니까 JavaScript 키를 복사해둔다. 나중에 Vue에서 참조해야 한다.

플랫폼 항목이 비어있는데 [플랫폼 설정하기]를 클릭하고 들어가서 Web 영역에 [Web 플랫폼 등록]을 클릭한다. 여기서는 Vue가 서비스할 서버 정보를 입력해준다.

 

 

시작하기

 

Vue 프로젝트 설정 및 키 저장

Vue Cli 기반으로 간단히 프로젝트를 생성해준다.

vue create vue_samples

이제 서비스 키를 소스에 노출하지 않고 설정하기 위해서 .env.local 파일을 프로젝트 루트에 생성하고 아래와 같이 추가한다.

VITE_KAKAOMAP_KEY=카카오맵_서비스_키

키 등록과 관련된 내용은 아래 포스트를 참조하자.

https://goodteacher.tistory.com/703

 

[vue 3] vite의 환경변수와 모드

이번 포스트에서는 vite의 환경 변수에 대해 살펴보자 환경 변수 import.meta.env Vite의 환경변수는 import.meta.env 객체로 관리된다. (import.meta는 import된 모듈의 context 정보를 가져오기 위한 객체이다. ) i

goodteacher.tistory.com

 

지도에 담을 영역 만들기

카카오 맵을 별도의 component로 만들어서 사용하기 위해서 [src/views/KaKaoMapComp.vue]를 생성한다. 

카카오 맵은 기본적으로 id가 map인 영역을 찾아서 랜더링 한다. 따라서 template에는 id가 map인 div를 만들어 준다.

<template>
  <div class="wrapper">
    <div ref="container"></div>
    <div class="control-btns">
      <button @click="displayMarkers">마커 표시</button>
      <button @click="displayMarkersWithInfo">InfoWindow</button>
      <button></button><button></button><button></button>
    </div>
  </div>
</template>

<style scoped lang="scss">
.wrapper {
  background: greenyellow;
  height: 100%;
  width: 100%;

  & div:first-child {
    width: 100%;
    height: 500px;
  }
}
</style>

App.vue에서 잘 얻어지는지 확인하자.

 

<template>
  <div class="container">
    <h1>카카오 맵</h1>
    <KakaoMapComp></KakaoMapComp>
  </div>
</template>

<script setup>
import KakaoMapComp from '../components/KakaoMapComp.vue'
</script>

<style lang="scss" scoped></style>

화면이 잘 처리되었다면 이제 다음 동작은 계속 KaKaoMapComp.vue에서 처리하면 된다.

 

실제 지도를 그리는 JavaScript API 불러오기

카카오 맵은 <script src=""> 형태로 가져와야 한다. Vue를 사용하다 보니 import에만 익숙해져서 그냥 로딩하는 것을 난감해하기도 한다.  이 경우는 위의 태그 객체를 javascript에서 생성 후 document에 추가해주면 자동으로 로딩된다. 통상 이런 자바스크립트는 html>head>script에 작성한다.

참고로 카카오 맵 api를 살펴보면 동적으로 script를 로딩하기 위해서는 autoload=false 옵션을 지정해주어야 한다.

Kakao 지도 Web API Documentation

html에서 코드를 작성할 때는 line by line으로 동작하니 작성한 순서대로 로딩이 되지만 우리는 javascript를 통해서 script를 로딩 시켰으므로 로딩 완료에 대한 감지도 역시 javascript를 통해서 이뤄져야 한다. 로딩 완료 시점을 파악히기 위해서는 load라는 이벤트를 이용하면 된다. 

이 과정을 함수로 만들어보자.

const loadScript = () => {
  const key = import.meta.env.VITE_KAKAOMAP_KEY   // env에 등록된 키 가져오기
  const script = document.createElement('script') 	 
  // 동적 로딩을 위해서 autoload=false 추가
  script.src = `//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=${key}`
  // kakaomap script loading 후 initMap 실행
  script.addEventListener('load', () => kakao.maps.load(initMap))
  document.head.appendChild(script)
}

const initMap = ()=>{}

 

지도를 띄우는 코드 작성을 위한 추가적인 처리

"스크립트가 로딩이 되면" 지도를 띄워주자! 따라서 이 과정은 dom이 구성된 후 처리해야 하므로 mounted hook에서 처리해준다.

이 과정에서 script 가 로딩되면 kakao라는 객체가 window 객체 즉 전역에 등록되는데 이를 처리하는 과정에 두 가지 주의점이 있다.

첫 번째 주의점은 전역 객체이기 때문에 mount가 될 때마다 여러 번 로딩할 필요가 없다는 점이다. 컴포넌트가 mount 될 때마다 다시 script를 로딩하면 당연히 성능상 문제를 발생시킬 것이다. 로딩이 되어있지 않을 때만 로딩하도록 해주자. 따라서 window 객체에 kakao가 등록되어있는지 확인하고 없을 때만 loadig 해주어야 한다.

onMounted(() => {
  if (window.kakao?.maps) {
    console.log(`KakaoMapComp.vue - 이미 map 있음`, window.kakao.maps)
    initMap()
  } else {
    console.log(`KakaoMapComp.vue - map script loading 필요`)
    loadScript()
  }
})

 

두 번째 주의점은 kkako가 전역객체로 등록되기 때문에 발생하는 문제이다. 사실 window에 등록된 객체를 사용할 때는 window. 이라고 소속을 밝힐 필요가 없다. 

하지만 eslint는 전역에 사용자 정의 객체를 등록하고 사용하는 행위에서는 이런 행동은 아주 위험하다고 오류로 처리해버린다.(물론 동작은 한다.) 따라서 eslint에게 실수가 아니라 잘 알고 쓰는거라고 알려줄 필요가 있다. 3가지 형태로 처리할 수 있는데 가장 쉬운 방법은 window. 이라고 써주는 방법이다. 귀찮지만.

두 번째는 자바스크립트 코드에 아래와 같이 추가할 수 있다.

<script setup>
/* global kakao */

여기서 /*global kakao, B, C*/는 eslint를 위한 주석으로 eslint에게 kakao가 global 변수임을 알려준다.  ,를 이용해서 여러 개를 등록해줄 수 있다.

마지막으로 .eslintrc.cjs에 아래와 같이 globals를 설정 할 수 있다.

rules: {'no-unused-vars': 'warn'},
globals: {kakao: true}

당연히 둘 중 하나만 해야한다.

 

지도를 띄우는 코드 작성

이제 실제로 지도를 띄우는 함수를  methods에 작성해보자. 여기의 소스는 특별히 설명할 내용이 보이지 않는다. map 객체를 생성한 후 data의 map에 잘 설정하면 되겠다. 지점의 위/경도를 사용할 때는 LagLng라는 객체를 사용한다. 작명 센스 Good!!

let map = null
const container = ref(null)

const initMap = () => {
  const options = {
    center: new kakao.maps.LatLng(37.2429362, 131.8624647), // 독도는 우리땅
    level: 5
  }
  map = new kakao.maps.Map(container.value, options)
}

 

마커 표시하기

다음으로 프로젝트에서 자주 사용되는 기능들을 작성해보자. 위의 예제에서 map을 가져왔다면 다음은 이 맵에다가 무언가를 처리하는 과정이므로 kakao에서 제공하는 sample들을 잘 분석해보면 어렵지 않게 사용할 수 있다.

https://apis.map.kakao.com/web/sample/

 

data 설정

먼저 필요한 marker의 위치 정보를 선언해보자.

const getMarkerPositions = () => {
  return [
    { title: '카카오', latlng: new kakao.maps.LatLng(33.450705, 126.570677) },
    { title: '생태연못', latlng: new kakao.maps.LatLng(33.450936, 126.569477) },
    { title: '텃밭', latlng: new kakao.maps.LatLng(33.450705, 126.570677) },
    { title: '근린공원', latlng: new kakao.maps.LatLng(33.451393, 126.570738) }
  ]
}

// 화면에 표시되어있는 marker들
const markers = []

markers는 화면에 표시된 marker들을 저장할껀데 나중에 현재의 marker들을 reset 하기 위해 목록으로 관리가 필요하다.

 

마커의 표시

마커를 표시할 때는 기존 마커 제거 > [마커 이미지 커스터마이징] > 마커 표시 > 해당 지역으로 이동의 단계로 생각해볼 수 있다. 마커 이미지 커스터마이징은 필요할 때만 해보면 된다. 바로 소스를 확인해보자. 

const displayMarkers = () => {
  // marker 정보 로딩
  const positions = getMarkerPositions()

  // 1. 현재 표시되어있는 marker들이 있다면 marker에 등록된 map을 없애준다.
  if (markers.length > 0) {
    markers.forEach((item) => {
      item.setMap(null)
    })
  }
  // 2. 마커 이미지 커스터마이징 하기
  // javascript 영역에서 assets의 정보 가져오기
  const imgSrc = '/src/assets/map/markerStar.png'
  const imgSize = new kakao.maps.Size(24, 35)
  const markerImage = new kakao.maps.MarkerImage(imgSrc, imgSize)
  // 3. 마커 표시하기
  positions.forEach((position) => {
    const marker = new kakao.maps.Marker({
      map,
      position: position.latlng,
      title: position.title,
      image: markerImage
    })
    markers.push(marker)
  })
  // 4. 지도를 이동시켜주기
  // 배열.reduce( (누적값, 현재값, 인덱스, 요소)=>{ return 결과값}, 초기값);
  const bounds = positions.reduce(
    (bounds, position) => bounds.extend(position.latlng),
    new kakao.maps.LatLngBounds()
  )
  map.setBounds(bounds)
}

4번 영역에서는 reduce 함수가 사용되는데 전체를 하나로 누적시켜서 줄여가는 함수라고 이해하면 되겠다.

 

이벤트 처리와 info window 생성

이번에는 마커를 클릭했을 때 지점에 대한 정보를 보여줄 info window 를 사용해보자. 다른 부분은 동일하고 위 함수에서 3번 영역인 marker 표시 부분만 약간 수정해주면 된다. 

// 3. 마커 표시하기
  positions.forEach((position) => {
    // information window 생성
    const infowindow = new kakao.maps.InfoWindow({
      removable: true,
      content: `<div style="padding:5px;">${position.title}</div>`
    })
    const marker = new kakao.maps.Marker({
      map,
      position: position.latlng
    })
    // 이벤트 등록
    kakao.maps.event.addListener(marker, 'click', () => {infowindow.open(map, marker)})
    kakao.maps.event.addListener(marker, 'mouseover', () => {infowindow.open(map, marker)})
    kakao.maps.event.addListener(marker, 'mouseout', () => {infowindow.close(map, marker)})
    markers.push(marker)
  })

먼저 정보를 표시할 InfoWindo를 생성할 수 있는데 content 항목에 화면에 표시할 내용을 적어준다. removeable은 'x' 버튼을 표시해서 닫을 수 있게 할것인지 옵션이다.

InfoWindow는 이벤트 상황에서 open과 close 메서드로 제어할 수 있다. 이벤트를 등록할 때는 kakao.maps.event.addListener를 이용한다. click을 이용하면 'x' 버튼으로 닫아야 하기 때문에 mouseover, mouseout을 이용하는 것이 사용자 편의적으로 괜찮을것 같다.

 

참고로 info window의 style을 작성할 때 주의 할 점이 있는데 scoped 속성을 사용하면 안된다는 점이다. scoped가 적용될 수 있는 이유는 vue가 가상 돔을 만들면서 hash를 삽입하고 그 hash가 적용된 요소에 scoped 내용이 적용되는 방식이다. 하지만 info window DOM을 직접 코드로 작성해서 넣어준 것이기 때문에 hash가 적용되지 않는다.

따라서 전역의 style을 적용해주어야 함을 주의하자.

 

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.