네이버 지도 초간단 Vue 앱 만들기

메디스트림·2024년 3월 6일
26

프론트엔드

목록 보기
2/2
post-thumbnail
post-custom-banner

안녕하세요. 인티그레이션 개발자 최유락 입니다.

린다이어트(Lean Diet) 에서 웹과 앱 모든 환경에서 제공하는 지도 기반 서비스가 있습니다. 진료 신청시 지도 UI에서 한의원을 선택하게 하고자 vue3-naver-maps 라이브러리를 사용하여 적용하게 되었는데 공식 문서의 설명으로는 모두 이해하기 어려운 부분이 있어서 개발 진행하면서 누구나 고민할 법한 구현 사례를 모아 글을 쓰게 되었습니다. 이 글을 보시는 분들이 지도 서비스 개발하실 때 시간을 절약할 수 있으면 좋겠습니다.

일단 화면을 띄워보자

install

# yarn
yarn add vue3-naver-maps

# npm
npm install vue3-naver-maps --save

main.js 설정

// main.js
import { createApp } from "vue";
import { createNaverMap } from "vue3-naver-maps";
import App from "./app.vue";

createApp(App)
  .use(createNaverMap, {
    clientId: "your clientId", // Required
    category: "ncp", // Optional
    subModules: [], // Optional
	})
  .mount("#app");

간단하게 지도 호출하는 방법

<script setup>
import { NaverMap } from 'vue3-naver-maps'
import { NaverMarker } from 'vue3-naver-maps'

const mapOptions = {
  latitude: 37.51347, // 지도 중앙 위도
  longitude: 127.041722, // 지도 중앙 경도
  zoom: 13,
}
</script>

<template>
  <naver-map
    style="width: 100%; height: 400px"
    :map-options="mapOptions"
  >
    <naver-marker
      latitude="37.51347"
      longitude="127.041722"
    />
  </naver-map>
</template>

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

지도가 화면에 나타났습니다.

기본 마커(Marker) 디자인을 바꾸려면?

마커는 지도 위의 한 위치를 아이콘으로 표시하는 오버레이 입니다.

린다이어트 에서는 기본 마커가 아닌 한의사 사진이 들어간 UI로 수정을 해야해서 마커를 커스텀 하는 방법을 찾아보았으나 공식 문서에는 설명이 없었습니다. 마커가 vue 컴포넌트로 되어 있어서 일단 slot 기능으로 적용해봤습니다.

<script setup>
import { NaverMap, NaverMarker } from 'vue3-naver-maps'

const mapOptions = {
  latitude: 37.51347, // 지도 중앙 위도
  longitude: 127.041722, // 지도 중앙 경도
  zoom: 13,
}

const datas = [] // 마커를 그릴 데이터
</script>

<template>
  <naver-map
    style="width: 400px; height: 400px"
    :map-options="mapOptions"
  >
    <naver-marker
      v-for="(data) in datas"
      :key="data.id"
      :latitude="data.lat"
      :longitude="data.lng"
    >
      <div class="marker">
        <img :src="data.imageUrl">
      </div>
    </naver-marker>
  </naver-map>
</template>

<style lang="scss" scoped>
.marker {
  /* 원하는 style 코드를 여기에 작성합니다. */
}
</style>

마커 인터렉션시 에러?!

위와 같이 마커 커스텀을 성공하였습니다. 성공 후 추가 개발을 진행하는데 지도를 옮기면서 검색을 새로 하는데 아래와 같은 에러가 발생했습니다. (slot에 요소 삽입시 발생하는 DOM Exception 문제로 추정)

그래서 다른 방법을 찾아보다가 vue2 네이버 지도 라이브러리인 vue-naver-maps 문서에서 마커 객체의 setIcon 함수를 발견하였습니다. 해당 함수를 직접 호출하는 방식으로 수정하여 다시 시도해보니 문제없이 동작하였습니다!


<naver-marker>
  <div class="marker">
    <img :src="clinic.imageUrl">
  </div>
</naver-marker>

/*
  기존 naver-marker 컴포넌트에 슬롯을 삽입하는 방식(위) 에서,
  setIcon 함수를 직접 호출하는 방식으로 변경하기 위해 새로운 컴포넌트(아래)를 만들었습니다.
*/

// @/components/CustomMarker.vue
<script setup>
import { ref, watch } from 'vue'
import { NaverMarker } from 'vue3-naver-maps'
	
const props = defineProps({
  id: {
    type: String,
    required: true,
  },
  imageUrl: {
    type: String,
    required: true,
  },
  lat: {
    type: Number,
    required: true,
  },
  lng: {
    type: Number,
    required: true,
  },
})
	
const marker = ref(null)
	
watch(() => [marker.value], () => {
  if (!marker.value) return

  marker.value.setIcon({
    content: (
      `<div class="marker">
        <img src="${props.imageUrl}" />
      </div>`
    ),
  })
})
</script>

<template>
  <naver-marker
    :latitude="lat"
    :longitude="lng"
    @on-load="marker = $event"
  />
</template>

<style scoped></style>

새로 만든 컴포넌트를 적용하게 되면 아래와 같습니다.

<script setup>
import { NaverMap } from 'vue3-naver-maps'
import CustomMarker from '@/components/CustomMarker.vue'

const mapOptions = {
  latitude: 37.51347,
  longitude: 127.041722,
  zoom: 13,
}

const datas = [] // 마커 그릴 데이터
</script>

<template>
  <naver-map
    style="width: 400px; height: 400px"
    :map-options="mapOptions"
  >
    <custom-marker
	  v-for="(data) in datas"
      :key="data.id"
      v-bind="data"
    />
  </naver-map>
</template>

<style scoped>
/* setIcon 방식으로 css를 적용하려면 :deep() 을 사용해야 합니다. */
:deep(.marker) {
  /* 원하는 style 코드를 여기에 작성합니다. */
}
</style>

행정구역 범위 안에 마커를 표기하고자 할 때

지도 API 활용한 서비스 개발시 자주 요구되는 기능으로, 특정 행정구역을 포커스 하고 마커를 표기해줘야 하는 경우가 많습니다. 간단하게 구현하는 방법을 정리해봤습니다.

해당 기능 구현을 위해서는 아래 3개의 Naver Map API 기능이 필요합니다.

  • new naver.maps.LatLngBounds(sw, ne)
    • 남서쪽과 북동쪽의 위/경도 좌표가 설정돼 있는 직사각형의 지리적 영역(이하 좌표 경계)을 정의합니다.
  • extend(latlng)
    • 객체의 좌표 경계에 지정한 좌표가 들어가도록 좌표 경계를 확장합니다.
  • panToBounds(bounds, transitionOptions, margin)
    • 지정한 좌표 경계를 포함하는 위치로 지도를 부드럽게 이동합니다.

위의 기능을 사용하여 특정 지역을 선택하면 api 통신을 통해 해당 지역의 리스트를 가져오고 가져온 좌표값으로 모든 장소를 포함하는 bound를 만들어 그 위치로 이동을 시킬 수 있습니다.

코드

<script setup>
import { ref } from 'vue'
import { NaverMap } from 'vue3-naver-maps'
import CustomMarker from '@/components/CustomMarker.vue'

const mapOptions = {
  latitude: 37.51347, // 지도 중앙 위도
  longitude: 127.041722, // 지도 중앙 경도
  zoom: 13,
}

const morphOption = {
  duration: 300,
  easing: 'easeInCubic',
}

const mapRef = ref(null)

const onLoadMap = (map) => {
  mapRef.value = map
}

// 특정 지역의 장소를 fetch 하는 api
const { data: datas } = useAreaFilterDatas({
  address: filterAddress,
  onSuccess: (response) => {
    moveMapAllMarkers(response)
  },
})

const moveMapAllMarkers = (markers) => {
  if (mapRef.value) return
		
  const bounds = markers.reduce((acc, marker) => {
    if (!acc) {
      return new window.naver.maps.LatLngBounds(
        new window.naver.maps.LatLng(marker.lat - 0.002, marker.lng - 0.002),
        new window.naver.maps.LatLng(marker.lat + 0.002, marker.lng + 0.002),
      )
    }
    acc.extend(new window.naver.maps.LatLng(marker.lat, marker.lng))
    return acc
  }, null)

  mapRef.value.panToBounds(bounds, morphOption, { top: 60, left: 60, right: 60, bottom: 60 })
}
</script>

<template>
  <div>
    <naver-map
      style="width: 400px; height: 400px"
      :map-options="mapOptions"
      @on-load="onLoadMap"
	>
      <custom-marker
        v-for="(data) in datas"
        :key="data.id"
        :v-bind="data"
      />
    </naver-map>
  </div>
</template>

지도 활용한 서비스 개발하시는 분들께 도움이 되길 바랍니다.
관련해서 궁금하신 내용이나 질문 답글로 남겨주시면 답변 드리겠습니다.

감사합니다.

profile
메디스트림 기술 블로그
post-custom-banner

3개의 댓글

comment-user-thumbnail
2024년 3월 6일

오오 네이버는 Vue3도 지원하네요!!

답글 달기
comment-user-thumbnail
2024년 3월 7일

마침 필요했던 내용인데 잘 보고갑니다 ~ ^^

답글 달기

안녕하세요, 네이버 클라우드 플랫폼입니다.
네이버클라우드의 기술 콘텐츠 리워드 프로그램 ‘이달의 Nclouder(3월)’ 도전자로 초대합니다 🙂

네이버 클라우드 플랫폼 서비스와 관련된 모든 주제로 4/4(목) 23시까지 신청 가능합니다. (*3월 작성 콘텐츠 한정 신청 가능)

Ncloud 크레딧을 포함한 다양한 리워드가 준비되어 있으니 많은 관심 부탁드립니다!

자세한 내용은 아래 링크에서 확인부탁드립니다.
https://blog.naver.com/n_cloudplatform/223380729192

신청 링크
https://navercloud.typeform.com/to/lF8NUaCF

답글 달기