몇년전에 ROR (Ruby on Rails)이 이슈화 될때의 유명한 ScreenCast 가 생각납니다. '15분만에 블로그' 만들기 였는데, 그 뒤로 새로운 언어나, 새로운 웹프레임워크의 강력함을 설명하기 위해서 항상 블로그를 얼마만큼 빠르게 만들어 낼 수 있는가가 그 척도가 되는 듯 합니다. 

Node.js
도 예외는 아닌듯 싶은데, 실제로도 빠르게 만들어 볼 수가 있으며, 사용하는 사람에게 프로그램을 짜는 즐거움까지 전달합니다. 

제가 예전에 정리한 포스트 에서 마지막에 실전 예제라고 소개하는 직접 블로그 만들기에 관한
 예제입니다. 소스가 예전 버젼에 맞춰져 있기 때문에 현행화를 조금 거쳤습니다. 

환경
    

    OSX Mountain Lion 
    Node.js v0.8.14 
    express 3.0.1
    jade 0.27.7


입니다. 그 외 소소한 것은 Default 를 쓴다고 해도 별 무리는 없을 듯 합니다. (OSX 에서 node.js 와 
npm 설치 방법은 여기 참조 하시면 됩니다)

1. express 를 설치해 줍니다.
    
   

 $ sudo npm install -g express 

       
express 를 전역으로 설치해 주라는 명령입니다. 전역은 별 다른 차이가 없다고 보시고 다만 명령행이 존재한다면 그 명령을 command line 상에서 사용할 수 있다는 점이 다르다고 보면 됩니다. 

2. 프로젝트를 만들어 줍니다. blog 라는 이름이 좋아보이는 군요. 

       $ cd ~/work
       $ mkdir blog
       $ cd blog
       $ express -c stylus
       $ npm install -d 


프로젝트를 만들고 연관된 하위 라이브러리들을 전부 설치해 주라는 명령입니다. 

3. nodemon 을 설치해줍니다. 

       $ sudo npm install nodemon


express 로 코딩을 하다보면 뭐 변경될 때마다 Ctrl-c 누르고 '화살표 위'를 누르는 횟수가
 비약적으로 많아집니다. nodemon 은 js 파일이 변경될 때마다 자동으로 파일을 재구동해주는 편리한 툴입니다. 손가락에 병 생기기 전에 미리 미리 깔아줍시다. 

4. 기본 템플릿으로 만들어진 것을 구동시켜 봅니다. 

       $ cd ~/work/blog
       $ nodemon app.js


구동시키면 다음과 같은 에러가 발생합니다. 

`doctype 5` is deprecated, you must now use `doctype html`
(예전하고 변경됐군요.)
       
 ~/work/blog/views/layout.jade 파일을 열어서 에러메시지처럼 변경해줍니다. 

 doctype 5 -> doctype html 


 로 변경해줍니다. 

http://localhost:3000 을 브라우져에 입력하면 아무것도 안했지만 자동으로 만들어진 내용들이 화면에 출력됩니다. 

5. 데이타베이스를 만들어 봅니다. 나중에는 Mongodb 와 연결할 것입니다. 하지만 처음에는 메모리에
 간단한 타입으로 만들어서 테스트 해 볼것입니다. 

       $ cd ~/work/blog
       $ emacs articleprovider-memory.js 



자 수많은 callback 들하고 higher-order function 들이 난무하기 때문에 정신 사납지만 조금만 익숙해지면 쉽게 볼 수가 있을 것입니다. 사실 고백하자면 Synchronous 방식에 익숙해져 있다면 Node.js 의 거의 대부분을 이루고 있는 Asynchronous 방식이 많이 혼동될 수가 있습니다. 언젠가 언급을 하겠지만 미리 소스를 읽기 쉽게 가볍게 설명을 드리면 

- Callback 함수는 소스 볼 때 잊어버리는 것이 편하다. 
  Callback 은 Asynchronous 일 때 함수의 원하는 행동이 끝나고 나서 추가로 동작하는 방식으로 구동됩니다. function A (id , callback ) ; 이라는 함수가 있다면 A 의 내용이 수행되고 그 다음에 차례로 callback 이라는 함수가 실행된다고 보는 것이 편합니다. 

- function A (id, function (err, result) {..}) ; 와 같은 형식으로 선언이 되어 있다면 
  A 를 실행하기 위해서 파라미터로 id 가 필요하고 그 결과가 err 와 result 로 나온다 라고 보시면 편하게 이해되실 수 있습니다. 

드릴 말씀이 많지만 이 부분을 기억하시고 소스를 보시는 것이 마음 편하게 이해되실 듯 합니다. 


6. 5 에서 만들어준 방식과 app.js 와 연동 시켜보는 작업을 해 줍니다. 

       app.js 를 열어서 

       var express = require('express')
          , routes = require('./routes')
 , user = require('./routes/user')
 , http = require('http')
 , path = require('path');


의 아래에 
       

       var ArticleProvider = require('./articleprovider-memory').ArticleProvider;


과 같이 추가해 줍니다. 

       // app.get('/', routes.index);


기존의 소스를 커멘트 처리 해주시고 
       
그 위에다가 
 


을 추가해 줍니다. 

부연 설명을 드리자면 GET 방식으로 들어온 요청 '/' 에 대해서 function (req, res) 을 수행하라는 내용입니다. 그리고 다시 그 안으로 들어가 보면 articleProvider.findAll 을 수행하고 난 뒤에 function (error, docs) 를 수행하라는 뜻입니다. articleProvider.findAll 의 결과는 error, docs 가 되겠지요? 이 부분을 확실하게 이해를 하는 것이 좋을 것입니다. 거의 대부분이 이런 형식으로 이루어져 있습니다. Asynchronous 방식과 higher-orders function (함수를 입력 파라미터로 넘겨 받는 방식) 그리고 Anonymous function 이 섞여있는 Node.js 의 전형적인 방식의 함수 모습입니다. 

app.get 은 URL 로 넘겨온 값을 '매칭' 하는 함수이고 그것을 실행한 결과를 function (req, res) 로
 넘기고, articleProvider.findAll 은 articleprovider-memory.js 에서 선언한 함수입니다. 저장된 데이터를 전부 가져오고 난 뒤에 파라미터로 선언된 Anonymous function 인 function (error, docs) 를 실행하고 그 결과값 - 저장된 데이터 - 가 docs 로 넘겨집니다. 

http://localhost:3000 에 다시 잘 동작하는 지 확인해 봅니다. 

7. 이제 포장 작업을 해줄 차례입니다. 

       $ emacs ~/work/blog/views/index.jade


로 기본 화면 구성을 열어줍니다. 


jade
 라고 불리는 HTML 엔진입니다. 위 처럼 써주면 대응되는 HTML 코드로 바꾸어 줍니다. 자세한 것은 따로 공부를 하시면 됩니다. (저도 뭐 HTML 은 고수가 아니라서..)

extends layout 이라는 것은 layout.jade 로 부터 확장한다는 뜻입니다. 기초적인 설정은 전부 그곳에
 있다고 해도 과언이 아니겠지요? 

       $ emacs ~/work/blog/views/layout.jade 


로 열어줍니다. 



body 부분에 쓰여져 있는 block content 가 index.jade 의 block content 와 대응되리라는 것을 쉽게
 알 수가 있습니다. (응?..)

8.
jade 파일이 준비가 됐다면 app.js 에서 바꾸어 줄 필요가 있습니다. 


여기서 res.send 는 기본적인 내용을 전달하는 것이므로 (그래서 json 기반의 app 서버 작성시 편합니다) 렌더링 해주는 함수로 변경해줘야 합니다. 위 내용을 


index.jade 를 기본으로 삼되, title 에는 'Blog' 값을 넘기고 , articles 에는 docs 를 넘기라는
 내용입니다. 

9. 이쁘장 (?) 하게 바꾸어줄 시간입니다. (It's CSS Time!!)

귀찮으니까 나중에 쓸 내용까지 전부 CSS 로 밀어 넣기로 합니다. 참 이 내용은 stylus 라는
 라이브러리를 이용합니다. 

       $ emacs ~/work/blog/public/stylesheets/style.styl





인덴테이션이 중요합니다. 이 파일을 작성하면 이 파일에서 style.css 를 자동으로 생성하는 것 같습니다. (자세히는 모르겠음..)
        
여기까지 작성이 완료 됐다면 http://localhost:3000 으로 확인 바랍니다. 색.. 색깔이 나올것입니다. 

10. 새로 글 작성하는 기능을 추가해 보겠습니다. MVC 모델에 따라서 , M 은 이미 작성되어 있고
 (articleprovider-memory.js) 로 , V 에 해당하는 부분을 작성해 줍니다. 

$ emacs ~/work/blog/views/blog_new.jade



음.. 암호 같지요? 저한테도 암호 같습니다. 당분간 외워서 하는 수밖에 없을 듯합니다. 

11. 10 에 대응되는 C (controller) 부분을 추가해 줍니다. 2개의 라우팅 부분을 추가해 줍니다. 

$ emacs ~/work/blog/app.js



app.get 은 GET 방식 , app.post 는 POST 방식입니다. app.get 방식으로 '/blog/new' 로 브
라우져에서 접속하면 blog_new.jade 를 렌더링 하고 , 그 렌더링 페이지에서 내용을 입력하고 Send 버튼을 클릭하면 articleProvider.save 함수를 호출하고 res.redirect 함수로 '/' 에 돌아간다는 내용입니다. http://localhost:3000/blog/new 로 이동해서 테스트 해보기 바랍니다. 

12. 이제 mongodb 를 붙일 시간입니다. 지금까지 만들어온 메모리 버젼은 소스 변경하면 (nodemon 이
 알아서 app.js 를 재구동하니..) 작성하고 테스트 한 내용이 날라가버립니다. 그런 귀찮음을 제거하고자 mongodb 를 연동하는 것입니다. 

먼저 mongodb 모듈을 설치해야 합니다. . 

$ emacs ~/work/blog/package.json




에 위와 같이 "mongodb": "*" 를 추가해 주고 

$ cd ~/work/blog
$ npm install -d 


를 실행 합니다. 
 

13. 이제
mongodb 와 연결해서 포스트 관리해 주는 articleprovider-mongodb 를 제작할 시간입니다.

$ cd ~/work/blog
$ emacs articleprovider-mongodb.js 




14. memory 에서 articleprovider-mongodb 로 바꼈기 때문에 연관된 부분을 app.js 에서 수정할 필요가 있습니다. 

$ cd ~/work/blog
$ emacs app.js


아래와 같이 변경 해줍니다. 

var ArticleProvider = require('./articleprovider-mongodb').ArticleProvider;
//var ArticleProvider = require('./articleprovider-memory').ArticleProvider;


 
메모리를 로딩하는 부분을 comment 처리해주고 그 위로 내용을 추가해 줍니다. articleprovider 를 mongodb 기반으로 바꾸어 준다는 내용입니다. 

var articleProvider = new ArticleProvider('192.168.0.87', 27017);
// var articleProvider = new ArticleProvider();


이 내용은 192.168.0.87 서버에 mongodb 가 띄어져 있고 27017 port 로 접속한다는 이야기
 입니다. 제 경우는 머신이 두대 있을 경우에 쓰는 방법이고 대부분 한 서버에서 같이 동작시킬 때는 192.168.0.87 대신 localhost 라고 입력해 주시면 됩니다. 

또 저와같이 다른 머신에 mongodb 데몬을 띄워논 경우에는 그 머신의 mongodb.conf 파일을 열어서 bind_ip 부분을 comment 처리 하거나 

bind_ip = 192.168.0.* 


으로 같은 대역대에 있는 접속을 허용하는 식으로 변경해 주어야 외부에서 접속이 됩니다. 물론 내부는 세팅해 줄 필요가 없습니다. mongodb.conf 파일은 ubuntu 경우에는 /etc/mongodb.conf 존재합니다. 

15. index 페이지에서 개별 개별 포스트를 클릭했을 때 , 내용을 상세하게 보여주는 페이지 부분에 대한 작성 입니다. 

먼저 V (views) 부분에 대한 처리입니다. 

$ emacs ~/work/blog/views/blog_show.jade



jade 는 꼭 시간을 들여서 익히셔야 합니다. (저도..) 웹 개발이 빨라집니다. 

16. index 페이지에서 개별 개별 포스트를 클릭했을 때 , C (controller) 에 해당하는 라우팅 하는
 부분을 app.js 에 추가해줍니다. 

$ emacs ~/work/blog/app.js




http://localhost:3000 으로 화면을 띄우고 개별 Post 를 클릭하셔서 제대로 동작되는 지 확인하시면 됩니다. 

17. 이제 개별 Post 에 댓글 기능을 추가해 보겠습니다. 16 에서 열어둔 파일에 마저 작업을 해
 줍니다. POST 방식으로 댓글을 Post 에 추가하는 로직입니다. 

$ emacs ~/work/blog/app.js



을 추가해 줍니다. View 에 해당하는 것은 언제 추가해 주냐고요? 15 에서 추가해 줬습니다. 자 이제 모든 것이 완성 됐습니다. http://localhost:3000 에서 확인해 주시기 바랍니다. 

원문 은 여기서 확인 가능합니다. 구버젼 node.js 와 express 로 되어 있어서 현행화 이슈가 있었기에 제가 정리하는 김에 현행화를 한 것입니다. 

제가 현행화한 모든 소스는 https://github.com/crazia/nodejs-mongodb 여기에서 다운 받을 수
 있습니다. 

$ cd ~/work
$ git clone git://github.com/crazia/nodejs-mongodb.git 
$ cd nodejs-mongodb
$ npm install -d 
$ node app.js 


하시면 쉽게 완성된 형태를 따라하실 수 있습니다. 

샤딩 (Sharding) 을 하는 방법 또는 복제 셋 (Replication) 을 하는 방법에 관한 예제는 잘 나와 있습니다. 그런데 실전에서는 복제와 샤딩을 동시에 하는 것이 일반적일 것입니다. 이것에 관한 예제가  많이 없더군요. 어쩌다 찾은 것이 한개 있지만 한 기계에서 가상으로 돌려보는 예제 입니다. 이러한 예제로는 샤딩을 제대로 테스트 할 수가 없더군요. 하지만 그 문서를 바탕으로 실제로 샤딩과 복제를 클러스터 환경에서 테스트 해 봤습니다. 

http://cookbook.mongodb.org/operations/convert-replica-set-to-replicated-shard-cluster/ 

위 내용은 한 기계안에서 샤딩과 복제 셋을 테스트 하는 예제 입니다. 


원칙대로라면 복제 셋 (Replication Set) 을 설정할 때 한 머신에서 동작시키는 것이 아니라 서로 다른  서버에서 구동되게 설계를 해야 하지만 그것 까지 세팅하기가 매우 귀찮기 때문에 다음 과 같이 설계를 해 봤습니다. 

   

    Machine 1 : 

    - name : super1
    - replication : first * 3


    Machine 2 :

    - name : super2
    - replication : second * 3

    


 

 즉 2대의 머신에서 샤딩이 이루어 지게 세팅을 하는 것인데, 각각의 머신은 3개의 복제 셋을 가지게 구성 되는 것입니다. 

mongoDB 의 bin 이 path 에 걸려 있다는 가정 하에 진행 합니다. 

super1 부터 설정을 시작합니다.
  먼저 데이터들이 저장될 db 디렉토리를 만들어 줍니다. 

   

    $ mkdir ~/db/first1 
    $ mkdir ~/db/first2
    $ mkdir ~/db/first3

    
    로그를 저장할 공간도 만들어 줍니다. 

    $ mkdir ~/db/log 


    이제 몽고 데몬들을 띄워줍니다. 

 

    $ mongod --dbpath ~/db/first1 --port 10001 --replSet first > ~/db/log/first1.log &
    $ mongod --dbpath ~/db/first2 --port 10002 --replSet first > ~/db/log/first2.log &
    $ mongod --dbpath ~/db/first3 --port 10003 --replSet first > ~/db/log/first3.log &


    이제 이 세개의 몽고 디비 프로세스들을 한개의 복제셋 (여기서는 first )으로 묶어주는 작업을 합니다. 

 

  $ mongo localhost:10001/admin 

    > db.runCommand({"replSetInitiate" : {"_id" : "first", "members" : [{"_id" : 1, "host" : "super1:10001"}, {"_id" : 2, "host" : "super1:10002"}, {"_id" : 3, "host" : "super1:10003"}]}})
    {
        "info" : "Config now saved locally.  Should come online in about a minute.",
"ok" : 1
    }


    이러면 성공적으로 복제 셋이 만들어 진것 입니다. 이제 데이타를 실제로 넣어보기로 하겠습니다. 

   

PRIMARY> use test
    switched to db test
    PRIMARY> people = ["Marc", "Bill", "George", "Eliot", "Matt", "Trey", "Tracy", "Greg", "Steve", "Kristina", "Katie", "Jeff"];
    PRIMARY> for(var i=0; i<1000000; i++){
          name = people[Math.floor(Math.random()*people.length)];
 user_id = i;
 boolean = [true, false][Math.floor(Math.random()*2)];
 added_at = new Date();
 number = Math.floor(Math.random()*10001);
 db.test_collection.save({"name":name, "user_id":user_id, "boolean": boolean, "added_at":added_at, "number":number });
    }

 
    이름을 백만건 정도 랜덤으로 순서를 매겨서 집어넣는 소스코드 입니다. 


이제는 구성된 복제 셋을 가지고 샤딩으로 추가해 주는 작업을 해 줘야 할 차례 입니다. 역시  super1  서버에서 작업을 해 줍니다. 

config 서버를 띄워줄 차례 입니다. 먼저 config 데이타들이 저장 될 디렉토리를 만들어 줘야 합니다. 

   $ mkdir ~/db/config1
   $ mkdir ~/db/config2
   $ mkdir ~/db/config3


컨피그 서버는 1대 아니면 3대를 추천합니다. (제가 아니라 몽고디비 쪽이 추천하는 것입니다) 

 

   $ mongod --configsvr --dbpath ~/db/config1 --port 20001 > ~/db/log/config1.log &
   $ mongod --configsvr --dbpath ~/db/config2 --port 20002 > ~/db/log/config2.log &
   $ mongod --configsvr --dbpath ~/db/config3 --port 20003 > ~/db/log/config3.log &


혹시 컨피그 서버가 제대로 띄워지지 않으면 다른 포트로 띄워주시면 됩니다. 이제 컨피그 서버에서 정보를 가져오는 mongos (라우터 개념)를 띄워줄 차례 입니다. 

 

 $ mongos --configdb super1:20001,super2:20002,super3:20003 --chunkSize 1 > ~/db/log/mongos.log &


 chunkSize 는 테스트에서만 추가하는 것으로 생각하시면 됩니다. 크기를 1메가로 만드는  것입니다. (기본은 64메가 입니다) 이제 띄워논 mongos 에 접속해야 한다. 

 

 $ mongo super1/admin
  
 mongos> db.runCommand( { addshard : "first/super1:10001,super2:10002,super3:10003" } )

   { "shardAdded" : "first", "ok" : 1 }
   mongos>
   
  
이제 두번째 기계에서 세팅을 할 차례 입니다. super2 (Machine 2) 입니다.  먼저 데이타베이스가 저장될 디렉토리를 만들어 줍니다. 

   

   $ mkdir ~/db/second1 
   $ mkdir ~/db/second2
   $ mkdir ~/db/second3 
   

로그용 디렉토리도 만들어 줍니다. 
   

$ mkdir ~/db/log 


몽고디비 데몬을 띄워줍니다. 

   

   $ mongod --dbpath ~/db/second1 --port 10001 --replSet second > ~/db/log/second1.log &
   $ mongod --dbpath ~/db/second2 --port 10002 --replSet second > ~/db/log/second2.log &
   $ mongod --dbpath ~/db/second3 --port 10003 --replSet second > ~/db/log/second3.log &


이제 이 프로세스들을 한 개의 복제 셋(replication set)으로 묶어 줍니다. 

 

 $ mongo super2:10001/admin

   > db.runCommand({"replSetInitiate" : {"_id" : "second", "members" : [{"_id" : 1, "host" : "super2:10001"}, {"_id" : 2, "host" : "super2:10002"}, {"_id" : 3, "host" : "super2:10003"}]}})
   {
      "info" : "Config now saved locally.  Should come online in about a minute.",
      "ok" : 1
   }



방금 만들어 진 복제 셋 (replication set) 을 새롭게 샤드 (shard) 에 추가해 줍니다. 먼저 mongos 를 띄워줍니다. 지금 super2 서버고 mongos 도 super2 에서 띄우는 것이지만 바라보는 컨피그 서버 는 super1 에서 띄운 것을 쳐다봐야 합니다. 

   $ mongos --configdb super1:20001,super2:20002,super3:20003 --chunkSize 1 >  ~/db/log/mongos.log &


몽고 shell 을 이용해서 mongos (이건 super2 에 띄워 놓은 것입니다) 에 접속합니다. 

   

$ mongo super2/admin
   mongos> use admin
   switched to db admin
   mongos> db.runCommand( { addshard : "second/super2:10001,super2:10002,super3:10003" } )
     { "shardAdded" : "secondset", "ok" : 1 }


이러면 두번째 복제 셋 (replication set) 을 샤드에 추가 해 주었습니다.  제대로 되어 있는지 테스트를 확인 해 보기로 합니다. 

 

 mongos> db.runCommand({listshards:1})
   {
        "shards" : [
  {
      "_id" : "first",
      "host" : "first/super1:10001,super1:10003,super1:10002"
  },
  {
      "_id" : "second",
      "host" : "second/super2:10001,super2:10002,super2:10003"
  }
     ],
     "ok" : 1
   }


샤드로 설정되어 있는 것하고 샤드를 직접적으로 하는 것은 차이가 있습니다. 실제로 샤드를 구현해 보겠습니다. mongos 에 접속되어 있는 상태에서 

 

 mongos> db.runCommand( { enablesharding : "test" } )
   { "ok" : 1 }


 
위 처럼 입력해 주면 'test' db 를 샤딩하겠다고 정해주는 것입니다. 이제 샤딩 키 (Sharding Key) 를 정해줘야 합니다. 샤딩 키를 바탕으로 (이 키로 정렬한다는 뜻입니다) 샤드들이 나눠지게 되기 때문에 샤딩키를 적절하게 정해주는 것은 아주 중요합니다. 인덱스를 정해준다고 보셔도 무방합니다. 

 

   mongos> use test
   switched to db test
   mongos> db.test_collection.ensureIndex({number:1})

   
   생각 난 김에 인덱스도 정해줍니다. test 디비에 test_collection 이라는 컬렉션에서 number 라는 컬럼을
   인덱스로 지정해 주라는 명령입니다. 

   

mongos> use admin
   switched to db admin
   mongos> db.runCommand( { shardcollection : "test.test_collection", key : {"number":1} })
   { "collectionsharded" : "test.test_collection", "ok" : 1 }
   mongos>


   
test 디비에 test_collection 이라는 컬렉션에서 number 라는 컬럼을 샤딩키로 해서 샤딩을 해 주라는 명령입니다. 

제대로 됐는지 확인을 해 보겠습니다. 

 

   mongos> use test
   switched to db test
   mongos> db.stats()


   
db.stats() 나 db.printShardingStatus() 로 확인하시면 됩니다. 


몽고 DB 에서 간단한 맵 리듀스 (MapReduce)를 돌려보겠습니다.

예제의 원문은

http://cookbook.mongodb.org/patterns/pivot/

에서 찾을 수 있습니다.


db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

기본이 되는 데이터를 집어 넣습니다.

map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}


map 함수입니다. mapreduce 를 돌리는 대상이 아래에도 나오지만 actors 이기 때문에 this 는 actors 컬렉션을 지칭한다고 보면 됩니다.

  for(var i in this.movies){

map 은 대체적으로 모든 데이터 한 row 당 매핑 (mapping) 된다고 보면 되기 때문에 actors 의 한 row 는 actor 한개와 movies 여러개가 들어 있습니다. 첫 줄은 movies 갯수만큼 for 문을 돌리라는 이야기 입니다.

    key = { movie: this.movies[i] };

key 값은 영화명으로 지정해 주는 것입니다. 해쉬 값으로 설정되는 것이겠지요? {movie: **** } 형태입니다.

    value = { actors: [ this.actor ] };

value 값은 배우명으로 지정해 주는 것입니다. 역시 해쉬 값으로 설정되는 것입니다. {actors: ***}

    emit(key, value);

map 함수들이 보통 하는 것이 reduce 에게 전달하기 위한 값들을 추출하는 것이 목적이기 때문에 (key, value) 쌍으로 값을 송출합니다.


reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

리듀스 (reduce) 함수는 프레임워크가 맵 (map) 함수에서 송출된 데이터를 key 값으로 정렬 시킨 값을 넘겨 받습니다. 넘겨 받는 인자가 (key, values) 로 바뀐것을 주목하시면 됩니다. 즉 map 에서 송출한 데이타들을 key 값으로 정렬한 것입니다. 즉 { movie : aaa } 라는 key 값으로 { actors : [bbb , ccc]} 라는 값으로 넘어옵니다.

  actor_list = { actors: [] };

reduce 함수는 일정한 형태로 값을 넘겨 받아야 합니다. 그 형식을 만들어 주는 것이라고 보면 됩니다. (단  key, value 가 한개씩 붙어 있는 형태는 reduce 를 거치지 않고 바로 db 에 쓰여집니다)

  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }

values 에 있는 값들을 한개씩 리턴할 값에 붙여 주는 행동입니다.


printjson(db.actors.mapReduce(map, reduce, "pivot"));


실제로 mapReduce 를 구동시키고 그 결과값을 Pivot 에 써주는 작업입니다.

db.pivot.find().forEach(printjson);


잘 쓰여졌는지 확인하는 작업입니다.

위 파일을 내려 받으시고 command line 상에서

$ mongo movie-star.js --shell


라고 입력하시면 구동하시는 모습을 확인할 수 있습니다.


+ Recent posts