지금까지 Express로 기초적인 서버를 만들어 보았습니다. 그렇다면 우리가 서버에 요청할 때에, 한 번에 하나의 일만 처리하는 것보다 더 많은 일들을 할 수 있지 않을까요? 어떤 url에 대해 한 번 요청하고 난 뒤, 해당 요청에 대해 응답을 처리하는 일 외에도 별도의 일을 중간에 처리하고 싶으면 어떨까요?
이번 포스팅에서는 Express.js 의 장점 중 하나이자, 서버 프로그래밍에서 널리퍼져있는 개념인 미들웨어가 무엇인지 알아보고, 이를 활용해봅시다.
미들웨어?
미들웨어(middleware)는 서버에 들어온 요청에 대해, 중간에 위치하여서 요청을 처리하기 전에 실행하는 소프트웨어를 말합니다. 소프트웨어라는 말에서 알 수 있듯이, 미들웨어라는 개념은 Express.js 뿐만 아니라, 다른 소프트웨어 업계에서도 쓰입니다.
Express.js 에서는 들어온 요청을 처리하는 중간에 미들웨어를 삽입하여 처리하고 싶은 작업을 중간중간에 실행시켜줄 수 있습니다. 이제 어떻게 처리가 되는지 한 번 살펴보도록 하지요.
기본적인 미들웨어 사용해보기
이번 포스트에서는 Express.js를 사용할 때에 자주 사용되는 몇가지 미들웨어를 활용해보고, 마지막에는 작은 기능이지만 미들웨어를 직접 구현해보도록 하겠습니다. 우선 서버에서 요청이 들어올 때에 해당 요청에 대한 로그를 남기는 morgan이라는 미들웨어를 사용해보도록 하겠습니다.
npm i morgan
Shell
복사
//app.js
const express = require('express');
const app = express();
const dotenv = require("dotenv");
const logger = require("morgan"); // 로거 선언
const compression = require("compression");
dotenv.config();
const basic_port = 3000;
app.use(logger('short')); // 로거 사용
app.set('port', process.env.PORT || basic_port);
app.get('/', (req, res) => {
res.send('hello, Express');
});
app.listen(app.get('port'), () => {
console.log(`${app.get('port')}에서 대기중입니다.`);
});
JavaScript
복사
이렇게 로거를 설치하고 나면 package.json 내부의 dependencies에 등록이 되고, 이를 require로 가져와서 사용할 수 있습니다. app.use() 함수를 이용해 가져온 미들웨어를 간단하게 사용해줄 수 있는데요. 이제 이 로거가 잘 동작하는 지 보겠습니다. 로거 사용을 위해 app.use 의 매개변수로 로거를 주고, 간단한 로그만 받기 위해 옵션 'short' 를 주어보았습니다.
보시다시피 포트 4333에 정상적으로 서버가 작동되고 있고, localhost:4333/ 으로 접속하니 아래와 같이 한 줄로 로그가 남겨진 것을 볼 수 있습니다. 저번 포스트에서 app.get() 은 매개변수 첫 번째로 들어온 url에 대해서 들어온 http 메서드 중 GET 메서드 요청에 대해서 대응하는 함수라고 설명을 드렸습니다. 우리가 서버를 열어둔 localhost:4333/ 주소에 대해 GET 요청을 하였으니, 이 요청에 대해 로거가 로그를 마지막 줄에 딱 남겨둔 것을 볼 수 있습니다. 어떤 메서드로 요청이 들어왔고, 프로토콜 버전과, http 응답코드, 그리고 요청 처리 시간을 기록한 걸 볼 수 있습니다.
이러한 로그 외에도 다양한 입력 처리를 위한 body-parser나, 세션관리에 용이한 session, 다른 도메인의 요청에 대해서 처리하는 cors, 계정 기능을 간편하게 구축할 수 있는 passport , Express에서 라우터를 쉽게 구축할 수 있는 express-router 가 있습니다.
나만의 로거를 만들어보자
이제 미들웨어를 활용하는 방법을 알았으니, 자신만의 미들웨어를 구축할 수도 있겠죠? 금방 전까지 morgan 으로 로그를 콘솔에 찍었는데, 이제는 이 글을 보고 있는 우리가 로그를 직접 만들어 볼 수도 있을 것 같습니다. 간단한 자신만의 로그를 만들어봅시다.
app.use() 함수를 보시면 아시다시피 어떤 미들웨어를 가져와서 app.use 로 실행해줍니다. 그렇다면 우리가 미들웨어를 만들려고 하려면 어떻게 해야할까요? app.use 에 인자로 던져주는 함수를 구현하면 됩니다. 저는 아래와 같이 만들었습니다. 코드는 이전 포스팅에 쓰던 코드를 계속 활용하겠습니다.
//index.js
const express = require('express');
const app = express();
const dotenv = require("dotenv");
const logger = require("morgan");
const compression = require("compression");
dotenv.config();
const basic_port = 3000;
app.use(compression());
// 구현한 로거 구현부분
app.use((req, res, next) => {
console.log(`지금은 ${req.ip}에 ${req.method} 요청을 받았습니다.`);
next();
})
// 구현부분 끝!
app.use(logger('short'));
app.set('port', process.env.PORT || basic_port);
app.get('/', (req, res) => {
res.send('hello, Express');
});
app.listen(app.get('port'), () => {
console.log(`${app.get('port')}에서 대기중입니다.`);
});
JavaScript
복사
잘 보시면 app.use 에 콜백으로 던져주는 함수를 구현하여 미들웨어를 구현함을 알 수 있습니다. 그러나 다른 함수들과 특이한 부분은 3번째 매개변수로 던져주는 next함수 라는 인자인데요. 미들웨어에서 작업을 마치고 나면 이 인자를 내부적으로 실행해주어야만 다음 절차로 건너갈 수 있습니다. 반드시 next 함수를 끝에서 실행시켜주세요! 그렇지 않으면 요청이 마쳐져서 다음 절차를 넘기지 않고 종료됩니다.
로그는 간단하게 요청이 들어오는 req 객체 내부의 ip와 http 메서드 정보를 가져와서, 콘솔에다 띄우도록 구현했습니다. req 와 res 객체는 각각 요청과 응답 객체인데, 객체 내부에는 흥미로운 정보들이 많이 있으므로 상상력을 넓힌다면 다양한 기능을 구현할 수 있겠지요? 공식 메뉴얼을 참고하여 무엇이 있는지 알아보는 것도 좋을 것 같습니다.
이제 제가 만든 로거가 잘 만들어졌는지 확인을 해볼까요?
서버를 실행하고 홈페이지에 접속해보니 잘 구동되는 것을 확인할 수 있습니다!