Chapter6 express

3 분 소요

Express

4장에서 웹 서버를 http 모듈만으로 만들기 때문에 불편하고 확장성도 떨어진다 느꼈을 것이다. npm에는 서버 제작 시 불편함을 해소하고, 편의 기능을 추가한 웹 서버 프레임워크가 있다.

express 외에도 koa나 hapi 같은 웹 서버 프레임워크가 존재하지만, express의 다운로드 수가 압도적으로 높다. 이는 성능과 직접적인 연관이 없을지 모르지만 기능 추가나 유지보수 측면에서 좋게 작용할 것이다.

express-generator

익스프레스 프레임워크는 많은 패키지를 사용하여 입문자 입장에서는 찾아서 설치하기 어렵다. express-generator가 이를 대신 해줄 수 있는 패키지이다.

콘솔에서 npm i -g express-generator를 입력하자.


설치가 완료되었다면 새 익스프레스 프로젝트 생성을 위해 express <project-name>을 입력하자

express learn-express --view=pug

   create : learn-express\
   create : learn-express\public\
   create : learn-express\public\javascripts\
   create : learn-express\public\images\
   create : learn-express\public\stylesheets\
   create : learn-express\public\stylesheets\style.css
   create : learn-express\routes\
   create : learn-express\routes\index.js
   create : learn-express\routes\users.js
   create : learn-express\views\
   create : learn-express\views\error.pug
   create : learn-express\views\index.pug
   create : learn-express\views\layout.pug
   create : learn-express\app.js
   create : learn-express\package.json
   create : learn-express\bin\
   create : learn-express\bin\www

   change directory:
     > cd learn-express

   install dependencies:
     > npm install

   run the app:
     > SET DEBUG=learn-express:* & npm start

여기서 --view=pugexpress-generatorJade템플릿 엔진으로 설치하기 떄문이다. JadePug로 개명한 지 오래되었다. 따라서 Pug설치를 위해 옵션을 주는 것.

이후 learn-express 폴더가 생성되었을 것이기에 이동 후 npm 모듈을 설치한다.
cd learn-express && npm i

package.jsonscripts 설정 값에 start속성이 있고 node ./bin/www가 적혀있다. 따라서 npm run start 명령어로 서버를 실행할 수 있다. http://localhost:3000 으로 접속하면 기본 페이지가 구성되어 있을 것이다.

익스프레스 구조

가장 중요한 www 파일 구조부터 살펴보자 bin/www 파일은 http 모듈에 express를 연결하고 포트를 지정하는 부분이다.

js 확장자가 붙어있지 않은 이유는 추후에 14장에서 설명 가능하다.

var app = require('../app');
var debug = require('debug')('learn-express:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 debug 모듈은 콘솔에 로그를 남기는 모듈이다.
 app 모듈은 나중에 살펴보도록 하자.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * process.env 객체에 PORT 속성이 있다면 그 값을 사용하고 아니라면 3000을 사용한다.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 app 모듈을 넣어준다. app 모듈이 createServer 메서드의 콜백 함수 역할을 한다.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 콜백 함수를 달아주고 포트를 연결하여 서버를 실행시킨다.
 */

app 모듈은 어떻게 생겼는지 보자.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();
//express 패키지를 호출하여 app 변수 객체를 만들었다. 이 변수의 각종 기능을 연결.
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
//app set method로 express app을 설정할 수 있다.

//~~~ middleware 부분은 이후 알아보도록 하자.

module.exports = app;

클라이언트 요청을 받아서 처리한 후 다시 클라이언트에게 응답한다. 이는 4장의 http 서버와 같지만 중간에 middleware를 거친다는 것이 다릅니다 그럼 미들웨어가 무슨 역할을 하는지 알아보자.

미들웨어


middlewareexpress의 핵심이다. 요청과 응답의 중간에 위치하여 middleware라고 부른다.

...
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());


app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
...

middleware는 주로 app.use와 함께 사용된다. 위에서 본 것과 같이 app.js에선 app.use를 계속 사용하고 있다. app.use 메서드의 인자로 들어 있는 함수가 middleware이며, app에 장착된다.

제일 위에 logger('dev')부터 시작하여 순차적으로 거친 후 라우터에서 클라이언트로 응답을 보낸다.

logger -> logger(morgan) -> json, unlencoded -> cookieParser -> Router -> 404 handler -> error handler

커스텀 미들웨어

단순히 request에 대해 콘솔에 메세지를 찍는 미들웨어를 만들어 보자.

...
app.use(function(req, res, next){
  console.log(req.url, 'im middleware')
  next();
});
//반드시 next();를 호출해주어야 다음 미들웨어로 넘어간다.
app.use(logger('dev'));
...

next()를 호출하지 않으면 다음 미들웨어로 넘어가지 않으며, loggerexpress.json, express.urlencoded, cookieParser, express.static 모두 내부적으로 next() 를 호출하므로 다음 미들웨어로 넘어간다. next()는 흐름 제어의 핵심적인 함수이다!

next는 인자에 따라 특정한 역할도 하는데 route를 넣으면 특수한 기능을 수행한다. 이는 라우터와 함께 알아보도록 하고, route이외에 다른 값을 넣으면 바로 에러 헨들러로 이동한다. 넣어준 값은 에러에 대한 내용으로 간주된다. express가 기본적으로 주어주는 에러 핸들링 미들웨어를 보면 이해가 쉽다.

...
// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});
...
...
// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
...

일반적으로 에러 헨들링 미들웨어는 가장 아래 위치하여 위에 있는 미들웨어에서 발생하는 에러를 받아 처리한다.

express-session

세션 관리용 미들웨어이다.

Refernce
  • 조헌영, Node.js 교과서, 길벗, 6장 express