본문 바로가기

SCRIPT/NODE JS

[Nodejs] Express Middleware 유형

Express 
Node.js 웹 프레임워크. Web Application 이나 API 서비스 개발을 위해 설계되었으며,
가장 많이 사용되는 웹 프레임워크 중 하나.
* Koa 등의 다양한 웹 프레임워크가 존재하며, 필요와 선택에 따라서 웹 프레임워크를 사용하면 될 것 같다.

 

MiddleWare
네트워크를 통해서 연결된 여러개의 컴퓨터에 있는 많은 프로세스들에게 어떤 서비스를 사용할 수 있도록 연결해주는 소프트웨어.
* 웹 서비스 기준으로 쉽게말해서 [ 클라이언트 - 미들웨어 - 서버 ] 사이에서 데이터를 읽고, 쓰고, 처리해주는
중간 역활을 하는 소프트웨어라고 볼 수 있겠다.

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크이며, Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시됩니다.

  미들웨어 함수 TASK

  • 모든 코드를 실행.
  • 요청 및 응답 오브젝트에 대한 변경을 실행.
  • 요청-응답 주기를 종료.
  • 스택 내의 그 다음 미들웨어 함수를 호출.

참고 URL : https://expressjs.com/ko/guide/using-middleware.html#middleware.built-in

- 모든 코드를 실행

app.get('/', function (req, res) {
  res.send('main');
});
app.get('/test', function (req, res) {
  res.send('test');
});

https.createServer(httpsOptions, app).listen(sPort, function() {
	console.log("[ Express HTTPS Server Start on Port ] :: " + sPort);
});

> 코드된 미들웨어 함수들이 실행됩니다. 또한, 코드들은 '순서' 대로 진행되므로, 상황에 따라 ( 에러 처리등.. ) 순서대로 코드를 작성해야 합니다. 

 

- 요청 및 응답 오브젝트에 대한 변경을 실행 , 요청-응답 주기를 종료

> Request & Response 오브젝트를 변경하여, 중간역활을 수행하고, 요청에 대한 응답시 해당 주기는 종료됩니다. 하나의 요청에 하나의 응답만 가능하므로, 해당 주기안에서 모든 처리를 한 후, 응답을 하도록 합니다. 만약 응답을 하지 않을 경우, 그대로 해당 코드는 '정지'상태에 있으며, 클라이언트에서 응답을 기다리고 있는 요청일 경우, 일정 시간후, 에러가 발생하게 됩니다.

- 스택 내의 그 다음 미들웨어 함수를 호출.

app.get('/test1', function (req, res, next) {
	console.log('test1 start !');
    next();
});
app.get('/test2', function (req, res, next) {
  console.log('test2 start !');
  next();
});
app.get('/test3', function (req, res) {
  console.log('Test Over !');
  res.send('TEST OVER !');
});

> 위와 같이 next() 함수를 통해서, 다음 함수를 순서대로 호출하여 처리할 수 있습니다.

 

1) 애플리케이션 레벨 미들웨어

app.use(),app.method() 함수를 이용해서, 미들웨어를 제어할 수 있습니다. (https://expressjs.com/ko/4x/api.html#app )

var app = express();
// # Code 1
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// # Code 2
app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  console.log('CODE 2 -1 ');
  res.send('User Info');
});

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
  console.log('CODE 2 -2 ');
  res.end(req.params.id);
});

# Code 1 

 위와 같이 함수 정의시, 스택 아래의 모든 함수는 '순서대로' 실행됩니다.                                                           아래와 같이, #Code 1 의 처리 후, # Code 2 로 진행되게 됩니다. 

// 결과 값
> Time: 시간
> Id : 요청.id
> CODE 2 -1 

 

# Code 2

Code 2 와 같이, 특정 경로의 요청에 대한 처리도 가능합니다. '/user/:id' 에 대한 요청처리 함수를 정의했고, 해당 경로로 Get 요청이 왔을때 위의 함수가 실행됩니다.

> 왜 결과값이  CODE 2 -1 만 나왔을까요? 분명 /user:id/ 에 대한 요청에 대해 두가지 응답을 정의했는데 말이죠. 이유는 위에서 이야기한 '순서대로' 실행되기 때문에, 처음에 정의된 Code 2-1 에 대한 수행이 진행이 되었고, 해당 요청에 대한 응답도 이미 끝난상태입니다. 분명히 Code 2 -2 는 절대 실행되지 않을 것 입니다. 이처럼 코드의 순서는 중요합니다. code 2-2 를 실행하려면, 다른 경로의 요청처리로 함수를 정의하던지, next() 함수를 사용해서 Code 2 -1에서 해당 요청을 종료하지말고, Code 2 -2 로 넘겨서 처리를 해야 할 것입니다.

 

 

2) 라우터 레벨 미들웨어

var app = express();
var router = express.Router();

// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next router
  if (req.params.id == 0) next('route');
  // otherwise pass control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// mount the router on the app
app.use('/', router);

 

동작 방식은 어플리케이션 레벨과 동일하며, 보통 각각의 요청에 대한 처리를 기능별 혹은 화면별 등의 개발자와 설계에 따라서 구분하여 분리해 사용합니다.

 

3) 오류 처리 미들웨어

 오류 처리 미들웨어에는 항상 4개 의 인수가 필요합니다. 어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개의 인수를 제공해야 합니다. next 오브젝트를 사용할 필요는 없지만, 시그니처를 유지하기 위해 해당 오브젝트를 지정해야 합니다. 그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류 처리에 실패하게 됩니다.

 쉽게 말해서, 기존의 (req, res , next) 3개의 인수정도 사용했지만, error 처리 미들웨어를 사용하기 위해서는 4개의 인수를 사용해야지만, 오류 처리 미들웨어라는걸 인식하고 동작한다는 이야기 입니다.

// Server Error 
app.use(function(err, req, res, next) {
  console.log('Server Error : ' + err);
  res.status(500).send("시스템 에러입니다. 관리자에게 문의하세요.");
});

// Client Error
app.use(function(req, res, next) {
  res.status(404).send("페이지를 찾을 수 없습니다.");
});

ServerError 감지시, 해당 텍스트롤 보여주는 예제, 404 에러 발생시 ( 페이지 찾을 수 없을 경우) 의 예제입니다.

 

4. 기본 제공 미들웨어 

4.x 버전 이후의 Express는 더 이상 Connect에 종속되지 않습니다. express.static의 실행을 제외하면, 이전에 Express와 함께 포함되었던 모든 미들웨어 함수는 이제 별도의 모듈에 포함되어 있습니다. 미들웨어 함수 목록을 확인하십시오.

본 예제들은 2019 년 9월 7일 기준, 4.16.4 Express 버전을 토대로 작성한 것이고, 현재는 Alpha 버전으로 5.xx 버전을 배포하고 있습니다.

express.static(root, [options]) 특성

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

// 정적 디렉토리
app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

Express App 의 정적 리소스들을 관리하고, 설정할 수 있습니다. 정적 디렉토리는 2개 이상 설정할 수 있습니다.

정적 디렉토리를 지정하지 않는 경우에는, 미들웨어에서 정적 리소스에 접근할 수 없기 때문에, 업로드 파일, 공통 파일 (이미지, 라이브러리 등등.. ) 을 설정하여, 해당 리소스 경로를 잡아주셔야 합니다.

 

 

5. 써드파티 미들웨어 

> 3rd Party 는 쉽게 이야기해서, 외부 라이브러리를 이용하여, Express 웹 프레임워크에 기능을 추가하는 것 이라고 보시면 될 것 같습니다.

- 파비콘 : 해당 웹 브라우저의 아이콘

- 로그 : 서버 콘솔에 로그를 남기는 것으로, 에러, 기능 확인 등의 용도로 사용할 수 있습니다.

- 바디파서 : 클라이언트에서 POST , PUT 요청시, BODY 에 담긴 데이터를 파싱해서 데이터를 분석하기 위해 사용합니다

대표적으로 위의 3가지 예제가 있습니다. 이전 버전의 경우, 파비콘이나 로그기능을 사용하기 위해서는 해당 기능을 제공하는 라이브러리를 설치하여, 불러오는 방식을 사용했지만 현재는 express 웹 프레임워크에 내장되어 있어서, 설정 후 사용만 하시면 됩니다.

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

// 파비콘
app.use(favicon(path.join(__dirname, 'public', 'icons', 'favicon.png'))); //loads

// 로그
console.log('Log MiddleWare !');


// bodyParser 
var bodyParser = require('body-parser');
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true, parameterLimit: 50000 }));

// 홈페이지 본문에는 cookieParser 예제가 있습니다.
var cookieParser = require('cookie-parser');
app.use(cookieParser());