📢 들어가며
이번 포스팅은 지난 포스팅에서 이어진다.
지난 포스팅에선 개발 환경 설정을 했었다.
이번 포스팅에선 UI 틀을 잡고 OpenLayers를 활용해 지도를 띄워 볼 것이다.
모든 소스코드는 깃헙에서 확인할 수 있다.
🍜 UI 구성
대략적인 UI를 설계해보았다.
- 전체 화면에 꽉 차는 느낌으로 지도를 띄운다.
- 좌측엔 맛집에 대한 정보를 기록/출력하는 사이드 바가 있다.
- 사이드 바는 드래그로 크기를 늘렸다 키울 수 있다.
- 사이드 바는 버튼으로 최소/최대화가 가능하다.
- 사이드 바는 지도 위에 띄우고 Opacity(투명도)를 두어 지도 위에 띄운다는 느낌으로 구현한다.
사이드 바 안에 들어갈 구체적인 내용은 추후 생각해볼 예정이다.
원하는 대로 가능할지는 모르겠지만 대략적인 UI 틀은 이렇다.
🍜 OpenLayers 설치
OpenLayers는 웹 앱에 동적 지도를 띄우도록 해주는 라이브러리이다.
OpenLayers를 설치해보자.
CDN으로 설치해줄 수도 있고, npm 등으로 install 해줄 수 있다.
나는 npm을 활용했다.
CDN
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.9.0/build/ol.js"></script>
NPM
npm i ol
🍜 OpenLayers 지도 출력하기
지난 포스팅의 설정 상태 그대로라면 frontend/src/App.vue
라는 파일이 있을 것이다.App.vue
파일은 가장 초기 화면이 되는 파일이다.
이는 main.js
에 설정되어 있다.
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
App.vue
파일을 import
해온 뒤 렌더링 시키고, 파일 내의 id가 'app'인 element를 사용하겠다는 뜻이다.
이 App.vue
파일이 가득차게 지도를 띄울 것이다.
frontend/src/components
폴더에 MainMap.vue
파일을 파주었다.
그리고 App.vue
파일에 MainMap.vue
를 import
해오고 아래와 같이 입력해주었다.
<template>
<div id="app">
<MainMap/>
</div>
</template>
<script>
import MainMap from '@/components/MainMap'
export default {
name: 'App',
components: {
MainMap
}
}
</script>
<style>
#app {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
</style>
frontend/src/App.vue
화면을 가득 차게 구현하고 싶기 때문에 position
을 absolute
로 주고 상하좌우를 모두 0으로 세팅했다.
이제 MainMap.vue
를 구현하여 지도를 띄워보자.
OpenLayers 공식 문서가 굉장히 잘 되어 있었다.
공식문서를 따라 아래와 같이 입력해주었다.
<template>
<div class="main-map" ref="map">
</div>
</template>
<script>
import OlLayerTile from 'ol/layer/Tile.js';
import OlView from 'ol/View.js';
import OlMap from 'ol/Map.js';
import OSM from 'ol/source/OSM';
import {fromLonLat} from 'ol/proj.js'
export default {
name: 'MainMap',
data() {
return {
olMap: undefined,
}
},
mounted() {
this.olMap = new OlMap({
target: this.$refs.map,
layers: [
new OlLayerTile({
source: new OSM()
})
],
view: new OlView({
center: fromLonLat([127.1388684, 37.4449168]), // 경기도 성남
zoom: 10
})
})
}
}
</script>
<style scoped>
.main-map {
width: 100%;
height: 100%;
}
</style>
frontend/src/components/MainMap.vue
먼저 화면 꽉 차게 지도를 띄우기 위해 width
와 height
를 모드 100%로 주었다.
olMap
이라는 data()
를 정의하고 OlMap
을 생성해 저장한다.
지도와 관련된 모듈은 node_modules
에 위치해있는 ol
폴더에 있는 기능 중 필요한 것을 import 해와서 사용했다.
OlMap
을 생성하는 데에 필요한 옵션은 크게 target
, layers
, view
가 있다.target
은 이 지도를 띄울 element를 찾아 정의하는 것인데, 공식문서는 id
를 활용했으나,
나는 Vue를 사용하기 때문에 ref
를 활용했다.
💡 mounted 에 지도를 생성, 정의하는 이유
처음엔 created에 지도 생성 코드를 넣어 줬었는데, 아무 것도 뜨지 않았다.
위처럼 지도가 그려질 위치를 찾는 데에 ref
를 사용했다.ref
는 하위 컴포넌트(여기선 <div ref="main-map">
의 요소를 사용하기 위해 쓰는 Vue 속성이다.
때문에 하위 컴포넌트가 완전히 렌더링 된 후에 ref
로 참조할 수 있어서,
created 에선 ref
로 해당 target
을 찾을 수 없었던 것이다.
때문에 지도 생성은 mounted에서 진행해야한다.
layers
는 말 그대로 레이어를 의미하는데, 화면에 종이 한장을 얹는다는 의미로 생각하면 되겠다.
이 종이엔 OlLayerTile
라는 Tile 형태를 통해 생성된 지도가 그려지게 된다.
지도는 OSM(Open Street Map)이라는 오픈소스를 통해 그린다.
view
는 사용자 화면에 보여질 위치를 지정하는 옵션이다.
나는 경기도 성남시의 위도 경도를 찾아 입력해줬다.
여기서 fromLonLat 은 위도, 경도를 좌표계로 변환시키는 Openlayers 의 api이다.
OpenLayer는 위도, 경도가 아닌 좌표계(coordinate)로 위치가 표현된다.
default 좌표계 종류는 'EPSG:3857'이다.
'EPSG:3857'? 생소한 말처럼 들릴 수 있다 (내가 그랬음...)
해당 용어나 좌표계에 대한 설명은 주소를 검색/입력 받는 기능을 구현하는 다음 포스팅에서 좀 더 자세히 다뤄보겠다.
정상적으로 성남시쪽 지도가 뜬 것을 확인할 수 있었다!🎉
그런데 좌측 위를 보면 보기 싫은..? 버튼들과 copyright 문구가 보인다.
나중에 추가하는 일이 있더라도, 지금은 이를 없애보자.
공식 new Map
속성 관련한 공식 문서를 보면, controls
라는 옵션이 있는 걸 볼 수 있다.
해석해보면, controls
라는 옵션을 따로 추가하지 않으면 defaults
가 사용된다는 것이다.
이 detaults
가 바로 화면에 보이는 못생긴 버튼과 copyright이다.
import 를 추가하고 defaults를 없애주기 위해 controls
옵션을 추가 시켜주자.
// ...
import {defaults} from 'ol/control.js';
// ...
this.olMap = new OlMap({
target: this.$refs.map,
controls : defaults({
attribution : false,
zoom : false,
rotate: false,
}),
layers: [
new OlLayerTile({
source: new OSM()
})
],
view: new OlView({
center: fromLonLat([127.1388684, 37.4449168]), // 경기도 성남
zoom: 11
})
})
//...
frontend/src/components/MainMap.vue
attribution
, zoom
, rotate
는 각 detaults
에 정의된 각 버튼 및 속성을 의미한다.
전부 false
로 주어 비활성화 시켜줬다.
버튼과 copyright가 깔끔하게 없어진 것을 확인할 수 있었다.
🍜 사이드바 UI 틀 잡기
지도를 띄웠으니, 이제 설계했던 사이드바 UI를 구현해볼 것이다.
resizable하게 구현하기 전에, 먼저 대략 위치정도만 잡아보자.
frontend/src/components
에 SideBar.vue
파일을 추가해주고App.vue
에 import 해주자.
그리고 css로 위치와 크기를 잡는다.
<template>
<div id="app">
<MainMap/>
<SideBar class="side-bar"/>
</div>
</template>
<script>
import MainMap from '@/components/MainMap'
import SideBar from '@/components/SideBar'
export default {
name: 'App',
components: {
SideBar,
MainMap
}
}
</script>
<style lang="scss">
#app {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
> .side-bar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
}
}
</style>
frontend/src/App.vue
<template>
<div class="side-bar-wrapper">
<div class="side-bar">
</div>
</div>
</template>
<script>
export default {
name: 'SideBar',
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.side-bar-wrapper {
> .side-bar {
background-color: #000000;
opacity: 0.5;
width: 500px;
height: 100%;
}
}
</style>
frontend/src/components/SideBar.vue
설계했던대로 opacity도 주고, 좌측에 위치 시켰다.
지금은 사이드 바의 width
가 500px이지만, 이것은 resizable해야한다.
나는 이를 위해 vue-resizable
라이브러리를 사용하기로 했다.
vue-resizable 설치
npm i vue-resizable
vue-resiazble
을 import 해 온 후, 컴포넌트화 한다.
컴포넌트 해 온 vue-resiazble
하위에 <div class="side-bar">
를 넣어보자.
<template>
<div class="side-bar-wrapper">
<VueResizable
class="resizable-side-bar"
:width="500"
:min-width="500"
:max-width="Infinity"
:active="['r']"
>
<div class="side-bar">
</div>
</VueResizable>
</div>
</template>
<script>
import VueResizable from 'vue-resizable';
export default {
name: 'SideBar',
components: {
VueResizable
},
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.side-bar-wrapper {
> .resizable-side-bar {
> .side-bar {
background-color: #000000;
opacity: 0.5;
width: 100%;
height: 100%;
}
}
}
</style>
VueResizble
의 옵션엔 여러가지가 있는데 나는 그 중 width
, min-width
, max-width
, active
를 추가해줬다.width
, min-width
, max-width
는 보기만 해도 무슨 뜻인지 짐작이 갈 것이다.active=['r']
는 r
. 즉, 오른쪽(right)의 resize화를 활성화한다는 뜻이다.
정상적으로 Resize되는 것을 확인할 수 있었다!🎉
🚨 참고
TypeError: (0 , i.openBlock) is not a function 에러가 뜨면서 vue-resizable 이 적용이 되지 않는다면
vue-resizable 을 다운그레이드하면 된다.
아직 공식적으로 해당 이슈 관련하여 vue-resizable 측의 답변이 달리진 않았으나,
vue2 와 vue-resizable 특정 버전이 서로 호환이 되지 않는 것으로 보인다.
현재 나는 vue는 v2.6.14, vue-resizable은 v2.0.5 버전을 사용 중인데, 에러 없이 잘 되고 있다.
vue 2 를 쓰고 있다면, vue-resizable v2.0.5, v1.3.2, v2.1.3 버전으로 install 해 보는 것을 추천한다!
참고로 어떤 버전이 존재하는 지 잘 모르겠다면,
아래 명령으로 역대 버전들을 확인할 수 있다.
npm info vue-resizable versions
이번 포스팅에선 OpenLayers로 지도를 띄우고 UI틀을 잡는 작업을 진행했다.
다음 포스팅에선 사이드바 내에 들어갈 UI를 구현해보겠다. (시간이 남으면 약간의 기능도...)
댓글/하트, 피드백은 언제나 환영입니다! 😇
'개인 프로젝트' 카테고리의 다른 글
맛집 지도 만들기(6) - 리뷰 지도에 표시하기 및 리뷰 수정, 삭제 (2) | 2022.07.22 |
---|---|
맛집 지도 만들기(5) - CRUD API 구현하기 (feat. Axios, 함수형 컴포넌트) (1) | 2022.06.19 |
맛집 지도 만들기(4) - 지도 클릭 이벤트로 주소 입력 받기 (Nominatim API) (12) | 2022.05.16 |
맛집 지도 만들기(3) - 사이드바 UI 구현 (Font Awesome Icon, 글꼴 적용) (3) | 2022.02.13 |
맛집 지도 만들기(1) - Spring Boot + Vue.js 설치 및 연동하기 (7) | 2021.11.27 |