LBS(Location Based Service)는 위치기반으로 제공하는 서비스 서버이다.
데이터베이스로 특정 위치의 정보를 제공 또는 조회할 수 있다.
위도와 경도를 기준으로 위치를 확인한다.
특히 위치 정보를 저장 혹은 조회할 때 공간 인덱싱(Spatial Indexing) 방법을 통해 인덱스를 만들어 조회하는 속도를 빠르게 할 수 있다. 이는 몽고 DB에서 GeoSpatial Indexing라고 불린다.
위치 정보 조회 방법
- 사용자 위치에서 가장 가까운 지점
- 사용자가 보고 있는 범위 안의 지점
- 사용자가 있는 곳에서 일정 반경 안에 있는 지점
위치 데이터 종류
종류가 다양하지만 대표적인 3가지가 있다.
Point : 현재 위치처럼 특정한 지점
LineString : 도로와 같이 이어진 위치
Polygon : 잠실동, 역삼동과 같은 지역
'스타벅스' 카페의 위치 정보를 예시로 들어서 위치 정보를 입력 또는 조회하는 예제를 함께 살펴볼 것이다.
그전에 앞서,
Express 서버 연결, DB 연동, 라우팅 처리와 관련해서는
https://realzzu.tistory.com/82
따로 분리하여 이곳에 작성하였으니 참고 바란다.
html 파일
편의상 html 파일을 먼저 확인하고 가겠다.
순서대로 입력 / 조회 파일이다.
위치 정보 입력 - addCoffeeShop.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>
<title>스타벅스 추가</title>
</head>
<body>
<h1>스타벅스 추가☆⌒(*^-゜)v</h1>
<br/>
<form action="/process/addCoffeeShop" method="post">
<table>
<tr>
<td><label>이름</label></td>
<td><input type="text" name="name"/></td>
</tr>
<tr>
<td><label>주소</label></td>
<td><input type="text" name="addr"/></td>
</tr>
<tr>
<td><label>전화번호</label></td>
<td><input type="text" name="tel"/></td>
</tr>
<tr>
<td><label>경도</label></td>
<td><input type="text" name="longitude"/></td>
</tr>
<tr>
<td><label>위도</label></td>
<td><input type="text" name="latitude"/></td>
</tr>
</table>
<input type="submit" value="추가"/><br/>
</form>
<a href="/public/listCoffeeShop.html">전체보기</a>|
<a href="/public/nearCoffeeShop.html">가까운 매장보기</a>|
</body>
</html>
스타벅스의 위치정보를 입력하기 위한 html 코딩이다.
submit 하는 순간 action의 경로로 넘어가게 된다.
특정 지점 찾기 - nearCoffeeShop.html
<title>가까운 스타벅스 찾기</title>
</head>
<body>
<p>역삼역</p>
<br/>
<form action="/process/nearCoffeeShop" method="post">
<table>
<tr>
<td><label>경도</label></td>
<td><input type="text" name="longitude" value="127.036430"/></td>
</tr>
<tr>
<td><label>위도</label></td>
<td><input type="text" name="latitude" value="37.500613"/></td>
</tr>
</table>
<input type="submit" value="찾기"/>
</form>
</body>
역삼역을 기준으로 가까운 스타벅스 지점을 찾기 위한 코딩이다.
경도와 위도에는 바로 역삼역에 해당되는 값이 나오도록 넣어주었다.
따라서 submit만 눌러주면 값이 들어가게 된다.
config.js
routeInfo:[
{file:"./coffeeShop",path:"/process/addCoffeeShop",method:"add",type:"post"},
{file:"./coffeeShop",path:"/process/nearCoffeeShop",method:"findNear",type:"post"}
]
라우터 처리해준 부분만 가지고 왔다. (해당 내용은 바로 전 글에 있으니 참고 바란다!)
어떤 방식으로 path가 넘어오는지에 따라 file 경로의 method로 넘어간다.
스키마 정의 - coffeeshopSchema.js
- 스키마 정의
var Schema = {};
Schema.createSchema = function(mongoose){
var CoffeeShopSchema = mongoose.Schema({
name:{type:String,index:"hashed"},
addr:{type:String},
tel:{type:String},
geometry:{
type:{type:String,"default":"point"},
coordinates:[{type:"Number"}]
},
created:{type:Date,"default":Date.now}
});
CoffeeShopSchema.index({geometry:"2dsphere"});
Schema.createSchema 함수를 만들어 DB에 저장할 스키마를 정의해준다.
들어갈 컬럼은 name, addr, tel, geometry, created이다.
geometry의 경우 위치정보 유형이 point로 설정하였으며
coordinates은 좌표값이다. 위도와 경도가 들어가므로 값이 2개이기 때문에 배열로 저장한다.
index({geometry:"2dsphere"})는 공간인덱싱으로 속도를 향상해준다.
위치 좌표를 2d sphere타입으로 설정해준 것이다.
- 스키마 메소드 추가 (static)
위치 정보를 추가 또는 조회하기 위한 메소드를 생성할 것이다.
CoffeeShopSchema.static("findNear",function(longitude,latitude,maxDistance,callback){
console.log("findNear 호출");
this.find().where("geometry")
.near({center:{type:"Point",coordinates:[parseFloat(longitude),parseFloat(latitude)]},
maxDistance:maxDistance}).limit(1).exec(callback);
});
console.log("CoffeeShopSchema 정의!");
return CoffeeShopSchema;
}
module.exports = Schema;
- findNear
가장 가까운 스타벅스 지점을 조회하기 위한 함수이다.
find().where(속성이름).near(조회조건)에 따라 입력해준다.
parseFloat는 경도와 위도가 소수 단위이므로 써주었다.
maxDistance : 기준점으로부터 최대 거리
exec(callback): 실행한 후 callback함수 호출
coffeeShop.js
- 위치정보 저장하는 함수
var add = function(req,res){
console.log("add함수 호출..");
var name = req.body.name;
var addr = req.body.addr;
var tel = req.body.tel;
var longitude = req.body.longitude;
var latitude = req.body.latitude;
var database = req.app.get("database");
if(database){
addCoffeeShop(database, name, addr, tel, longitude, latitude, function(err,result){
if(err){
res.writeHead("200",{"Content-Type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>스타벅스 추가중 에러발생</h2>");
res.write("<p>"+ err.stack+ "</p>");
res.end();
return;
}
if(result){
res.writeHead("200",{"Content-Type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>스타벅스 추가 성공</h2>");
res.end();
}else{
res.writeHead("200",{"Content-Type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>스타벅스 추가 실패</h2>");
res.end();
}
});
}else{
res.writeHead("200",{"Content-Type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>데이터베이스 연결 실패</h2>");
res.end();
}
};
html 파일에서 받아온 값들을 받는다.
database가 연결되어있다면 addCoffeeShop 함수를 실행하게 된다. 함수는 밑에서 확인할 수 있다.
따라서 에러가 있을 시 에러 발생 문자를 출력하여 종료한다.
나아가 결과의 유무, 데이터베이스 연결 유무에 따라 if 조건으로 화면에 문자를 출력한다.
var addCoffeeShop = function(database,name,addr,tel,longitude,latitude,callback){
var coffeeShop = new database.CoffeeShopModel(
{name:name,addr:addr,tel:tel,
geometry:{
type:"Point",
coordinates:[longitude,latitude]
}
}
);
coffeeShop.save(function(err){
if(err){
callback(err,null);
return;
}
callback(null,coffeeShop);
});
}
방금 위에서 호출했던 addCoffeeShop 함수이다.
매개변수로 값들을 받아 CoffeeShopModel 모델 객체를 생성한다.
위도, 경도의 경우 값이 2개이므로 배열로 정의한다.
또한 save로 값을 저장하며 에러 유무에 따라 콜백함수에 갑을 다르게 넣어준다.
- 위치정보 조회하는 함수
var findNear = function(req,res){
var maxDistance = 100;
var longitude = req.body.longitude
var latitude = req.body.latitude;
var database = req.app.get("database");
if(database){
database.CoffeeShopModel.findNear(longitude,latitude,maxDistance,function(err,result){
//if(err) 생략
if(result){
res.writeHead("200",{"Content-Type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>스타벅스 리스트</h2>");
res.write("<div><ul>");
for(var i=0;i<result.length;i++){
var curName = result[i]._doc.name;
var curAddr = result[i]._doc.addr;
var curTel = result[i]._doc.tel;
var curLongitude = result[i]._doc.geometry.coordinates[0];
var curLatitude = result[i]._doc.geometry.coordinates[1];
res.write("<li>#" + (i+1) + " : " +
curName + ", " +
curAddr + ", " +
curTel + ", " +
curLongitude + ", " +
curLatitude + "</li>");
}
res.write("</ul></div>");
res.end();
}else{
//리스트 조회 실패 출력 생략
}
});
} //else - 데이터베이스 연결 실패 출력 생략
};
maxDistance의 경우 100m는 10, 1km는 100을 입력한다.
html에서 위도, 경도의 값을 받아온다.
마찬가지로 데이터베이스가 연결되었을 때 database.CoffeeShopModel.findNear를 호출하며
이는 coffeeshopSchema.js 파일에서 보았던 findNear함수이다.
따라서 에러의 유무, 결과 존재 유무, 데이터베이스 연결 유무에 따라서 출력되는 내용이 달라진다. 위의 추가 함수와 내용이 같은 부분은 생략하였다.
if문에서 결과가 있을 때에는 값이 여러개일 수 있으므로 <ul>로 값을 출력하도록 하였다.
데이터베이스에 저장된 값이기 때문에 ._doc로 값을 가져온다.
경도와 위도는 배열이므로 인덱스 번호를 통해 각각 가져온다.
이렇게 해서 위치기반 서비스 서버 예제를 알아보았다!
내용이 조금 복잡해서 글을 2개로 나누어 작성했다.
웹페이지에 실제로 지도가 출력되며 위치가 표시되는 내용도 배웠지만
오늘은 기본적인 정도만 알아보았다.
4월 12일 수업🌕
'개발 교육 TIL > back-end' 카테고리의 다른 글
[Node.js] Express 서버 연결, 데이터베이스, 라우터 - 파일 분리 (0) | 2022.04.12 |
---|---|
[Node.js] 비밀번호 암호화 - crypto, virtual함수, salt (2) | 2022.04.06 |
[Node.js] mongoose 모듈 - 설치 및 예제 (회원가입, 로그인) (0) | 2022.04.05 |
[Node] Express 설치&서버 연결 / post 데이터 및 오류페이지(404) 출력 (0) | 2022.04.04 |
[node] 내장모듈 - path, fs (0) | 2022.04.01 |