티스토리 뷰
* 오랜만에 다시 블로그 글을 쓰고자 하니 이전에 쓰다가 날라갔던 내용을 다시 쓰려고 한다. 쉬운 내용으로 HTML5의 geolocation에 대해서 살펴보고자 한다. 아마 많은 웹들이 위치 기반으로 서비스를 제공하고 싶은 경우가 많을텐데 예전에는 웹앱으로 네이티브API와 연동하는 것이 필요했다면 이제는 이러한 경우에 웹페이지에서 위치 정보를 수집할 때 사용하면 된다. 웹페이지에서 HTML5의 위치 정보 수집 기능을 이용하면 좋은 점은 모바일 뿐 만 아니라 GPS가 내장된 노트북 등에서도 동일하게 위치 정보 기반의 서비스를 제공할 수 있다는 점이다. 그러면 먼저 간단하게 사용법부터 알아보자.
* 이전 편들
2012/10/07 - [HTML5 튜토리얼] 공부 시작 - 계획
2012/10/25 - [HTML5 튜토리얼] HTML5의 등장 배경과 여러 가지 개발 환경 및 현재 현황
2012/11/03 - [HTML5 튜토리얼] 기초 및 기본 구조, 프로그래밍상 기존의 HTML과 바뀐 점
2012/11/05 - [HTML5 튜토리얼] HTML5에 추가된 Element들 그리고 문서의 구조화
2012/11/18 - [HTML5 튜토리얼] Canvas 기초 강좌
2012/11/23 - [HTML5 튜토리얼] Video와 Audio 태그 기본
2012/12/03 - [HTML5 튜토리얼] 새로운 form element 들
2016/05/04 - [HTML5 튜토리얼] Web storage (localStorage, sessionStorage)
* navigator.geolocation API
원래 한동안 위치정보는 일반적으로 앱을 통해서만 제공되었던 특징적인 정보이었지만, HTML5을 통해 각종 모바일 장비들의 브라우져들이 직접 위치 정보를 수집하기 시작하였다. 따라서 이제는 네이티브 앱이나 하이브리드 앱을 통해서 위치 정보를 수집할 필요 없이 직접 웹에서도 위치 정보를 수집할 수 있다. 그것이 바로 navigator.geolocation API이다. 모바일 앱과 다른 점이 있다면 최근의 랩탑들 또한 GPS가 내장되어있는 경우가 많아서 이 API를 이용해서 웹서비스를 제작하게 된다면 모바일에만 한다는게 아니라 그러한 랩탑에서 접속하는 사용자들 또한 위치 기반 서비스를 제공할 수 있다.
이러한 위치 정보 API는 단말의 위도와 경도를 수집할 수 있는 인터페이스를 제공해주며, 각 단말의 GPS나 IP, RFID, WiFi나 블루투스 맥 주소, GSM/CDMA 셀 등을 이용하여 위치 정보를 수집하는 것이 일반적이다. 하지만 표준에서는 이렇게 수집된 정보가 실제 단말의 주소인지는 보장할 수 없다고 한다.
* 위치 정보 수집 함수
위치 정보 수집 함수는 아래의 세 가지 함수 스펙을 가지고 있다. 간단하게 설명하자면 getCurrentPosition() 함수는 1회성으로 위치 정보를 수집할 때 사용하면 되고, setInterval() 함수와 비슷한 개념으로 주기적으로 위치 정보를 수집하여 갱신할 때에는 watchPosition() 함수를 사용하면 되고, 이러한 지속적인 정보 갱신을 멈추려면 clearWatch() 함수를 사용하면 된다.
void getCurrentPosition(PositionCallback successCallback, optional PositionErrorCallback errorCallback, optional PositionOptions options); long watchPosition(PositionCallback successCallback, optional PositionErrorCallback errorCallback, optional PositionOptions options); void clearWatch(long watchId);
getCurrentPosition 함수는 위치 정보를 수집하는 API로 첫번째 인자는 요청이 성공 했을 경우의 콜백함수, 두번째 인자는 요청이 실패 했을 경우의 콜백 함수, 그리고 세번째 인자는 수집 옵션을 인자로 받고 있다. 여기서 PositionOptions는 아래와 같은 인자들을 설정할 수 있다.
boolean enableHighAccuracy
: 기본 값이 false. 앱이 위치 정보를 최대한 정확하게 수신하고자할 때 true로 설정하면 된다. 하지만 이러한 경우 응답 속도가 늦어지거나 기기의 전원 소모를 가중 시킬 수도 있게 된다. 사용자가 이러한 경우 높은 정확도를 수집하기 위한 권한 일부를 거절하는 것이 가능하기도 하고, 기기 자체에서 지원이 안 될수도 있다. 이 인자의 주된 목적은 배터리로 동작하는 모바일 장비들의 전원 소모를 줄이기 위하여 굳이 '아주' 정확한 위치 정보가 필요하지 않다고 표기할 때 사용된다.long timeout
: 기본 값이 0xFFFFFFFF(Infinity). getCurrentPosition() 함수나 watchPosition() 함수가 콜백함수를 정상적으로 호출하기 전에 최대 허용 시간을 명시한다. 정상적인 위치 정보를 수집할 수 없는 경우에는 에러콜백 함수가 호출되고, 에러 종류는 TIMEOUT으로 설정된다. 참고로 사용자가 위치 정보를 수집하기 위하여 권한 요청을 하는 시간은 제외 된다. getCurrentPosition()의 경우에는 타임아웃으로 인한 에러콜백 호출이 단 한번만 이루어진다면, watchPosition() 함수는 매번 위치 정보에 대한 갱신 요청이 있을 때마다 타임아웃으로 인한 에러콜백 함수 호출이 일어나게 된다. 시간 단위는 밀리초 단위이다.long maximumAge
: 기본 값이 0. 기존에 수집했던 위치 정보를 얼마나 보관하여 캐쉬로 사용할지 결정하는 값이다. 값이 0으로 설정되었다면, 캐쉬를 사용하지 않고 매번 요청이 있을때마다 위치 정보의 갱신을 요청하게 되고, Infinity로 설정하게 되면 한번만 위치 정보를 수집하고 이후 계속 같은 값을 재활용하게 된다. watchPosition() 함수의 경우에는 가장 처음 위치 정보를 수집한 시점을 시작으로 시간을 계산하고 시간단위는 밀리초이다. 위치 정보가 굳이 아주 정확할 필요가 없다면 이 값도 적당하게 조절하여 모바일 장비에 대한 전원 소모를 배려해줄 수 있다.
callback PositionCallback = void (Position position); callback PositionErrorCallback = void (PositionError positionError);
Position 데이터형은 아래와 같은 속성과 하위 속성 목록을 가진다.
Coordinates coords
: 위치 정보double latitude
: 위도double longitude
: 경도double altitude
(옵션): 고도double accuracy
: 정확도double altitudeAccuracy
(옵션): 고도 정확도double heading
(옵션): 현재 향하고 있는 방향double speed
(옵션): 현재 속도DOMTimeStamp timestamp
: 수집 시간 정보
PositionError는 아래와 같은 속성들을 가진다. 위치 정보 수집을 하면서 발생한 오류의 종류와 각 오류 종류에 대한 코드를 상수 값으로 가지고 있으므로 각 에러 발생 상황에 따라서 다른 에러 처리나 경고 메세지를 띄워주면 된다.
unsigned short code
: 오류 코드DOMString message
: 오류 메세지unsigned short PERMISSION_DENIED = 1
: 사용자가 위치 정보 수집을 거부unsigned short POSITION_UNAVAILABLE = 2
: 위치 정보 수집 불가(예: GPS 동작 불가 지역 등)unsigned short TIMEOUT = 3
: 위치 정보를 수집하기 전에 먼저 옵션의 timeout 값 시간이 소모
* watchPosition 처리 순서
watchPosition은 주기적으로 위치 정보를 수집하도록 요청하는 함수로 외부적으로 입력 주기를 별도로 입력할 수 없고 내부적으로 언제 위치 정보를 수집하게 되는지 정책이 별도로 정의되어있다.
- 기존에 수집된 위치 정보가 아직 maximumAge에 도달 안 했으면 일단 캐쉬된 위치 정보를 콜백함수로 넘긴다.
- 기기에 위치 정보 수집관련 정보(예: 와이파이나 무선셀 정보)가 갱신 될 때 시스템 이벤트로 알림을 받도록 등록한다.
- 위치 정보 수집을 수행한다.
- 옵션의 timeout 값을 밀리초로 timer를 등록하여 timer가 만료되면 TIMEOUT의 오류코드로 에러 콜백함수를 호출
- 만약 위치 정보가 정상적으로 수집되었다면,
- 타임아웃을 측정하는 timer 취소
- 이전에 수집되었던 위치 정보와 비교해서 차이가 있으면 successCallback 함수로 새로운 위치 정보를 전달. 이 때에 브라우저에서 이러한 비교를 제한하여 컴퓨팅 자원이 과도하게 소모되는 것을 방지할 수 있다.
- 아니라면 POSITION_UNAVAILABLE 오류 코드로 에러콜백 함수를 호출
- 2번에서 등록한 시스템 이벤트가 발생하면 위치 정보 수집 단계를 다시 수행
* 위치 정보 유스케이스
- 사용자 주변에 관심거리를 추천: 외국의 도시 등을 방문했을 때 위치 정보를 수집하여 관광할 것들을 추천해주거나 추천 순위를 제공
- 위치 정보의 내용을 기반으로 메모: 친구들과 등산을 하다가 여러 곳에서 위치 정보를 수집하고 사진에 위치 정보를 함께 제공하여 여행일기를 제공. 산 중에 있을 때 네트워크가 안 될 때에는 웹앱처럼 동작하여 로컬에 관련 정보를 저장하고 있다가 나중에 데이터 네트워크 접속이 되면 서버에 업로드하는 등의 기능 제공
- 사용자의 위치를 지도에 표시: 사용자가 여행할 때 어디에 있는지 편하게 지도로서 활용할 수 있도록 사용
- 네비게이션 기능: 사용자에게 맵 어플리케이션과 같이 경로를 탐색하여 경로를 따라서 네비게이션의 기능을 웹앱에서 수행
- 사용자 주변에 있는 흥미거리 알림: 웹앱 기반의 관광 서비스를 제공하여 현재 사용자 근처에 있는 흥미거리를 알림으로 제공
- 현재 위치 정보 제공: 날씨가 지역 뉴스 등과 같이 지역 기반으로 달라지는 정보를 사용자의 위치 정보를 지속적으로 수집하여 자동 갱신
- SNS에 위치 태그 기능을 제공: SNS앱과 연동하여 자동으로 위치 태그와 상태를 업데이트할 수 있는 웹앱 기능을 제공
* 위치 정보 사용 예
* https 사이트만 위치 정보 수집 허용
위의 예를 실행할 때 아래와 같은 오류가 일어날 수 있다. 옛날에는 그냥 사용 가능했던 것 같은데 최신 브라우저로 위치 정보 수집을 하려고 하면 기본적으로 허용하지 않는 브라우저도 있다.
최근에 개인정보 강화로 인하여 크롬이나 사파리 등의 브라우저에서는 일반 http:// 사이트에서는 위치 정보를 수집할 수 없게 만들었고, https:// 사이트들을 대상으로만 가능하도록 보호하고 있다. 이는 위치 정보도 개인 정보로 분류되기 때문에 브라우저에서부터 개인정보보호를 신경쓰고 있는 것이다. 따라서 웹에서 위치정보를 수집하고 싶다면 웹사이트를 기본적으로 https:// 로 하는 것이 좋다. 따라서 현재 블로그 페이지의 주소를 https://unikys.tistory.com/375 로 수정하여 접속하면 위의 버튼을 눌러 스크립트를 정상적으로 수행할 수 있다. 따라서 아래와 같이 https 로 들어가서 실행하면 위의 오류는 해결 된다.
따라서 사이트에 위치정보 수집 API를 사용하고자 한다면 먼저 https 환경을 먼저 구성해 놓는 것이 좋을 것이다.
* 사용자의 위치 수집 권한 요청
이제 버튼을 누르게 되면 아래와 같이 사용자에게 위치 정보 수집을 요청한다.
이렇게 요청하게 되면 사용자는 쉽게 승낙할수도 있지만 거절할수도 있기 때문에 항상 거절하는 것에 대하여 대비해놔야한다.
거절할 때에는 errorCallback 함수가 호출되고 위의 표준에 명시되어있는대로 오류 코드는 1로 리턴하므로 이러한 경우 사용자에게 기능 사용에 대한 제한이 있음을 알리는 것이 좋다. 이렇게 웹브라우저를 통해서 위치 정보를 수집하는 경우에는 접속할 때마다 권한 설정이 다르게 나타날 수 있으므로 항상 위치 정보 수집 권한이 거절 되었을 때를 대비하여 프로그램과 UX를 설계해야 한다. 만약 한번 위치 정보 수집을 거절하였다면 이것을 푸는 것 또한 의외로 사용자에게 번거로운 일이 될 수 있으므로 사전에 원활한 서비스 제공을 위하여 위치 정보 수집을 수락해달라는 알림을 표시해도 좋고, 나중에 위치 정보 수집이 거절 당한 경우 어떻게 위치 정보 수집 제약을 해제할 수 있는지도 알리면 좋을 수도 있다. 아래는 크롬에서 거절한 경우 그것을 해제하는 방법이다. 주소창 오른쪽 구석에 있는 오류 아이콘을 누르고 "Clear these settings for future visits"를 클릭한다음 페이지를 새로고침하면 다시 정상적으로 권한 요청을 할 수 있게 된다.
기타 다른 위치 정보 수집 오류가 발생하는 경우에는 위치 정보 수집을 재시도하던가 사용자에게 새로고침을 권하는 것도 하나의 방법이 될 수 있다. 이러한 오류에 대한 문제를 해결하는 것은 사용자 경험에 있어서 필수이며 '사용자가 권한을 거절하지 않을 것이다' 등의 가정은 절대로 세우면 안된다. 아무리 사용 권한을 수락해달라는 알림을 해줘도 실수로라도 누르게 되는 사람은 반드시 발생한다.
* 정상적으로 위치 정보 수집
이제 정상적으로 위치 정보를 수집하게 되면 아래와 같이 위도와 경도를 successCallback() 함수로부터 받게 된다. 그러면 표준에 명시되어있는대로 Position 객체와 그 속성인 coords 를 통해서 현재 위치를 확인할 수 있다. 아래와 같이 현재 위치가 출력 될 것이고, 구글 지도에도 현재 위치에 정보 창이 뜨게 된다.
이렇게 간단하게 현재 위치를 구글 지도위에 표시해줄수도 있고, 서비스 특성에 따라서 지도의 위치를 옮긴 다음 해당 위치 주위에 있는 정보들을 서버에 요청해서 클라이언트에 뿌려주도록 하는 것 가능하다. 물론 이 때에 지도의 위치를 먼저 옮길지, 아니면 서버의 응답을 기다렸다가 지도의 표시 위치를 옮길지는 서버의 성능과 쿼리 종류에 따라서 테스트해보고 결정하면 좋을 것이다. 또한 노트북 등을 토해서 위치 정보를 수집해보면 모바일과는 다르게 시간이 다소 소요되는 것을 느낄 수 있을 텐데 이러한 경우 위치 정보 수집 요청 후 응답이 오기까지 "위치 정보 수집 중..." 등과 같은 표시를 해줘도 좋을 것이다. 개인적으로는 의존성이 강하지 않은 경우 모달창이 아닌 슬라이드다운되거나 오버랩되는 알림의 형태로 알려주는 것이 사용자 경험이 더 좋은 것 같다.
* 소스
그러면 가장 중요한 위의 내용을 구현한 소스는 아래와 같다. 구글지도 API를 불러오는 스크립트에서 key=$API_KEY 부분은 각자의 구글 계정에 사이트에 맞는 키를 신청해서 사용해야 하고, 다른 지도API를 사용하고자 한다면 지도 부분은 다르게 구현해야 할 것이므로 각 지도API 메뉴얼 홈페이지를 참고하면 된다.
<button id="getLocation" type="button">위치 정보 수집</button> <div id="map" style="width:500px; height: 500px;display: block;"></div> <script> (function () { let map, infoWindow; window.initMap = function () { map = new google.maps.Map(document.getElementById('map'), { center: {lat: -34.397, lng: 150.644}, zoom: 8 }); infoWindow = new google.maps.InfoWindow({map: map}); infoWindow.setContent('Your location'); }; function successCallback(position) { let pos = { lat: position.coords.latitude, lng: position.coords.longitude }; infoWindow.setPosition(pos); map.setCenter(pos); alert("Your current position is: latitude(" + pos.lat + "), longitude(" + pos.lng + ")"); } function errorCallback(error) { alert("Error: " + error.message); } document.getElementById("getLocation").onclick = function () { navigator.geolocation.getCurrentPosition(successCallback, errorCallback); }; }()); </script> <script src="https://maps.googleapis.com/maps/api/js?key={$API_KEY}&callback=initMap" async="" defer=""></script>
* 반응형 프레임워크(Bootstrap)와 구글지도 충돌
이번 블로그글을 쓰면서 이상하게 구글지도를 넣고 테스트하는데 구글지도가 너무 이상하게 표시되는 것이다. 구글맵에 숨겨져야할 타일들이 겹쳐져서 보이고 현재 위치도 잘 안 보이는데 반응형 프레임워크와 구글지도의 CSS가 충돌이 일어날수도 있다. 이것에 대해서는 따로 팁글을 하나 작성하도록 하겠다.
작성 중: [티스토리 반응형 스킨 편집] 반응형 스킨(Bootstrap)과 구글맵 충돌 해결
* 다음편 예고
이제 슬슬 일반적으로 많이 사용되는 HTML5 기능들은 둘러본 것 같고, 다음에는 WebSQL, 그리고 그 다음에는 WebGL에 대해서 살펴볼 것이다.
작성 예정: [HTML5 튜토리얼] WebSQL
작성 예정: [HTML5 튜토리얼] WebGL
* 읽을 거리
위치정보 수집 API 표준(https://www.w3.org/TR/geolocation-API/): 표준 문서는 항상 기본적인 세부적인 구현내용을 살펴보는데 도움이 된다.
구글지도 튜토리얼(https://developers.google.com/maps/documentation/javascript/tutorial): 구글 지도의 다양한 사용 방법을 소개한다.
- Total
- Today
- Yesterday
- 속깊은 자바스크립트 강좌
- gae
- lecture
- 안드로이드
- Android
- 삼식이
- 서울
- java
- 강좌
- mini project
- ny-school
- Python
- gre
- google app engine
- Javascript
- 샷
- GX-10
- 팁
- 사진
- php
- HTML5
- 뽐뿌
- 탐론 17-50
- HTML5 튜토리얼
- c++
- Writing
- 안드로이드 앱 개발 기초
- TIP
- K100D
- 자바스크립트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |