본문 바로가기

프로그래밍/Node.js

[Do it! Node.js 프로그래밍] 13일차

1. 공부한 범위


[12] 위치 기반 서비스 서버 만들기



2. 공부한 내용


[12] 위치 기반 서비스 서버 만들기

 

12-1 커피숍 위치 지정하기

 

커피숍의 위치 정보 다루기

- 위치 기반 서비스를 다루기 위해서는 여러 기능들을 포함해야 한다.

- 커피숍을 찾는 기능을 만드는 과정

위치 정보 확인 : 특정 지역의 커피숍 이름이나 전화번호, 위치 정보 등 정보를 확인한다. 커피숍의 위치 정보는 특히 경위도 좌표로서 알고 있어야 한다.

위치 정보 저장 : 위치 정보를 포함한 커피숍 정보를 서버에 저장한다. 데이터는 서버의 데이터베이스에 저장된다.

커피숍을 위치로 조회 : 가까운 커피숍을 경위도 좌표를 활용해 조회한다.

- 커피숍의 위치 정보를 확인하는 첫 단계에서는 해당 위치를 경위도 좌표로 알고 있어야 한다. 해당 정보는 모바일 단말이나 기타 장비를 사용해 현장의 위치를 알아내거나 이미 조사된 위치 정보를 확인하는 등의 방법을 사용하게 된다.

- 위치 정보를 서버의 데이터베이스에 저장하는 두 번째 단계에서는 데이터베이스에 정보를 저장할 때 사람의 이름같은 문자열 데이터를 저장할 때와는 달리 경도와 위도라는 두 가지 정보를 함께 저장해야 한다.

- 두 정보를 함께 저장 및 조회해야 하기 때문에 일반적인 문자열 데이터와는 다른 방식을 사용해 처리해야 한다.

- 만일 문자열 데이터를 처리할 때와 같은 방식을 사용하려 한다면 데이터가 많아질 수록 점점 조회속도가 느려지게 된다.

- 조회 속도를 빠르게 유지하기 위해  공간 인덱싱 방법을 활용하게 되는데, 경도와 위도 좌표의 인덱스를 별도로 만듬으로서 조회 속도를 높이는 방식이다. 해당 방법을 몽고디비에서는 GeoSpatial Indexing이라 부른다.

- 위치를 조회하는 세 번째 단계에서는 다양한 조회 방식을 사용할 수 있다.

- 가장 대표적인 조회 방식

1) 사용자가 있는 곳에서 가장 가까운 커피숍 찾기

2) 사용자가 보고 있는 지도 범위 안의 커피숍을 찾아서 보여주기

3) 사용자가 있는 곳에서 반경 몇 미터 안에 있는 커피숍을 찾아서 보여주기

 

- 지도 서비스를 제공해주는 사이트를 이용해 몇 가지 커피숍들의 위치 정보를 확인 후 웹 서버의 데이터베이스에 저장한다.

- 커피숍의 위치는 데이터 유형 중 Point 위치 데이터에 해당하게 된다. 이는 점으로 표시할 수 있는 위치라는 의미이며 몽고디비에서는 Point라는 유형으로 저장할 수 있다.

- 위치 데이터의 유형들 (점, 선, 면의 느낌)

Point : 사용자의 현재 위치 등 특정한 지점을 나타낸다.

LineString : 도로 등 이어진 지점을 나타낸다.

Polygon : 청담동, 방배동과 같은 지역을 나타낸다.

- 위치 정보를 서버에서 받아 보여주는 기능을 단순히 모든 위치들을 리스트로 보여주는 경우와 위치를 검색해 보여주는 경우로 나눌 수 있다.

 

커피숍 스키마 만들기

 

- 커피숍의 정보를 저장하는 스키마에는 name, address, tel, geometry, created_at, updated_at의 6가지 속성이 들어간다.

- 각 속성의 기능

name : 커피숍 이름을 저장하며 인덱스를 만든다.

address : 커피숍 주소를 저장한다.

tel : 커피숍 전화번호를 저장한다.

geometry : 경위도 좌표를 저장하며, 이 안에는 다시 두 개의 속성을 가진 객체가 존재한다.

1) type : 위치 정보의 유형 3가지 중 하나를 선언하며 디폴트값은 Point다.

2) coordinates : 위치 좌표를 넣는 배열로 정의되며 Number형의 좌표를 저장 가능하다. 이 때 좌표는 경도, 위도 순으로 저장된다.

- geometry 속성을 인덱스로 만들게 되면 데이터 검색 속도가 훨씬 늘게 되며 해당 속성을 index() 메소드로 인덱싱할때의 위치 좌표는 2dsphere 타입으로 지정한다.

 

커피숍 정보를 추가하고 커피숍 리스트 조회하기

- 만들어진 스키마로 검색할 때 사용할 static() 메소드를 추가한다.

- 만들 함수는 findAll, findNear, findWithin, findCircle의 4가지다.

- 커피숍을 조회하는 코드는 스키마 파일 또는 라우팅하여 처리하는 파일 둘 중 아무곳에 들어가도 정상 작동한다.

 

- 완성된 스키마 파일은 웹 서버가 실행될 때 해당 파일을 불러올 수 있도록 config.js 파일에 정보를 추가해두어야 한다.


- 모든 커피숍을 조회하는 코드는 일반적으로 데이터를 조회하는 방식과 차이가 없다.

- 데이터가 크게 늘어나게 되면 전체 검색 기능은 제한되기 때문에 limit 속성을 지정해 조회하는 데이터 개수에 제한을 두는 것이 좋다.


- 커피숍의 스키마 객체는 database.CoffeeShopSchema로 참조 가능하며, 모델 객체는 database.CoffeeShopModel 객체로 참조 가능하다.

- 모델 인스턴스 객체를 선언할 때 파라미터에는 클라이언트로부터 전달받은 이름, 주소, 전화번호, 경도, 위도 좌표를 순서대로 넣어 준다.

- save() 메소드를 호출하게 되면 입력한 커피숍 정보가 데이터베이스에 정상적으로 저장된다.


- 라우터의 마지막에는 항상 작성한 코드를 메인 파일에서 불러올 수 있도록 export시킨다.(module.exports.list = list;)

- html을 이용해 커피숍 정보를 데이터베이스에 추가하며 리스트를 보여주도록 작성할 수 있다.

 

12-2 가장 가까운 커피숍 찾기

 

- 가까운 커피숍을 찾는 방법에는 3가지 대표적인 방법이 있으며 이들을 구현한다.

 

- 가장 가까운 커피숍을 조회하고자 할 때는 geometry 속성 내 near() 메소드를 호출한다. (find().where(속성 이름).near(조회 조건);)

- 경도와 위도에 대한 속성인 lonitude, latitude는 숫자 타입이 들어가야 하기 때문에 parseFloat() 메소드를 호출해 문자열을 숫자로 변환해준다.

- limit() 함수는 조회 결과의 개수를 제한해주는 역할을 한다.

 

- 위의 모든 위치를 찾을 때와 같이 findNear 함수도 exports를 통해 module에 넣어둔다.

- 두 위치 사이의 거리를 알고자 한다면 아래의 코드를 활용하면 도움이 된다.

 

- 각 라우팅 함수들은 config에 정보를 추가해준다.

 

 

12-3 영역 안의 커피숍 찾기

- 찾고자 하는 범위 영역의 종류는 원으로 설정하는 방법과 사각형 박스로 설정하는 방법으로 나뉜다.

 

- 일정 범위를 찾고자 할 때는 geometry 속성의 within() 메소드를 호출한다. (find().where(속성 이름).within(조회 조건);)

 

- 클라이언트로부터 검색할 대상들이 포함될 사각형 박스의 왼쪽 위 경위도 좌표부터 오른쪽 아래 경위도 좌표를 전달받는다. 이 좌표값들은 모델 객체의 findWithin() 메소드를 호출하게 되면 파라미터에 전달된다.

 

12-4 반경 안의 커피숍 찾기

 

- 반경은 사용자가 있는 위치를 기준으로 원을 그렸을 때 만들어지는 영역이다. 이는 곧 사용자 위치에서 일정 거리 내에 있는 장소들을 찾고자 할 때 사용한다는 것이다.

- 몽고디비에서는 within() 메소드에 반경 정보를 넣어주면 그 안에 들어있는 정보들을 찾을 수 있도록 지원한다.

 

- within() 메소드를 호출할 때 내부 조회 조건에는 여러 가지가 있다.

center : 기준점의 좌표를 전달한다.

radius : 기준점으로부터의 거리를 전달한다.

- within() 메소드를 통해 전달되는 좌표값들은 숫자 타입이여야 하기 때문에 parseFloat() 메소드를 통해 숫자로 변환해준다.

- 클라이언트로부터 기준점의 경위도 좌표와 기준점으로부터의 거리를 전달받으며 해당 값을 모델 객체의 findCircle() 메소드를 통해 전달한다.

 

12-5 지도에 커피숍의 위치 표시하기

 

- 위 4 챕터들은 결과 위치를 좌표값으로 표시해 사용자가 정확히 어디에 위치하고 있는지 알아보기 힘들다는 문제가 있다.

 

지도에 내 위치 보여주기

- 지도를 활용하는 여러 방법 중 구글 맵을 활용한다.

- public 폴더 안에 map.html 파일을 만든다.

 

- body 태그에 붙어있는 onLoad를 통해 위에서 작성한 스크립트를 불러온다.

- 직접 작성한 스크립트 위의 src를 통해 불러와진 주소는 구글 맵 라이브러리를 불러온 것이다.

- 구글 맵 등 외부 API를 사용하기 위해서는 API 키를 발급받아야 한다.

- 지도를 초기화하기 위한 파라미터

center : 지도가 어느 지역을 보여줄 것인지에 대한 좌표를 지정한다. 자바스크립트 안에 lat와 lng 속성으로 좌표값을 넣어준다.

ex) {lat: 37.57321, lng: 127.067523}

zoom : 지도의 확대 정도를 지정한다. 구글맵은 이미지를 받아오는 방식이기 때문에 확대 수준에 따라 서버에 이미지가 나뉜다. 값이 더 클수록 더 세밀한 지도 이미지를 얻어올 수 있다.

- 지도를 보여줄 때 사용자의 위치도 아이콘으로 함께 보여준다면 더 명확하게 알려줄 수 있게 된다. 해당 위치는 마커를 통해 표시가 가능하며 google.maps.Marker 프로토타입을 활용해 객체를 만들어 구현 가능해진다.

- 마커 객체 내의 setMap 메소드를 호출하면 지도에 마커가 추가된다.

- 마커의 속성에는 position, icon, animation 속성이 사용된다.

position : 마커가 추가될 위치를 지정한다.

icon : 아이콘 이미지를 지정한다.

animation : 마커에 애니메이션을 부여한다.

 

가장 가까운 커피숍을 찾아 지도 위에 보여 주기

 

- 클라이언트가 확인할 수 있는 화면을 출력하기 위해 html 파일을 작성한다.

- 모든 라우터 함수 및 스키마 파일들은 설정 파일에 정보를 기록해주어야 한다. 그렇지 않으면 메인 파일이 이를 인식하지 못해 오류가 발생하게 된다.

 

- 클라이언트로부터 경위도 좌표값을 파라미터로 받아 데이터베이스로부터 조회하는 부분은 같으나 CoffeeShopModel 객체의 findNear 메소드를 실행한 결과를 받아 처리하는 부분에서 차이가 발생한다.

- if (results.length > 0) ...부분에서 응답 객체인 res의 render 메소드를 실행한다.

- render 메소드는 뷰 템플릿을 이용해 응답 웹 문서를 만든 후 클라이언트로 응답을 보내주는 역할을 한다.

- 데이터베이스로부터 조회된 결과값이 존재할 경우 findnear.ejs 뷰 템플릿을 사용해 결과 웹 문서를 생성한 후 그것을 클라이언트에게 다시 돌려준다.

- findnear.ejs를 통해 전달되는 자바스크립트 객체에는 응답 결과 객체와 함께 조회에 사용된 paramLatitude, paramLongitude 속성이 들어있다.

 

- cneterLocation은 사용자의 위치좌표값을 가지고 있어야 하기 때문에 paramLatitude와 paramLongitude 변수값을 할당한다.

- coffeeLocation 변수에는 데이터베이스로부터 조회한 커피숍의 위치 좌표를 할당한다.

- 결과 좌표값은 배열형태로 경도, 위도 순으로 기록되어 있기 때문에 [0], [1] 인덱스를 불러오는 과정을 통해 값을 lat와 lng 속성에 할당한다.

전체적인 과정

1) 사용자가 조회할 웹 문서인 nearcoffeeshop2.html 파일을 만든다.

2) 웹 문서의 버튼을 눌렀을 때 요청할 findNear2 메소드를 config.js에 등록한다.

3) coffeeshop.js 파일 안에 findNear2 메소드를 추가하고, 결과 웹을 만들 때 findnear.ejs 뷰 템플릿을 사용할 수 있도록 코드를 수정한다.

4) findnear.ejs 파일을 만들고 findNear2 메소드로부터 전달된 데이터를 사용해 지도 위에 마커를 표시한다.

 

일정 범위 안의 커피숍을 찾아 지도 위에 보여주기

- 위의 findWithin 메소드의 기능을 수정하여 기능을 구현할 수 있다.

 

- 위의 findNear 메소드때와 마찬가지로 if (results.length > 0) ... 이후의 코드에 변동이 있다.

- 응답 객체인 res의 render 메소드를 실행하게 되며 클라이언트로 결과값을 응답으로 보내준다.

 

- 클라이언트로 결과를 표시해주기 위한 뷰 템플릿이다.

- 기존 findnear.ejs와 다른 점은 검색을 위해 사용했던 사각형 영역 역시 지도 위에 함께 표시한다는 것이다.

- coords 변수에는 사각형 영역을 그리기 위한 좌표가 들어 있으며, 이를 활용해 Rectangle 객체를 만들었다.

- google.maps.Rectangle 프로토타입 형태로 사각형 객체를 만들게 된다면 지도 위에 네모를 출력시킬 수 있게 된다.

- 사각형에는 strokeColor와 fillColor 등의 CSS를 적용했다.