미들웨어
말 그대로 요청(Request)과 응답(Response) 사이 중간(middle)에서 핸들링해주는 익스프레스의 핵심 기능이다.
사용법
use
메서드를 사용하고 요청 메서드 내부에서 미들웨어를 정의할 수 있다.
//모든 요청에서 미들웨어 실행
app.use((req, res, next)=> {
console.log('middle ware execute');
next();
}, (req, res) => {
// code..
});;
//특정 요청 또는 경로에서 미들웨어 실행 (next 인자는 생략 가능)
app.get(('/', (req, res, next)=>{
console.log("request get, '/'");
next();
}, (req, res)=>{
throw new Error('error!');
});
연속성
여러 인자로 콜백 함수를 전달하면 연달아 미들웨어를 사용할 수 있다.
app.use(
(req, res, next) => {
console.log('first middleware!');
next();
},
(req, res, next) => {
console.log('second middleware!');
next();
},
(req, res, next) => {
console.log('thrid middleware!');
next();
}
);
app.get('/', (req, res) => {
console.log('complete!');
});
미들웨어 확장
미들웨어 내부에 미들웨어를 삽입하는 방식으로, 예를 들어 특정 사용자 별 다른 리소스를 응답하여야 할 경우에 사용한다.
원리는 미들웨어의 패턴인 (req, res, next)
내부적으로콜백을 전달하는 방식이다. 이러한 패턴을 미들웨어 확장법이라 한다.
app.use('/user', (req, res, next) => {
if (req.session.id) {
express.static(__dirname, 'assests')(req, res, next);
} else {
next();
}
});
데이터 공유
혹시 여러 미들웨어에서 요청 값을 공유하고 싶다면 절대로 전역변수(app.set)
을 통해 클라이언트의 데이터를 공유해서는 안된다.
미들웨어의 request
인수에 값을 저장하면 요청이 끝나기 전 까지 서로 다른 미들웨어에서 공유할 수 있다.
// custom middleware
app.get('/', (req, res, next) => {
req.data = 'jiny';
next();
});
app.get('/', (req, res) => {
res.send(`${req.data}`); //jiny
});
에러 핸들링
에러 처리도 마찬가지로 미들웨어로 핸들링 할 수 있다.
주의사항으로는 에러 미들웨어는 반드시 인자 4개 (error, req, res, next)
를 꼭 받아야한다.
(인자를 굳이 쓰지 않아도 반드시 넘겨주어야한다. 함수 취급 자체가 달라진다.)
app.use((err, req, res, next) => {
console.err(err);
});
에러 처리 미들웨어로 보내는 방법은 next
콜백에 여러 객체를 넣어주는 방법이 있다. next에 에러 인자가 들어가게되면 알아서 에러처리 미들웨어로 실행하도록 한다. 에러는 예고 없이 일어나기 때문에 try-catch
로 핸들링 해야한다.
app.use(
(req, res, next) => {
console.log('middle ware execute!');
next();
},
(req, res, next) => {
try {
throw new Error('error!!'); //고의적인 에러 발생
} catch (error) {
next(error);
}
}
);
//에러 핸들링 미들웨어
app.use((err, req, res, next) => {
console.error(err);
// http 규칙 상 500으로 전달되어야하긴 하지만, 보안 위협을 방지하여 200으로 속인다.
res.status(200).send('undefined error');
});
유용한 미들웨어 소개
morgan
요청 정보를 로깅해주는 라이브러리이다. 여러 옵션이 있다.
const express = require('express');
const morgan = require('morgan');
const app = express();
app.set('port', 3111);
app.use(morgan('dev')); //log level
app.get('/', (req, res) => {
res.send('home');
});
app.listen(app.get('port'), () => {
console.log('start express server');
});
- dev : 요청 url과 응답 시간을 출력한다.
GET /test 200 3.063 ms - 4
- common : 접속 ip와 요청 시각을 출력한다.
::1 - - [02/Jan/2022:07:34:31 +0000] "GET /test HTTP/1.1" 200 4
- combined : 상세한 정보를 출력한다. (브라우저, 접속 환경, ip)
::1 - - [02/Jan/2022:07:36:54 +0000] "GET /test HTTP/1.1" 200 4 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
cookieparser
쿠키 파싱을 편하게 해주는 라이브러리
const cookieparser = require('cookie-parser');
const app = express();
app.set('port', 3111);
//[private-key] 비밀키 옵션 (암호화)
app.use(cookieparser('jiny'));
쿠키 얻어오기
req 인수를 통해 쿠키 값을 가져올 수 있다. 일반 쿠키이면 req.cookies
를 사용하고 서명된 쿠키이면 req.signedCookies
를 사용한다. (서명 쿠키일 때 signed
옵션을 true로 줘야한다.)
app.get('/', (req, res) => {
req.signedCookies; //서명이 된 쿠키
});
쿠키 생성하기
res 인수의 cookie
메서드를 통해 쿠키를 전송할 수 있다.
사용 방법은 아래와 같다. 인자에 순서대로 쿠키 이름, 값, 쿠키 옵션 객체 등을 넣어주면된다.
app.get('/', (req, res) => {
res.cookie('cookie-name', 'cookie-value', {
expires: new Date(Date.now() + 1000000),
httpOnly: true,
path: '/',
signed: true
});
res.send('main');
});
쿠키 삭제하기
더 이상 필요가 없는 쿠키는 clearCookie
메서드로 삭제할 수 있다.
쿠키를 삭제할 때는 key-value와 옵션도 정확히 일치해야한다.
app.get('/clear', (req, res) => {
res.clearCookie('cookie-name', 'cookie-value', {
expires: new Date(Date.now() + 1000000),
httpOnly: true,
path: '/'
});
res.send('clear-cookie!');
});
static
express 환경에서 정적인 파일(html, css 등등..)들을 제공하는 일종의 라우터 역할을 하는 미들웨어이다. 요청 경로와 실제 경로를 다르게 설정 할 수 있어 상대적으로 보안에도 조금 도움이 된다.
여러 미들웨어들을 사용할 때 첫 부분에 와야 자원 낭비를 조금이나마(?) 줄일 수 있다. (미들웨어의 순서는 생각하면서 작성)
사용법
const express = require('express');
const app = express();
app.use('/', express.static('/public'));
//디렉토리 절대 경로 식을 사용하면 더 안정적이다.
app.use('/', express.static(__dirname + 'public'));
express-session
cookie-parser
처럼 세션을 효율적으로 관리할 수 있게 하는 라이브러리이다. 요청 클라이언트 마다 별도의 데이터를 set 해 앱 내에서 관리할 수 있다. (로그인 기능, 임시 데이터 등)
1.5 버전 이전이라면 cookie-parser
미들웨어 보다 앞에 선언해야한다.
npm i express-session
사용법
express-session
을 import 후, 인자로 세션에 대한 설정 객체를 넘기면 된다.
const session = require('express-session');
app.use(
session({
resave: true, //항상 세션을 덮어씌울지
saveUninitialized: false, //처음부터 세션을 생성할지
secret: 'secret-key', //쿠키 서명 값
cookie: {
//세션 쿠키 옵션
httpOnly: true,
secure: false
},
name: 'session-cookie' //세션 쿠키 이름 (defalut : connect.sid)
})
);
dotenv
시스템 내의 환경 변수를 효율적으로 관리해주는 라이브러리이다. 기존 node에서 존재하는 process.env
객체로 관리하는 것과 같은 역할을 한다. (비밀키와 같은 외부에 노출되어야 하지 않는 값들에 사용한다.)
npm install dotenv
사용법
프로젝트 내부에 .env
라는 파일을 만든다.
아래와 같이 key = value
형태로 관리 프로퍼티들을 작성한다.
COOKIE_PRIKEY = jiny
DB_PASSWORD = mysql123
프로젝트 메인 파일에서 dotenv
모듈을 불러온 뒤 config()
함수를 호출만 하면 된다. 주의 할 사항은 먼저 process.env
를 읽어버리면 안된다. 그래서 보통 소스 최상위 라인에 dotenv.config 호출을 위치시킨다.
const dotenv = require('dotenv');
dotenv.config();
console.log(process.env.COOKIE_PRIKEY); //jiny
참고자료
- Node.js 교과서 - 미들웨어 사용하기 (조현영 저)