10.1 Express Basics

Routing, Middleware, Static file, Template engine

Express는 Node.js 환경에서 동작하는 Web application Framework이다. Express는 Web Application 구성에 필요한 Routing, View Helper, Session(영속적 Session관리를 위해서는 Redis등의 Data store가 필요하다)등의 기능을 제공한다.

Express 4.14.0 버전을 기준으로 한다.

1. Install

Node.js가 install되어 있음을 전제로 한다.

프로젝트 폴더를 생성하고 npm init로 package.json을 생성한 후 express install을 실시한다.

$ mkdir myapp
$ cd myapp
$ npm init
$ npm install express --save

2. Hello world example

프로젝트 폴더(myapp)에 app.js를 생성한다.

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

터미널에서 다음 명령을 실행하여 application을 구동시킨다.

$ node app.js

서버는 port 3000에서 사용자의 접속을 대기하고 있다. 클라언트가 root URL(http://localhost:3000/)로 요청를 보내면 서버는 ‘Hello World!’로 응답할 것이다.

3. Routing

클라이언트는 서버에 URI 및 특정한 HTTP 요청 메소드(GET, POST 등)로 요청을 전달한다.

// client-side ajax request  
$.ajax({
  url    : '/signin',
  method : 'POST',
  data   : $('#signin-form').serialize()
})

이러한 클라이언트 요청에 응답하는 방법을 결정하는 것을 라우팅이라 한다. 각 라우트는 하나 이상의 핸들러 함수를 가질 수 있으며, 이러한 함수는 라우트가 일치할 때 실행된다.

라우트 정의에는 다음과 같은 구조가 필요하다.

define route

위의 클라이언트 요청에 대응하는 route를 설정해보자.

먼저 request body parsing 미들웨어인 body-parser를 설치한다. body-parser 미들웨어는 POST 요청 데이터를 request 객체의 body 속성에 바인딩한다.

$ npm install body-parser --save

설치가 되었으면 아래와 같이 app.js를 수정한다.

// Server-side: app.js  
var express    = require('express');
var bodyParser = require('body-parser');
var app = express();

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.post('/signin', function (req, res) {
  var username = req.body.username;
  var password = req.body.password;

  res.send({
   username : username,
   password : password
 });
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

postman

Postman

3.1 Route method

Express는 HTTP 메소드에 해당하는 다음과 같은 라우팅 메소드를 지원한다.

get, post, put, head, delete, options, trace, copy,
lock, mkcol, move, purge, propfind, proppatch, unlock,
report, mkactivity, checkout, merge, m-search, notify,
subscribe, unsubscribe, patch, search, connect.
// GET method route
app.get('/api/books', function (req, res) {
  res.send('GET request to the /api/books');
});

// POST method route
app.post('/api/books', function (req, res) {
  res.send('POST request to the /api/books');
});

app.all() 메서드는 모든 HTTP method에 대응한다. next()를 사용하면 후속 route handler로 제어를 전달할 수 있다.

// 모든 요청 메서드에 대응
app.all('/', function (req, res, next) {
  console.log('All request to the root section ...');
  next(); // pass control to the next handler
});

app.get('/', function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from homepage!');
});

3.2 Route path

Route path에는 문자열 또는 정규표현식을 사용할 수 있다.

// localhost:3000/
app.get('/', function (req, res) {
  res.send('root');
});

// localhost:3000/about
app.get('/about', function (req, res) {
  res.send('about');
});

// localhost:3000//random.text
app.get('/random.text', function (req, res) {
  res.send('random.text');
});

// localhost:3000/<number>
app.get(/^\/[0-9]+$/, function(req, res) {
  res.send('regexp');
});

// localhost:3000/user/<userId>/item/<itemId>
app.get('/user/:userId/item/:itemId', function(req, res) {
  res.send('userId:' + req.params.userId + ", itemId:" + req.params.itemId);
});

3.3 Route handler

Route handler는 요청을 처리하는 콜백함수이다.

app.get('/example/a', function (req, res) {
  res.send('Hello from A!');
});

next()를 사용하면 후속 route handler로 제어를 전달할 수 있다.

app.get('/example/b', function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});

함수나 함수 배열 또는 둘을 조합한 형태로 사용한다.

var cb0 = function (req, res, next) {
  console.log('CB0');
  next();
}

var cb1 = function (req, res, next) {
  console.log('CB1');
  next();
}

var cb2 = function (req, res) {
  res.send('Hello from C!');
}

app.get('/example/c', [cb0, cb1, cb2]);

app.get('/example/d', [cb0, cb1], function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from D!');
});

3.4 Response method

메소드 설명
res.download() 다운로드될 파일을 전송한다.
res.end() 응답 프로세스를 종료한다.
res.json() JSON 응답을 전송한다.
res.jsonp() JSONP 지원을 통해 JSON 응답을 전송한다.
res.redirect() 요청 경로를 재지정한다.
res.render() view template을 렌더링한다.
res.send() 다양한 유형의 응답을 전송한다.
res.sendFile() 파일을 옥텟 스트림(이메일이나 http에서 사용되는 content-type에서 application의 형식이 지정되어 있지 않은 경우에 octet-stream이라고 한다)의 형태로 전송한다.
res.sendStatus() 응답 상태 코드(response status code)를 설정한 후 해당 코드를 문자열로 표현한 내용을 응답 본문으로서 전송한다.
res.download(__dirname + '/public/report.pdf', 'report.pdf');

res.end();
res.status(404).end();

res.json(null);
res.json({ user: 'Lee' });
res.status(500).json({ error: 'message' });

res.jsonp({ user: 'Lee' });
res.status(500).jsonp({ error: 'message' });

res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login');

// send the rendered view to the client
res.render('index');
// if a callback is specified, the rendered HTML string has to be sent explicitly
res.render('index', function(err, html) {
  res.send(html);
});
// pass a local variable to the view
res.render('user', { name: 'Lee' }, function(err, html) {
  // ...
});

res.send(new Buffer('whoop'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
res.status(404).send('Sorry, we cannot find that!');
res.status(500).send({ error: 'something blew up' });

res.sendFile(__dirname + 'test.json');

res.sendStatus(200); // equivalent to res.status(200).send('OK')
res.sendStatus(403); // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404); // equivalent to res.status(404).send('Not Found')
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')

4. Middleware

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트(res) 그리고 애플리케이션의 request-response cycle 내에서 다음 미들웨어 함수 대한 액세스 권한을 갖는 함수이다.

미들웨어에는 유용한 동작을 하거나 요청이 실행되는 데 도움이 되는 무언가를 추가하는 패스스루(pass-through) 함수가 있다.

예를 들면 bodyParser()와 cookieParser()는 각각 HTTP 요청 페이로드(req.body)와 파싱된 쿠키 데이터(req.cookie)를 추가한다.

var express = require('express');

var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');

var app = express();

// parse application/json
app.use(bodyParser.json());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

현재의 미들웨어 함수가 요청-응답 주기(request-response cycle)를 종료하지 않는 경우에는 next() 를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 한다. 그렇지 않으면 해당 요청은 정지된 채로 방치된다.

var express = require('express');
var app = express();

var myLogger = function (req, res, next) {
  responseText = 'Requested at: ' + req.requestTime + '';
  console.log('LOGGED: ' + responseText);
  next(); // Call the next middleware in the stack.
};

app.use(myLogger); // Execute myLogger.

app.get('/', function (req, res) {
  res.send('Hello World!'); // End the request-response cycle.
});

app.listen(3000);

5. 정적 파일의 제공

HTML, CSS, Javascript, 이미지 파일과 같은 정적 파일을 제공하기 위해 Express의 기본 제공 미들웨어 함수인 express.static을 사용한다. 정적 파일들이 저장되어 있는 디렉터리명을 express.static 함수에 전달하면 정적 파일 서비스를 사용할 수 있다.

아래는 public 디렉터리에 있는 정적 파일을 제공하는 예이다.

app.use(express.static('public'));

기존의 웹서버 상의 파일을 요청하는 것과 동일하게 정적 파일 서비스를 사용할 수 있다.

http://localhost:3000/index.html
http://localhost:3000/images/bg.png

6. Template engine

Express는 jade, ejs, handlebars와 같은 템플릿 엔진을 사용할 수 있다.

jade

// Optional since express defaults to CWD/views
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

app.get('/', function(req, res){
  res.render('index', {
    title: 'Hello world'
  })
});

ejs

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

app.get('/', function(req, res){
  res.render('index', {
    title: 'Hello world'
  })
});
Back to top
Close