[Node.js] Express 와 MongoDB 를 이용해서 빠르게 블로그 만들기

몇년전에 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 


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