10.6 Deploying Node.js(Express) & MongoDB to Heroku

Heroku에서의 Node.js와 MongoDB 설치

Heroku Logo

Heroku는 AWS의 IaaS 상에 구축된 PaaS이다. Git로 deploy가 가능하며 Web app 개발에서 공개까지 간단히 사용할 수 있는 Platform이다.

소규모 사이트나 개인 블로그 정도는 충분히 무료로 사용할 수 있는 공간이 주어진다.

1. Heroku account 취득

Heroku에서 sign up을 실시하여 account를 취득한다.

2. Node.js & npm install 확인

node.js와 npm, git가 사전에 install되어 있어야 한다. install 여부를 확인한다.

$ node -v
v6.9.4
$ npm -v
4.2.0
$ git --version
git version 2.6.4 (Apple Git-63)

3. Heroku CLI 설치

Heroku CLI(a.k.a. Heroku Toolbelt)는 command line/shell에서 Heroku 애플리케이션을 생성하고 관리할 수 있는 도구이다.

자신의 사양에 맞는 Heroku CLI를 설치한다.

4. Heroku 로그인

터미널에서 Heroku에 로그인한다.

$ heroku login
Enter your Heroku credentials.
Email: ungmo2@gmail.com
Password (typing will be hidden):
Logged in as ungmo2@gmail.com

5. sample app의 준비

Sample app을 clone한다. app의 이름은 나중에 수정이 가능하므로 지금은 heroku-express-example이라는 이름의 app을 생성한다.

$ git clone https://github.com/heroku/node-js-getting-started.git heroku-express-example
$ cd heroku-express-example
$ ls -al
total 56
drwxr-xr-x  12 leeungmo  staff   408  2 24 00:23 .
drwxr-xr-x+ 71 leeungmo  staff  2414  2 24 00:23 ..
-rw-r--r--   1 leeungmo  staff     8  2 24 00:23 .env
drwxr-xr-x  13 leeungmo  staff   442  2 24 00:23 .git
-rw-r--r--   1 leeungmo  staff   133  2 24 00:23 .gitignore
-rw-r--r--   1 leeungmo  staff    19  2 24 00:23 Procfile
-rw-r--r--   1 leeungmo  staff  1371  2 24 00:23 README.md
-rw-r--r--   1 leeungmo  staff   301  2 24 00:23 app.json
-rw-r--r--   1 leeungmo  staff   460  2 24 00:23 index.js
-rw-r--r--   1 leeungmo  staff   485  2 24 00:23 package.json
drwxr-xr-x   5 leeungmo  staff   170  2 24 00:23 public
drwxr-xr-x   4 leeungmo  staff   136  2 24 00:23 views

6. Deploy App

app을 Heroku에 생성한다. app의 이름을 지정하지 않으면 random한 이름이 자동으로 생성된다.

$ heroku create heroku-express-example
Creating ⬢ heroku-express-example... done
https://heroku-express-example.herokuapp.com/ | https://git.heroku.com/heroku-express-example.git

이제 app이 Heroku에 생성되었고 Heroku와 로컬 git 저장소는 연결된다.

이때 .git/config 파일에 아래 내용이 추가된다.

[remote "heroku"]
	url = https://git.heroku.com/heroku-express-example.git
	fetch = +refs/heads/*:refs/remotes/heroku/*

heroku create app

Sample app을 Heroku로 push한다. 이것이 바로 deploy이다.

$ git push heroku master
Counting objects: 456, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 695 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Node.js app detected
remote:
remote: -----> Creating runtime environment
remote:
remote:        NPM_CONFIG_LOGLEVEL=error
remote:        NPM_CONFIG_PRODUCTION=true
remote:        NODE_ENV=production
remote:        NODE_MODULES_CACHE=true
remote:
remote: -----> Installing binaries
remote:        engines.node (package.json):  unspecified
remote:        engines.npm (package.json):   unspecified (use default)
remote:
remote:        Resolving node version (latest stable) via semver.io...
remote:        Downloading and installing node 5.11.1...
remote:        Using default npm version: 3.8.6
remote:
remote: -----> Restoring cache
remote:        Skipping cache restore (new runtime signature)
remote:
remote: -----> Building dependencies
remote:        Installing node modules (package.json)
remote:        express-skeletion@0.0.1 /tmp/build_08cf53544f91bec4bcfe881762701b40
remote:        └─┬ express@4.14.0
remote:        ├─┬ accepts@1.3.3
remote:        │ ├─┬ mime-types@2.1.11
remote:        │ │ └── mime-db@1.23.0
remote:        │ └── negotiator@0.6.1
remote:        ├── array-flatten@1.1.1
remote:        ├── content-disposition@0.5.1
remote:        ├── content-type@1.0.2
remote:        ├── cookie@0.3.1
remote:        ├── cookie-signature@1.0.6
remote:        ├─┬ debug@2.2.0
remote:        │ └── ms@0.7.1
remote:        ├── depd@1.1.0
remote:        ├── encodeurl@1.0.1
remote:        ├── escape-html@1.0.3
remote:        ├── etag@1.7.0
remote:        ├─┬ finalhandler@0.5.0
remote:        │ ├── statuses@1.3.0
remote:        │ └── unpipe@1.0.0
remote:        ├── fresh@0.3.0
remote:        ├── merge-descriptors@1.0.1
remote:        ├── methods@1.1.2
remote:        ├─┬ on-finished@2.3.0
remote:        │ └── ee-first@1.1.1
remote:        ├── parseurl@1.3.1
remote:        ├── path-to-regexp@0.1.7
remote:        ├─┬ proxy-addr@1.1.2
remote:        │ ├── forwarded@0.1.0
remote:        │ └── ipaddr.js@1.1.1
remote:        ├── qs@6.2.0
remote:        ├── range-parser@1.2.0
remote:        ├─┬ send@0.14.1
remote:        │ ├── destroy@1.0.4
remote:        │ ├─┬ http-errors@1.5.0
remote:        │ │ ├── inherits@2.0.1
remote:        │ │ └── setprototypeof@1.0.1
remote:        │ └── mime@1.3.4
remote:        ├── serve-static@1.11.1
remote:        ├─┬ type-is@1.6.13
remote:        │ └── media-typer@0.3.0
remote:        ├── utils-merge@1.0.0
remote:        └── vary@1.1.0
remote:
remote:
remote: -----> Caching build
remote:        Clearing previous node cache
remote:        Saving 2 cacheDirectories (default):
remote:        - node_modules
remote:        - bower_components (nothing to cache)
remote:
remote: -----> Build succeeded!
remote:        └── express@4.14.0
remote:
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote:
remote: -----> Compressing...
remote:        Done: 12.1M
remote: -----> Launching...
remote:        Released v3
remote:        https://heroku-express-example.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/heroku-express-example.git
 * [new branch]      master -> master

app이 deploy되었다. deploy될 때 package.json의 dependency가 자동으로 install된다.

heroku deploy app

instance가 동작하고 있지 않으면 다음 명령어로 기동시킨다.

$ heroku ps:scale web=1
Scaling dynos... done, now running web at 1:Free

이제 생성된 app이 동작하는 URL으로 방문하여 동작을 확인한다. 또는 아래의 명령어로 방문할 수 있다.

$ heroku open

heroku open app

log를 확인하는 방법은 아래와 같다.

$ heroku logs --tail

7. Procfile

루트 디렉터리에 있는 Procfile에는 app이 start할 때 실행하여야 하는 동작을 명시적으로 정의한다.

web: node index.js

web은 process type을 의미한다.

8. local 환경에서의 Code의 수정과 Heroku에의 Deploy

8.1 local 환경 구축

local 환경에서 code를 수정하고 local 환경에서 app을 기동하여 수정사항을 확인한 후 Heroku에 수정사항을 반영한다.

local 환경을 구축하기 위하여 필요에 따라 pakage.json의 dependency 설정을 변경하고 local 환경에 필요 dependency를 설치한다.

{
  "name": "node-js-getting-started",
  "version": "0.2.5",
  ...
  "engines": {
    "node": "5.9.1"
  },
  "dependencies": {
    "ejs": "2.4.1",
    "express": "4.13.3"
  },
  ...
}
$ cd heroku-express-example
$ npm install

heroku local command를 사용하여 local에서 app을 기동한다.

$ heroku local web
[OKAY] Loaded ENV .env File as KEY=VALUE Format
17:15:52 web.1   |  Node app is running on port 5000

반드시 heroku 명령어를 사용해야 하는 것은 아니다. 아래와 같이 일반적인 방법도 가능하다.

$ npm start

브라우저에서 http://localhost:5000으로 접속하여 local 환경에서 app이 실행되었음을 확인한다.

8.2 Code의 수정

code를 수정한다.

cool-ascii-faces를 install한다.

$ npm install --save --save-exact cool-ascii-faces

index.js를 아래와 같이 수정한다. ool-ascii-faces를 require하고 /cool 라우트를 추가한다.

var cool = require('cool-ascii-faces');
var express = require('express');
var app = express();

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

app.use(express.static(__dirname + '/public'));

// views is directory for all template files
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

app.get('/', function(request, response) {
  response.render('pages/index')
});

app.get('/cool', function(request, response) {
  response.send(cool());
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});
$ npm install
$ heroku local web

브라우저에서 http://localhost:5000/cool으로 접속하여 local 환경에서 app이 실행되었음을 확인한다.

( ⚆ _ ⚆ )

8.3 Heroku에의 Deploy

먼저 모든 파일을 local git에 추가한다.

$ git add .

수정사항을 repository에 commit한다.

$ git commit -m "Demo"

heroku master에 git push한다.

$ git push heroku master
$ git push origin master

app을 실행시켜서 정상 작동됨을 확인한다.

$ heroku open cool

9. GitHub Integration

github와 연동하여 heroku에 수정사항을 반영할 수 있다.

Heroku Dashboard의 Deploy 탭으로 이동한다.

Deployment method에서 GitHub를 선택한다.

Connect to GitHub에 자신의 github repository를 등록한다.

Heroku GitHub Integration

Connect to GitHub의 Search 버튼을 클릭하여 github repository를 등록한 후 Connect 버튼을 클릭한다.

Heroku GitHub Integration

Automatic deploys에서 Enable Automatic Deploys 버튼을 클릭한다.

Heroku GitHub Integration

이후 code를 GitHub에 push하면 자동으로 Heroku에 deploy가 실행된다.

10. Add-on 설치

Logging add-on Papertrail을 설치한다.

$ heroku addons:create papertrail
$ heroku addons

Add-on                                  Plan     Price
──────────────────────────────────────  ───────  ─────
papertrail (papertrail-colorful-87606)  choklad  free
 └─ as PAPERTRAIL

The table above shows add-ons and the attachments to the current app (heroku-express-example) or other apps.
$ heroku addons:open papertrail

Heroku Papertrail web console

11. Database 설치

Heroku는 Redis, MongoDB, Postgres, MySQL 등 다수의 data store add-on을 제공한다.

이중 MongoDB add-on을 추가한다.

$ heroku addons:create mongolab

또는 아래와 같이 추가할 수도 있다.

https://elements.heroku.com/addons으로 이동하여 mLab MongoDB를 선택한다.

Heroku MongoDB

Install mLab MongoDB 버튼을 클릭한다.

Heroku MongoDB

Install 대상 app을 선택한다.

Heroku MongoDB

Heroku Dashboard의 Resources 탭으로 이동하여 Add-ons의 mLab MongoDB를 클릭한다.

Heroku MongoDB

Users 탭을 선택하고 Add database user 버튼을 클릭하여 새로운 사용자를 생성한다.

Heroku MongoDB

MongoDB add-on을 생성하면 database connection URI이 config var에 저장된다.

이 값은 Node.js 내에서 process.env.MONGODB_URI로 접근할 수 있다.

새롭게 생성한 user를 config var의 MONGODB_URI에 저장한다.

MongoDB에 접속한다.

$ mongo ds029328.mlab.com:29328/<dbname> -u <dbuser> -p <dbpassword>
MongoDB shell version: 3.2.8
connecting to: ds029328.mlab.com:29328/heroku_t7tcg30n
rs-ds029328:PRIMARY>

sample data를 insert한다.

use heroku_t7tcg30n
switched to db heroku_t7tcg30n
rs-ds029328:PRIMARY> db.books.insert(
	[{ title: "Example1", author: "Lee", price: 100 },
	{ title: "Example2", author: "Kim", price: 200 },
	{ title: "Example3", author: "Choi", price: 300 },
	{ title: "Example4", author: "Park", price: 400 }])
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 4,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})
rs-ds029328:PRIMARY> db.books.find()
{ "_id" : ObjectId("57bf7441eed4391ce89f1f55"), "title" : "Example1", "author" : "Lee", "price" : 100 }
{ "_id" : ObjectId("57bf7441eed4391ce89f1f56"), "title" : "Example2", "author" : "Kim", "price" : 200 }
{ "_id" : ObjectId("57bf7441eed4391ce89f1f57"), "title" : "Example3", "author" : "Choi", "price" : 300 }
{ "_id" : ObjectId("57bf7441eed4391ce89f1f58"), "title" : "Example4", "author" : "Park", "price" : 400 }
rs-ds029328:PRIMARY>

mongoose 모듈을 install한다.

$ npm install --save --save-exact mongoose

index.js를 수정한다.

var cool     = require('cool-ascii-faces');
var express  = require('express');
var mongoose = require('mongoose');

var app = express();

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

app.use(express.static(__dirname + '/public'));

// views is directory for all template files
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// CONNECT TO MONGODB SERVER
mongoose.connect(process.env.MONGODB_URI);

// DEFINE MODEL
var Books = require('./models/books');

app.get('/', function(request, response) {
  response.render('pages/index');
});

app.get('/cool', function(request, response) {
  response.send(cool());
});

// GET ALL BOOKS
app.get('/books', function(req,res){
  Books.find(function(err, books){
    if(err) return res.status(500).send({error: 'database failure'});
    res.json(books);
  });
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

root 디렉터리에 models 디렉터리를 생성하고 books.js를 생성하여 추가한다.

var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

var booksSchema = new Schema({
  title : String,
  author: String,
  price : Number
});

module.exports = mongoose.model('books', booksSchema);

Heroku에 deploy 후 동작을 확인한다.

Heroku MongoDB

Reference

Back to top
Close