클라우드 컴퓨팅 & NoSQL/Vert.x & Node.js

빠르게 훝어 보는 node.js - #6 MongoDB 연동 (mongo-native)

Terry Cho 2014. 4. 3. 23:49

빠르게 훝어보는 node.js

#6- mongo-native 모듈을 이용한 MongoDB 연동

조대협 (http://bcho.tistory.com


아래 글은 오래되서 monk를 이용한 방식으로 새로운 글을 작성하였습니다.

새 글은 node.js 4.x , express 4.x 에서 기동하도록 작성되었습니다.

링크 : 

http://bcho.tistory.com/1080

Persistence 연동

node.js DB NoSQL등의 연동을 지원하는데, 이 역시 철저하게 non-blocking io 방식으로 동작한다. db 연결 socket을 열어서 query를 던져놓고, query 결과가 오면 이벤트를 받아서 callback 함수로 처리하는 순서이다.

그러면 여기서는 몇가지 persistence 연동 방식에 대해서 알아보도록 한다.

MongoDB

먼저 mongodb nosql 데이터베이스중에 가장 많이 사용되는 제품중의 하나이다. Json document를 저장하는 스타일의 document db이며, index grouping과 같은 RDBMS와 유사한 기능까지 지원하기 때문에 사용이 매우 쉽다.

설치 테스트

Mongodb mongodb.org에서 다운로드 받을 수 있다. Mongodb는 무료 tutorial이 잘되어 있는 것이 많은데, https://university.mongodb.com/ 에 가면 언어별 (node.js용도 있다) 튜토리얼이 있으니 참고하기 바란다.

윈도우를 기준으로 다운 받아서 압축을 푼후,  db 디렉토리를 만들어 주어야 하는데, 필요한 곳에 디렉토리를 만든다. 여기서 C:\dev\mongodb-win32-x86_64-2.4.3 \data 에 만들었다.

C:\dev\mongodb-win32-x86_64-2.4.3 에서 다음과 같은 명령어를 이용하여 구동한다.

.\bin\mongod --dbpath C:\dev\mongodb-win32-x86_64-2.4.3\data

인스톨이 끝났으면 간단한 테스트를 해보자, ./bin/mongo.exe를 수행하면 Java Script 기반의 쉘이 수행된다. 이해를 돕기 위해서 하나의 테이블에 Insert , select, delete, update를 수행하는 명령을 SQL 문장과 비교해서 소개한다.

Insert

SQL          : insert into users ("name","city") values("terry","seoul")

Mongo DB     : db.users.insert({_id:"terry",city:"seoul"})

 

Select

SQL          : select * from users where id="terry"

Mongo DB     : db.users.find({_id:"terry"})

 

Update

SQL          : update users set city="busan" where _id="terry"

Mongo DB     : db.users.update( {_id:"terry"}, {$set :{ city:"Busan" } } )

 

Delete

SQL          : delete from users where _id="terry"

Mongo DB     : db.users.remove({_id:"terry"})

 

간단하게 나마, mongodb query에 대해서 설명하였다.

개념적을 약간 더 설명하자면, dbRDBMS db와 같은 개념으로 보면 되고, collection rdbms의 하나의 테이블로 보면 된다. collection에 들어가는 데이터 필드중에서 _id predefined된 필드로 해당 데이터에 대한 primary key로 생각하면 되고, 위와 같이 사용자가 직접 값을 넣어서 입력할 수도 있고 또는 값을 넣지 않으면 mongodb에 의해서 unique한 값으로 자동 지정된다.

조금 더 자세한 쿼리에 대한 설명은 http://docs.mongodb.org/manual/crud/ 를 참고하기 바란다.

GUI 툴로는 robomongo 라는 툴이 있다. http://robomongo.org/



mongodb-native 모듈을 이용하기

node.js에서 mongodb를 연결하는 방법은 여러가지가 있다. 먼저 가장 널리 사용되는 mongo-native 모듈에 대해서 알아보자 https://github.com/mongodb/node-mongodb-native

1) mongo native module 설치하기

mongo native module

% npm install mongo 로 설치가 가능하며, 설치중에 native 모듈을 컴파일 하기 때문에 반드시 컴파일러 (Windows의 경우 Visual C++)이 깔려 있어야 한다.

2) 간단한 쿼리 수행하기

설치가 끝났으면 간단하게 select를 해서 내용을 json으로 리턴하는 코드를 만들어보자. Select를 할것이기 때문에 미리 mongo에 값을 넣어보자

% mongo.exe 를 실행한 후에 다음 쿼리를 수행한다.

db.users.insert({_id:'terry',city:'seoul'});

db.users.insert({_id:'cath',city:'suwon'});

 

제대로 입력이 되었는지 select를 해본다.



아래는 mongodb 도구인 robomongo를 이용해서 데이터가 들어가 있는 것을 확인한 화면이다.



자아 그러면 이제 코드를 구현해보자

var express = require('express');

var routes = require('./routes');

var http = require('http');

var path = require('path');

var app = express();

var MongoClient = require('mongodb').MongoClient

var Server = require('mongodb').Server;

먼저 위와 같이 MongoClient Server 클래스를 생성한다

// all environments

app.set('port', process.env.PORT || 3000);

app.set('views', path.join(__dirname, 'views'));

app.set('view engine', 'ejs');

app.use(express.logger('dev'));

app.use(express.json());

app.use(express.urlencoded());

app.use(express.methodOverride());

app.use(app.router);

app.use(express.static(path.join(__dirname, 'public')));

 

var mongoclient = new MongoClient(new Server('localhost',27017,{'native_parser':true}));

var db = mongoclient.db('test');

다음으로 mongoclient를 생성한다. 일종의 Connection 설정이라고 보면 되는데, 설정만 여기서 하는 것이지 실제 연결은 open을 호출해야 된다. 다음으로 client연결을 이용해서 사용할 db를 선택한다. 여기서는 ‘test’ db를 사용하였다.

 

app.get('/', function(req,res) {

   db.collection('users').findOne({},function(err,doc){

       if(err) throw err;

       res.send(doc);

   });

});

Db 객체를 얻었으면, 쿼리를 수행할 수 있는데, collection(‘collection.즉 테이블명’)으로 collection을 선택한후, findOne()메서드를 이용하여 하나의 row select를 하였다. 비동기 호출이기 때문에 query 수행이 끝나면, function(err,doc) callback 함수를 호출하는데, 위에서는 에러가 났을때는 err throw하고 에러가 없을 경우에는 리턴받은 json document response return하도록 하였다. 여기 까지 끝났으면, 실제 mongodb를 연결해보자 연결은 mongoclient.open을 하면 되는데, 연결이 완료되면 open안에 정의된 callback method를 호출한다.

아래코드를 보면 이 callback 메서드 안에서 httpServer를 띄운 것을 볼 수 있는데, http server가 기동되서 mongodb를 사용하기 때문에 mongoclient를 먼저 띄우기 위해서 httpserver callback안에 넣었다.

 

mongoclient.open(function(err, mongoclient) {

    if(err) throw err;

    console.log('mongo client connected');

    http.createServer(app).listen(app.get('port'), function(){

        console.log('Express server listening on port ' + app.get('port'));

    });

 

});

 

코드 구현이 끝났으면 실행해보자. 다음과 같이 레코드가 JSON으로 리턴됨을 확인할 수 있다.



이 코드에서 보면 mongoclient를 하나만 생성하였다. 그렇다면 내부적으로 물리적인 connection이 하나만 생길까? Mongoclient는 내부적으로 connection pooling을 이용한다. 그래서 별도의 설정을 해주지 않아도 내부적으로 여러 개의 connection을 여는데, default 5개의 connection을 열도록 되어 있고, connection의 수는 open 옵션에서 조정할 수 있다.

var mongoclient = new MongoClient(new Server('localhost',27017,{'native_parser':true,'poolSize':8,'maxPoolSize':10}));

 

위의 코드는 max connection 10개로 하고, 초기에 poolSize 8개로 지정한 경우이다. 실제로 기동해보면 8개의 connection이 생성되었음을 확인할 수 있다.

3) Insert, update and delete

Select는 해봤는데, 그러면 insert,update,delete는 어떻게 할까? mongodb query와 매우 유사하다.먼저 Insert를 보자

db.collection('users').insert({city:'suji'},function(err,doc){

       console.log('inserted '+doc[0]._id+':'+doc[0].city);

   });

위와 같이 collection을 선택한다음에, insert메서드를 이용해서 json document를 넣으면 된다. 위에서는 callback function을 지정한 예인데, callback function은 생략할 수 도 있다. 위의 코드를 잘 보면 앞에 예제와는 다른게 _id값을 입력하지 않은 것을 볼 수 있다. 이 경우 _id값은 자동으로 mongodb가 생성하여 unique한 값을 넣게 된다.



그리고 insert된 값은 callback의 두번째 인자에서 array 형태로 넘겨 받게 된다.

또는

db.collection('users').insert([{city:'suji'},{city:'busan'}],function(err,doc){

          

와 같은 배열 형태를 사용하면, 하나의 insert문장으로 여러 개의 document를 동시에 insert할 수 있다.( batch insert가 가능하다)

삭제는 insert와 유사하게 remove 메서드를 이용하면 된다.

db.collection('users').remove({city:'busan'},function(err,doc){});

위의 예제는 city 필드가 busan인 것을 삭제한 것인데, _id 필드 이외에는 index로 지정이 되어 있지 않기 때문에 index를 지정하지 않은 필드로 삭제등을 했을 경우 table full scan이 발생할 수 있으니 주의하도록 해야 한다. (한마디로 느려진다는 이야기)

다음으로 수정은 collection.update 메서드를 이용하면 된다.

db.collection('users').update({_id:'terry'},{$set:{'sex':'male'}},function(err,doc){

       throw err;

   });

_id terry document‘sex’ 필드를 ‘male’로 변경하는 쿼리이다. 이미 ‘sex’필드가 있으면 그 내용을 ‘male’로 바꾸고, 없으면 새롭게 ‘sex’필드를 만든다.앞에 $set을 줬기 때문에 ‘sex’필드만 내용을 바꾸는데,

db.collection('users').update({_id:'terry'},{'sex':'male'},function(err,doc){

       throw err;

   });

$set을 빼버리게 되면 _id=’terry’ document의 내용을 {‘sex’:’male’}로 바꿔 버린다. (필드만 추가하는게 아니다.)

4) Search

앞에서는 findOne만 해서 하나의 record query 하는 예제 였는데, 여러 개의 record를 받고 싶으면 find 메서드를 사용해서 검색 조건을 주고 .toArray를 호출하면 인자로 넘어가는 callback함수의 docs 인자에 쿼리 결과를 배열로 리턴해준다.

db.collection('users').find({city:'suji'}).toArray(function(err,docs) {

       if (err) throw err;

       res.send(docs);

       for (i = 0; i < docs.length; i++) {

           console.log(docs[i].city);

       }

   });

여기서는 아주 기본적인 API만을 다뤘기 때문에 자세한 API들은 http://mongodb.github.io/node-mongodb-native/api-generated 를 참고하기 바란다