-
TIL_200531 (Express 공식문서 부수기)Today I Learned 2020. 5. 31. 23:00
Express 앱에서 사용하기 위한 미들웨어 작성
미들웨어 함수는 요청 오브젝트(request), 응답 오브젝트(response), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수에 대한 엑세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시됩니다.
미들웨어 함수는 다음과 같은 태스크를 수행할 수 있습니다.
모든 코드를 실행
요청-응답 주기를 종료
스택 내의 그 다음 미들웨어를 호출
현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next() 를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다.
기본형식
var express = require('express'); var app = express(); // app.METHOD(URL, function(request, response, NEXT) app.get('/', function(req, res, next) { next(); }) app.listen(3000);
간단한 "Hello World" Express 애플리케이션에 두개의 미들웨어 함수를 정의해봅니다.
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World!'); }); app.listen(3000);
앱이 요청을 수신할 때마다 "LOGGED"라는 메시지를 인쇄하는 미들웨어를 추가합니다.
var express = require('express'); var app = express(); // 미들웨어 // 해당 함수를 거쳐 전달될 때 'LOGGED'를 출력합니다. var myLogger = function (req, res, next) { console.log('LOGGED'); // next()를 호출함으로써 그 다음 미들웨어 함수가 호출됩니다. next(); }; // 미들웨어 함수를 로드하기 위해 app.use()를 호출합니다. app.use(myLogger); //루트 경로(/)로 라우팅하기 전에 미들웨어 함수를 로드합니다. app.get('/', function (req, res) { res.send('Hello World!'); }); app.listen(3000);
미들웨어의 로드 순서는 중요합니다. 먼저 로드되는 미들웨어 함수가 먼저 실행되며, 루트 경로에 대한 라우팅 이후에 myLogger 미들웨어가 로드된다면 루트 경로의 라우트 핸들러가 요청-응답 주기를 종료하므로 myLogger 미들웨어로 제어가 넘어오지 않습니다.
다음은 requestTime 미들웨어 함수를 사용하는 앱을 작성해봅니다. 이 미들웨어는 요청(request) 오브젝트에 requestTime이라는 특성을 추가하여 요청을 실행할때 타임스탬프를 브라우저에 표시합니다.
var express = require('express'); var app = express(); var requestTime = function (req, res, next) { // 요청(req)의 requestTime이라는 특성을 만들고 Date.now() 함수로 현재 시각을 할당합니다. req.requestTime = Date.now(); next(); }; app.use(requestTime); app.get('/', function (req, res) { var responseText = 'Hello World!'; responseText += 'Requested at: ' + req.requestTime + ''; res.send(responseText); }); app.listen(3000);
사용자는 요청 오브젝트, 응답 오브젝트, 스택 내의 그 다음 미들웨어 함수, 그리고 모든 Node.js API에 대한 엑세스 권한을 가지고 있으므로, 미들웨어 함수에 대한 가능성은 끝이 없습니다.
미들웨어 사용
Express는 자체적인 최소한의 기능을 같춘 라우팅 및 미들웨어 웹 프레임워크이며, Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.
미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트(res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수에 대한 액세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함구는 일반적으로 next라는 이름의 변수로 표시됩니다.
미들웨어 함수는 다음과 같은 태스크를 수행할 수 있습니다.
모든 코드를 실행
요청 및 응답 오브젝트에 대한 변경을 실행
요청-응답 주기를 종료
스택 내의 그 다음 미들웨어 함수를 호출
현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다.
Express 애플리케이션은 다음과 같은 유형의 미들웨어를 사용할 수 있습니다.
애플리케이션 레벨 미들웨어
라우터 레벨 미들웨어
오류 처리 미들웨어
기본 제공 미들웨어
써드파티 미들웨어
애플리케이션 레벨 및 라우터 레벨 미들웨어는 선택적인 마운트 경로를 통해 로드할 수 있습니다. 일련의 미들웨어 함수를 함께 로드할 수도 있으며, 이를 통해 하나의 마운트 위치에 미들웨어 시스템의 하위 스택을 작성할 수 있습니다.
애플리케이션 레벨 미들웨어
app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드할 수 있습니다.
이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메서드입니다.
마운트 경로가 없는 미들웨어 함수입니다. 앱이 요청을 수신할 때마다 실행됩니다.
let app = express(); app.use(function (req, res, next) { console.log('Time: ', Date.now()); next(); });
/user/:id 경로에 마운트되는 미들웨어 함수입니다. 해당 경로에서 모든 유형의 HTTP 요청에 대해 실행됩니다.
app.use('/user/:id', function (req, res, next) { console.log('Request Type: ', req.method); next(); });
라우트 및 해당 라우트의 핸들러 함수(미들웨어 시스템)이 표시되어 있습니다.
/user/:id 경로에 대한 GET 요청을 처리합니다.
app.get('/user/:id', function (req, res, next) { res.send('USER'); });
하나의 마운트 경로에 일련의 미들웨어 함수를 로드합니다.
/user/:id 경로에 대한 모든 유형의 HTTP 요청에 대한 요청 정보를 인쇄하는 미들웨어 하위 스택을 나타냅니다.
app.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(); });
라우트 핸들러를 이용하면 하나의 경로에 대해 여러 라우트를 정의할 수 있습니다.
/user/:id 경로에 대한 GET요청에 2개의 라우트를 정의합니다. 하지만 첫번째 라우트가 요청-응답 주기를 종료시키기 때문에 두번째 라우트는 절대로 호출되지 않습니다.
app.get('/user/:id', function (req, res, next) { console.log('ID:', req.params.id); next(); }, function (req, res, next) { res.send('User Info'); }); // handler for the /user/:id path, which prints the user ID app.get('/user/:id', function (req, res, next) { res.end(req.params.id); });
라우터 미들웨어 스택의 나머지 미들웨어 함수들을 건너뛰려면 next('route')를 호출하여 제어를 그 다음 라우트로 전달할 수 있습니다.
다만 next('route')는 app.METHOD() 또는 router.METHOD() 함수를 이용해 로드된 미들웨어 함수에서만 작동합니다.
/user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.
app.get('/user/:id', function (req, res, next) { // 만약에 id가 0이라면, 라우터로 넘어갑니다 if (req.params.id == 0) next('route'); // 만약에 id가 0이 아니라면, 다음 미들웨어로 갑니다. else next(); }, function (req, res, next) { // render a regular page res.render('regular'); }); // handler for the /user/:id path, which renders a special page app.get('/user/:id', function (req, res, next) { res.render('special'); });
라우터 레벨 미들웨어
express.Router() 인스턴스에 바인드 된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일한 방식으로 작동합니다.
let router = express.Router();
router.use() 및 router.METHOD() 함수를 사용하여 라우터 레벨 미들웨어를 로드합니다.
다음 코드는 위에서 애플리케이션 레벨 미들웨어에 대해 표시된 미들웨어 시스템을 라우터 레벨 미들웨어를 사용하여 복제한 것 입니다.
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);
오류 처리 미들웨어
오류처리 미들웨어에는 항상 4개의 인수가 필요합니다. 어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개 인수를 제공해야 합니다. next 오브젝트를 사용할 필요는 없지만, 시그니처를 유지하기 위해 해당 오브젝트를 지정해야합니다. 그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류처리에 실패하게 됩니다.
다른 미들웨어 함수와 동일한 방법으로 다음과 같이 오류처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 구체적으로 (err, req, res, next) 시그니처를 갖는다는 점이 다릅니다.
app.use(function(err, req, res, next) { console.log(err.stack); res.status(500).send('Something broke!') });
기본 제공 미들웨어
express.static(root, [options])
Express의 유일한 기본 제공 미들웨어 함수입니다. serve-static을 기반으로 하며, Express 애플리케이션의 정적 자산을 제공하는 역할을 합니다. root 인수는 정적 자산의 제공을 시작하는 위치가 되는 루트 디렉토리를 지정합니다.
var options = { // dotfile을 제공, 사용 가능한 값은 "allow", "deny", "ignore" dotfiles: 'ignore', // etag의 생성을 사용 또는 사용 안함으로 설정 etag: false, // 파일 확장자 폴백을 설정 extensions: ['htm', 'html'], // 디렉토리 인덱스 파일을 전송, 작성하지 않으면 false index: false, // Cache-Control 헤더의 max-age 특성을 설정 maxAge: '1d', // 경로 이름이 디렉토리인 경우 후미부의 '/'로 경로를 재지정 redirect: false, //파일을 제공하도록 HTTP 헤더를 설정하기 위한 함수 setHeaders: function (res, path, stat) { res.set('x-timestamp', Date.now()); } } app.use(express.static('public', options));
하나의 앱은 2개 이상의 정적 디렉토리를 가질 수 있습니다.
app.use(express.static('public')); app.use(express.static('uploads')); app.use(express.static('files'));
써드파티 미들웨어
Express 앱에 기능을 추가하려면 써드파티 미들웨어를 사용합니다.
필요한 기능을 위한 Node.js 모듈을 설치한 후, 애플리케이션 레벨 또는 라우터 레벨에서 해당 모듈을 앱에 로드합니다.
쿠키 구문 분석 미들웨어 함수인 cookie-parser의 설치 및 로드입니다.
$ npm install cookie-parser
let express = require('express'); let app = express(); let cookieParser = require('cookie-parser'); // load ths cookie-parsing middleware app.use(cookieParser());
미들웨어 모듈 설명 내장 함수 (Express 3) body-parser HTTP 요청 body를 파싱합니다. body, co-body, 그리고 raw-body도 참고하세요. express.bodyParser compression HTTP 요청들을 압축합니다. express.compress connect-rid 고유한 요청 ID를 생성합니다. 없음 cookie-parser 쿠키 헤더를 파싱하고 req.cookies에 할당합니다. cookies와 keygrip도 참고하세요. express.cookieParser cookie-session 쿠키 기반의 세션을 만듭니다. express.cookieSession cors 다양한 옵션들을 이용하여 Cross-origin resource sharing (CORS)를 활성화합니다. 없음 csurf CSRF 취약점을 방어합니다. express.csrf errorhandler 개발 중에 발생하는 에러를 핸들링하고 디버깅합니다. express.errorHandler method-override 헤더를 이용해 HTTP method를 덮어씁니다. express.methodOverride morgan HTTP 요청 로그를 남깁니다. express.logger multer multi-part 폼 데이터를 처리합니다. express.bodyParser response-time 응답 시간을 기록합니다. express.responseTime serve-favicon 파비콘을 제공합니다. express.favicon serve-index 주어진 경로의 디렉토리 리스트를 제공합니다. express.directory serve-static 정적 파일을 제공합니다. express.static session 서버 기반의 세션을 만듭니다 (개발 전용). express.session timeout HTTP 요청 처리를 위해 timeout을 만듭니다.. express.timeout vhost 가상 도메인을 만듭니다. express.vhost 추가 미들웨어 모듈
유명한 외부 미들웨어 모듈들
미들웨어 모듈 설명 connect-image-optimus 이미지 제공을 최적화힙니다. 할 수 있다면 이미지를 .webp나 .jxr로 바꿉니다. express-debug 템플릿 변수 (지역), 현재 세션, 기타 등등에 대한 정보를 제공하는 개발 도구입니다. express-partial-response JSON 응답을 URL의 fields를 받아서 필터링합니다. Google API의 Partial Response를 활용합니다. express-simple-cdn 정적 요소들을 위해 CDN을 사용하도록 도와줍니다. 다양한 호스트를 지원합니다. express-slash 구현된 루터에 맟춰서 슬래쉬 유무를 맞춰줍니다. express-stormpath 사용자 저장소, 인증 확인, 인증, SSO, 그리고 데이터 보안에 관련된 모듈입니다. (Okta로 합쳐졌습니다) express-uncapitalize 대문자를 포함하는 HTTP 요청들을 표준 소문자 폼으로 리다이렉트시킵니다. containing uppercase to a canonical lowercase form. helmet 다양한 HTTP 헤더를 설정해 앱이 안전하게 도와줍니다. join-io 요청 횟수를 줄이기 위해 파일들을 묶어줍니다. passport OAuth, OpenID 같은 방법들을 사용하는 인증 체계입니다. 자세한 정보는 http://passportjs.org/에서 확인하세요. static-expiry 정적 에셋을 위해 헤더를 캐싱하거나 URL을 고유화합니다.. view-helpers 뷰 엔진들을 위한 일반적인 도움을 제공합니다. sriracha-admin 동적으로 Mongoose의 관리 사이트를 만듭니다.
오류처리
다른 미들웨어 함수와 동일한 방법으로 오류 처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 즉 (err, req, res, next)를 갖는다는 점이 다릅니다.
app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); });
오류 처리 미들웨어는 다른 app.use() 및 라우트 호출을 정의한 후에 마지막으로 정의해야 합니다.
let bodyParser = require('body-parser'); let methodOverride = require('method-override'); app.use(bodyParser()); app.use(methodOverride()); app.use(function(err, req, res, next) { // logic });
미들웨어 함수 내부로부터의 음답은 HTML 오류 페이지, 단순한 메시지 또는 JSON 문자열 등 작성자가 선호하는 모든 형식일 수 있습니다.
조직적 목적을 위해, 여러 오류 처리 미들웨어 함수를 정의할 수 있으며 이는 일반적인 미들웨어 함수를 정의할 때와 매우 비슷합니다.
let bodyParser = require('body-parser'); let methodOverride = require('method-override'); app.use(bodyParser()); app.use(methodOverride()); app.use(logErrors); app.use(clientErrorHandler); app.use(errorHandler);
// logErrors는 요청 및 오류 정보를 stderr에 기록할 수 있습니다 function logErrors(err, req, res, next) { console.error(err.stack); next(err); }
// clientErrorHandler와 같이 정의되는 경우 오류는 명시적으로 다음 항목으로 전달됩니다. function clientErrorHandler(err, req, res, next) { if (req.xhr) { res.status(500).send({ error: 'Something failed!' }); } else { next(err); } }
// errorHandler 함수는 모든 오류를 처리하는(catch-all) 함수입니다. function errorHandler(err, req, res, next) { res.status(500); res.render('error', { error: err }); }
next() 함수로 어떠한 내용을 전달하는 경우('route' 문자열 제외) Express는 현재의 요청에 오류가 있는 것으로 간주하며, 오류처리와 관련되지 않은 나머지 라우팅 및 미들웨어 함수를 건너뜁니다. 이러한 오류를 처리하기 원하는 경우, 오류처리 라우트를 작성해야 합니다.
여러 콜백 함수를 갖는 라우트 핸들러가 있는 경우에는 route 매개변수를 사용하여 그 다음의 라우트 핸들러로 건너뛸 수 있습니다.
app.get('/a_route_behind_paywall', function checkIfPaidSubscriber(req, res, next) { if(!req.user.hasPaid) { // continue handling this request next('route'); // 다음 핸들러의 실행을 건너뜁니다. } }, function getPaidContent(req, res, next) { PaidContent.find(function(err, doc) { if(err) return next(err); res.json(doc); }); });
기본 오류 핸들러
Express는 내장된 오류 핸들러와 함께 제공되며, 내장 오류 핸들러는 앱에서 발생할 수 있는 모든 오류를 처리합니다.
'Today I Learned' 카테고리의 다른 글
TIL_200611 (0) 2020.06.12 TIL_200601 (0) 2020.06.02 TIL_200530 (What is Express) (0) 2020.05.31 TIL_200529 (what is Redux) (0) 2020.05.29 TIL_200528 (0) 2020.05.29