1. Middleware란?
미들웨어는 (req, res, next) 세 개의 파라미터를 가지는 함수이다.
여기에서 next 인자는 여러 개의 함수를 연결할 수 있도록 하는 함수이다.
간단한 예시 코드를 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 서버 시간이 오전이면 true를 반환, 그렇지 않으면 false를 반환
function isMorning() {...}
app.get("/hello",
// middleware #1
(req, res, next) => {
if (isMorning()) {
res.send("morning");
} else {
next();
}
},
// middleware #2: isMorning()이 false일 때 next()에 의해 실행되는 미들웨어
(req, res, next) => {
res.send("afternoon");
}
);
|
cs |
여기서는 '/hello' 경로를 처리하기 위해 두 개의 미들웨어 함수를 연결했다.
next()를 사용하여 첫 번째 미들웨어에서 두 번째 미들웨어로 제어를 전달했음을 알 수 있다.
이러한 미들웨어 함수는 경로간에 공통 코드를 공유하는 데 유용하다.
2. EX1 : 특정 경로에 대한 사용자 인증
여기에서는 사용자가 인증되었을 때만 next()를 호출하는 미들웨어를 만들었다.
미들웨어는 두 경로에서 공유되고있다. 사용자가 인증되지 않은 경우 next()를 호출하지 않아 체인이 중지된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function RequireUserAuthentication(req, res, next) {
if (req.user == null) {
res.status("401").send("User is unauthenticated.");
} else {
next();
}
}
app.get("/me/name", RequireUserAuthentication, (req, res, next) => {
res.send(req.user.name);
});
app.get("/me/avatar", RequireUserAuthentication, (req, res, next) => {
res.send(req.user.avatar);
});
|
cs |
3. EX2 : 모든 경로에 대해 사용자 인증
app.use(RequireUserAuthentication)를 사용하여 RequireUserAuthentication 미들웨어가 모든 경로에서 '주입'되도록 할 수 있다.
한 가지 주의할 점은 미들웨어가 순서에 영향을 받는다는 것이다.
따라서 아래 코드에서 app.use (RequireUserAuthentication) 이전에 정의 된 경로는 영향을받지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// This route is not affected by RequireUserAuthentication
app.get("/home", (req, res, next) => res.send(...));
// Require user auth for all routes defined after this line.
app.use(RequireUserAuthentication);
// Routes below are user sign-in required
app.get("/me/name", (req, res, next) => {
res.send(req.user.name);
});
app.get("/me/avatar", (req, res, next) => {
res.send(req.user.avatar);
});
|
cs |
4. EX3 : JSON 요청 본문을 객체로 구문 분석
때때로 요청 본문을 JSON 객체로 자동 변환하는 것이 유용하므로 모든 단일 경로에 대해 동일한 논리를 작성할 필요가 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// Converts request body into req.body as a javascript object
function JSONParser(req, res, next) {
if (req.headers['content-type'].startsWith('application/json')) {
const rawBody = readStreamIntoString(req);
req.body = JSON.parse(rawBody);
}
next();
}
// Apply JSONParser middleware to all routes defined after this line
app.use(JSONParser);
// Reads post name and content from req.body
app.get("/new/post", (req, res, next) => {
const postTitle = req.body.title;
const postContent = req.body.content;
...
});
// Reads username from req.body
app.get("/new/user", (req, res, next) => {
const userName = req.body.username;
...
});
|
cs |
여기에서 JSON 요청 본문을 객체로 구문 분석하고 객체를 req.body로 설정하는 JSONParser 미들웨어를 만들었다.
그 객체는 경로 /new /post 및 이후에 정의된 모든 다른 경로에서 적용된다.
5. EX4 : Configurable한 미들웨어
데이터 타입에 따라 다음과 같은 미들웨어 함수를 반환하는 '팩토리'함수를 만들어보는 것도 가능할 것이다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
function BodyParser(options) {
if (options.type === "JSON") {
return (req, res, next) => {
if (req.headers["content-type"].startsWith("application/json")) {
const rawBody = readStreamIntoString(req);
req.body = JSON.parse(rawBody);
}
next();
};
} else if (options.type === "URL_ENCODED") {
return (req, res, next) => {
if (
req.headers["content-type"].startsWith(
"application/x-www-form-urlencoded"
)
) {
const rawBody = readStreamIntoString(req);
req.body = new URLSearchParams(rawBody);
}
next();
};
}
}
app.use(BodyParser({ type: "JSON" }));
app.use(BodyParser({ type: "URL_ENCODED" }));
|
cs |
이처럼 미들웨어를 직접 작성하여 알맞게 사용하는 방법을 알아보았다.
또한 익스프레스에는 많은 유용한 미들웨어 모듈이 존재한다.
해당 미들웨어들을 잘 활용한다면 적은 코드로 생산성이 높은 서버를 만들 수 있을것이다!