04-28 06:36
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[Programming / WebApp] Express-MongoDB 활용 본문

Programming/Web App

[Programming / WebApp] Express-MongoDB 활용

cinema4dr12 2016. 9. 5. 21:34

이번 글에서는 Express Framework에서 MongoDB를 활용하는 방법에 대하여 알아보도록 한다.
Express 기본 프로젝트가 설정이 되어 있으며, MongoDB가 설치되어 있다고 가정한다.


Mongoose 설치

MongooseMongoDB의 ODM(Object Data Mapping) 툴이다.
Mongoose를 설치하려면 Command Line Tool에서

$ npm install --save mongoose

를 입력한다.


MongoDB 실행

Express 프로젝트의 Root 경로에 "data" 디렉터리를 생성하고, Command Line에 다음과 같이 입력하여 MongoDB를 실행한다:

$ mongod --dbpath {EXPRESS_PROJECT_ROOT}/data


웹페이지 member.pug 작성

DB에 데이터를 기록할 간단한 웹페이지를 작성하도록 한다.

간단한 회원(member)을 등록하는 페이지로 형식은 아래 이미지와 같다.


다음과 같이 "member.pug" 파일을 작성한다.

{EXPRESS_PROJECT_ROOT}/views/member.pug

html
    head
        title Membership
    body
        form(action="/member", method="POST")
            div
                label(for="name") Name : &nbsp
                input(name="name")
            br
            div
                label(for="email") Email :  &nbsp
                input(type='email', name="email")
            br
            div
                label(for="phone") Phone Number :  &nbsp
                input(type='number', name='phone')
            br
            button(type="submit") Register new member

form 태그로 구성되며 속성 중 action="/member"는 라우팅 주소이며, method="POST"는 HTTP method 중 POST 방식으로 데이터를 전송한다는 의미이다.

[Register new member] 버튼을 클릭하면 해당 form 데이터를 전송한다.


member.js 작성

form을 통해 전송된 데이터를 처리할 JavaScript 파일을 작성한다.

{EXPRESS_PROJECT_ROOT}/routes/member.js


Line 2: mongoose object를 로딩한다.

Line 5-9: mongoose Schema를 정의한다.

Line 11: mongoose Schema로부터 mongoose model을 연결한다.

Line 14: Mongo DB를 연결한다. DB 이름은 "my_db"로 하였다.

Line 17-21: HTTP GET 방식으로 요청 시 member.pug 페이지를 렌더링하도록 한다.

Line 24-46: HTTP POST 방식으로 요청 시 처리하는 프로세스이다.

Line 25: req.body는 전송된 데이터를 포함한다. console.log(req.body) 를 통해 Command Line Tool에 출력하면 전송된 데이터 내용을 볼 수 있다.

Line 27-29: 데이터 중 하나라도 누락되는 경우 에러 페이지를 출력한다. 해당 에러 페이지는 "show_message.pug"이며, 따로 언급하도록 한다.

Line 31-35: mongoose model인 Member의 인스턴스를 JSON 형식으로 생성한다.

Line 36 - 42: MongoDB에 데이터를 저장한다.


show_message.pug 페이지 작성

데이터 저장에 대한 결과를 출력해 주는 웹페이지를 작성한다.

html
    head
        title Member
    body
      if(type === "error")
          h3(style="color:red") #{message}
      else
          h3 New Member, Name: #{member.name}, Email: #{member.email} and Phone: #{member.phone} added!


app.js에 코드 추가

이제 app.js에 해당 라우트를 추가한다.

var member = require('./routes/member');
...
app.use('/member', member);


위의 코드를 어디에 추가할 지 모르는 분들이 있을까 하여 app.js의 전체 코드를 공개한다.

{EXPRESS_PROJECT_ROOT}/app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var multer = require('multer');

var upload = multer();

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

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

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

// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(upload.array()); // for parsing multipart/form-data
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser({ keepExtensions: true, uploadDir: "uploads" }));

app.use('/', routes);
app.use('/member', member);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

// server setup
var server = app.listen(app.get('port'), function() {
  console.log('Express server listening on port ' + server.address().port);
});


module.exports = app;


결과 확인

Node Server를 실행하고 웹 브라우저의 주소창에 "localhost:3000/member" 를 입력한다.

각 필드에 정보를 입력하고 [Register new member] 버튼을 클릭하여 DB로 데이터를 전송해 보자.

예를 들어, 다음과 같이 입력하면



다음과 같은 페이지가 전송될 것이다.


데이터 출력

데이터가 DB에 제대로 입력되었는지 확인해 보자.

Command Line Tool에서

> mongo

를 입력하여 MongoDB로 진입하고,

> use my_db

를 입력하여 사용할 DB 이름을 지정한다.

다음과 같이 입력하면 해당 DB의 Collection들을 출력할 수 있다.

> db.getCollectionNames()
[ "members" ]

이제 해당 Collection의 데이터를 출력하도록 한다.

> db.members.find().pretty()
{
        "_id" : ObjectId("57d7c25a84d4d01880e32f41"),
        "name" : "Geol Choi",
        "email" : "cinema4dr12@gamil.com"
        "phone" : "01012345678",
        "__v" : 0
}


데이터 불러오기

등록된 모든 데이터를 불러오도록 한다.
"member.js"에 다음 코드를 추가한다.

{EXPRESS_PROJECT_ROOT}/routes/member.js

/* Retrieve database */
router.get( '/findall', function ( req, res ) {
  Member.find( function( err, response ) {
        res.json( response );
  } );
} );


웹 브라우저의 주소창에 "localhost:3000/member/findall"를 입력하면,


위의 이미지와 같이 DB에 입력된 모든 데이터를 불러온다.

그러면 이쯤에서 특정 데이터를 불러오려면 어떻게 하는지 궁금할 수 있다.
검색 페이지를 만들어서 검색 결과를 출력해 보도록 한다.

우선 name 키워드로 검색할 수 있도록 검색 페이지를 작성하도록 한다.
페이지 이름은 "search.pug"로 하였으며, 다음과 같이 작성한다.

{EXPRESS_PROJECT_ROOT}/views/search.pug

html
    head
        title Membership
    body
        form(action="/member/search", method="POST")
            div
                label(for="name") Name : &nbsp
                input(name="name")
            br
            button(type="submit") Search Member


member.pug와 마찬가지로 form 형식으로 /member/search 경로로 POST HTTP method로 데이터를 전송한다.
위의 페이지는 다음과 같은 형태일 것이다.


DB에 다음과 같이 데이터(Member)가 등록되어 있다고 가정하자.

{
	"_id" : ObjectId("57da39f2ad6db00c788e48be"),
	"name" : "Geol Choi",
	"email" : "cinema4dr12@gmail.com",
	"phone" : "01011111111",
	"__v" : 0
}
{
	"_id" : ObjectId("57da3a2dad6db00c788e48bf"),
	"name" : "Jung Min Park",
	"email" : "raspberry@cj.net",
	"phone" : "0102222222",
	"__v" : 0
}
{
	"_id" : ObjectId("57da3a47ad6db00c788e48c0"),
	"name" : "Ho Shup Kim",
	"email" : "nicesubi@gmail.com",
	"phone" : "010333333333",
	"__v" : 0
}
{
	"_id" : ObjectId("57da3a5ead6db00c788e48c1"),
	"name" : "Tae Jin Kwak",
	"email" : "ozerodie@naver.com",
	"phone" : "01044444444",
	"__v" : 0
}
{
	"_id" : ObjectId("57da3a71ad6db00c788e48c2"),
	"name" : "Genome",
	"email" : "genonme@naver.com",
	"phone" : "0105555555",
	"__v" : 0
}


member.js 파일에 다음 코드를 추가하도록 한다.

/* GET : "/member/search" */
router.get( '/search', function ( req, res ) {
  res.render( 'search', {
    title: 'Member Data'
  } );
} );

/* POST : "/member/search" */
router.post( '/search', function ( req, res ) {
  Member.find( {name: {$regex: req.body.name}}, function( err, response ) {
    res.json( response );
  } );
} );

/member/search/ 의 경로에 각기 다른 HTTP method인 GET와 POST를 통해 처리하는 코드이다.
GET을 통해 처리되는 코드는 search.pug 페이지를 렌더링하도록 하고 있다.
POST를 통해 처리되는 코드는 DB로부터 name 키를 통해 도큐먼트를 검색한다.
{name: {$regex: req,body.name}} 을 주목할 필요가 있는데, $regex: 는 Regular Expression을 의미하며, 해당 문자가 포함되는 도큐먼트를 모두 검색하도록 한다.
res.json( response ) 는 검색 결과를 JSON 형태로 웹 페이지에 출력한다.
가령, name 키를 "Ge"로 검색해보면, 이 문자열을 포함하는 도큐먼트는 2개로 "Geol Choi"와 "Genome"이 검색될 것이다.
(검색 시 대소문자를 구분함에 유의하기 바란다.)



검색결과는, 아래와 같이 예상대로 name 키에 "Ge"를 포함하는 두 개의 도큐먼트가 검색되었다.


Regular Expression은 이렇듯 DB 검색 조건에 있어 매우 유용하다.
몇 가지 유용한 예를 들어 보면,

db.members.find({name:{$regex: "e"})  : name 키값에 "e"가 포함되어 있는 것을 검색
db.members.find({name:{$regex: "e$"}) : name 키값에 끝자리가 "e"인 것을 검색
db.members.find({name:{$regex: "^e"}) : name 키값에 "e"로 시작하는 것을 검색

과 같다.


데이터 업데이트

데이터를 업데이트 하는 방법을 알아보자.
name 키로 검색을 하고, 검색된 데이터에 대하여 email과 phone을 업데이트 할 수 있도록 다음과 같이 페이지를 작성한다.

{EXPRESS_PROJECT_ROOT}/views/update.pug

html
    head
        title Membership
    body
        form(action="/member/update", method="POST")
            div
                h3(for="search") SEARCH
            div
                label(for="name") Name : &nbsp
                input(name="name")
            br
            div
                h3(for="update") UPDATE
            div
                label(for="email") Emal : &nbsp
                input(name="email")
            br
            div
                label(for="phone") Phone : &nbsp
                input(name="phone")
            br
            button(type="submit") Update Member


위의 페이지 결과는 다음과 같을 것이다.


다음과 같이 member.js/member/update에 대한 GETPOST HTTP method를 처리할 수 있도록 코드를 추가한다.

{EXPRESS_PROJECT_ROOT}/routes/member.js

/* GET : "/member/update" */
router.get( '/update', function ( req, res ) {
  res.render( 'update', {
    title: 'Member Update'
  } );
} );

/* POST : "/member/update" */
router.post( '/update', function ( req, res ) {
  Member.findOneAndUpdate(
    {name: req.body.name},
    {email: req.body.email, phone: req.body.phone},
    function( err1, response1 ) {
      Member.find(
        {name: req.body.name},
        function( err2, response2 ) {
          res.json( response2 );
      } );
  } );
} );


데이터를 업데이트 하는 메써드는 findOneAndUpate()이며, 첫번째 파라미터는 검색 쿼리, 두번째 파라미터는 업데이트 쿼리, 세번째 파라미터는 콜백(callback) 함수이다.

가령, 다음과 같이 입력하면



"Geol Choi" name 키로 검색을 하여 emailphone을 업데이트 하여 다음과 같은 결과를 얻는다.


데이터 삭제

Member 데이터를 삭제하는 방법에 대해 알아보도록 한다.
name 키로 검색하여 검색된 대상을 삭제할 수 있도록 다음과 같이 웹 페이지를 작성한다.

{EXPRESS_PROJECT_ROOT}/views/remove.pug

html
    head
        title Membership
    body
        form(action="/member/remove", method="POST")
            div
                h3(for="remove") REMOVE
            div
                label(for="name") Name : &nbsp
                input(name="name")
            br
            button(type="submit") Remove Member


이렇게 작성된 페이지는 다음과 같을 것이다.


다음과 같이 member.js /member/remove에 대한 GETPOST HTTP method를 처리할 수 있도록 코드를 추가한다.

{EXPRESS_PROEJCT_ROOT}/routes/member.js

/* GET : "/member/remove" */
router.get( '/remove', function ( req, res ) {
  res.render( 'remove', {
    title: 'Remove Member'
  } );
} );

/* POST : "/member/remove" */
router.post( '/remove', function ( req, res ) {
  Member.findOneAndRemove(
    {name: req.body.name},
    function( err, response ) {
      if( err )
        throw err;
      else
        res.send(req.body.name + " has bee removed from DB.");
  } );
} );


데이터를 업데이트 하는 메써드는 findOneAndRemove()이며, 첫번째 파라미터는 검색 쿼리, 두번째 파라미터는 콜백(callback) 함수이다.

가령, 다음과 같이 입력하면



정상적으로 데이터 삭제 처리가 되는 경우 다음과 같은 메시지가 출력된다.


Alternative Method

DB를 철저히 하나의 모듈로 독립적으로 관리하고 싶은 경우가 있을 것이다.
(사실 그렇게 하는 것이 관리의 효율적 측면에서도 맞다.)

먼저 Express Project Root 경로에 config 디렉터리를 만든다.

이 경로에 "mongodb.js" 파일을 다음과 같이 작성한다.

{EXPRESS_PROJECT_ROOT}/config/mongodb.js



그리고, "member.js"도 다음과 같이 수정 작성한다.

{EXPRESS_PROJECT_ROOT}/routes/member.js



개별적인 코드 설명은 생략하기로 한다.
각자 충분히 코드를 분석 할 수 있으리라 믿는다. ^^

마지막으로, 앞서 설명한 member.js 전체 코드를 공개하고 이 글을 마무리하도록 하겠다.

{EXPRESS_PROJECT_ROOT}/routes/member.js


Comments