
Routing, View Helper, Session(์์์ Session๊ด๋ฆฌ๋ฅผ ์ํด์๋ย Redis๋ฑ์ Data store๊ฐ ํ์ํ๋ค)๋ฑ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.์น ์ ํ๋ฆฌ์ผ์ด์ ํ๋ ์์ํฌ๋?
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๋ ์์ฃผ ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ๋ค์ ๋ฏธ๋ฆฌ ๊ตฌ์กฐํํ์ฌ ์ ๊ณตํ๋ ์ํํธ์จ์ด ํ๋ ์์ํฌ
์ฆ, ์น ์๋น์ค๋ฅผ ๋ ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก ๊ฐ๋ฐํ ์ ์๋๋ก ๋์์ฃผ๋ ๊ฐ๋ฐ ๋๊ตฌ ์ธํธ์ธ ๊ฒ.
Layer ๊ฐ์ฒด๋ก ๊ฐ์ธ์ ธ์ ๊ด๋ฆฌ๋๋ค.Layer๋ ์ต์ํ์ ๊ณตํต ๊ตฌ์กฐ๋ง ์ ์ํ๊ณ , ์ํฉ์ ๋ฐ๋ผ ์์ฑ์ ์ ๋์ ์ผ๋ก ์ถ๊ฐํ๋ค.express/lib/router/layer.js ํ์ผ์์ ์ ์name: ํจ์ ์ด๋ฆhandle ํ๋กํผํฐ์ ๊ฐ์ธ ํจ์์ ์ด๋ฆ์ด๋ค.handle: ์ค์ ๋ก ์ฐ๋ฆฌ๊ฐ ๋ฑ๋กํ MiddleWare, Router, Routeregexp: ๊ฒฝ๋ก๋ฅผ ์ ๊ทํํ์์ผ๋ก ๋ณํํ ๊ฐpath : ํด๋น ์์ฒญ์์ ์ผ์นํ ์ค์ ๊ฒฝ๋ก ๋ถ๋ถ(ํ์ฌ ๋ ์ด์ด(layer)์์ ์ฒ๋ฆฌ ์ค์ธ ์๋ ๊ฒฝ๋ก๋ฅผ ์๋ฏธ)Layer ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋ path ๊ฐ์ ์ ์ฅํ์ง ์๋๋ค๋ ์ ์ด๋ค. path๋ ์ค์ ์์ฒญ์ด ๋ค์ด์์ ๋, ํด๋น Layer๊ฐ์ฒด์ match()๊ฐ ํธ์ถ๋ ํ, ๋์ ์ผ๋ก ๋งค์น๋(ํด๋น Layer๊ฐ ์ฒ๋ฆฌํ) ๋ถ๋ถ ๊ฒฝ๋ก(match[0])๊ฐ ์ ์ฅ๋๋ค. ์ด ๊ฐ์ ์ค์ฒฉ๋ ๊ฐ์ฒด์ ์ํด ๊ณ์ ๋ณ๊ฒฝ๋ ์ ์๋ค. (์์ธํ ๋ด์ฉ์ ์๋ ์ฐธ๊ณ )params : ๋์ ์ผ๋ก ์ถ์ถ๋ ๋ผ์ฐํธ ํ๋ผ๋ฏธํฐ ์ ๋ณด (/user/:id์ผ ๋์ id)path์ ๋ง์ฐฌ๊ฐ์ง๋ก Layer๊ฐ์ฒด์ match()๊ฐ ํธ์ถ๋ ํ ์ ์ฅ๋๋ค.route: ์ค์ ๋ผ์ฐํธ ๊ฐ์ฒด (๋ผ์ฐํฐ๊ฐ ์๋ ์ผ๋ฐ ๋ฏธ๋ค์จ์ด์ผ ๊ฒฝ์ฐ undefined)match(path): ์ฃผ์ด์ง path๊ฐ ์ด Layer์ path์ ์ผ์นํ๋์ง ์ ๊ทํํ์์ผ๋ก ๊ฒ์ฌhandle_request(req, res, next)Layer๊ฐ ์ค์ ๋ก ํด๋น ๊ฒฝ๋ก์ ๋งค์นญ๋๋ฉด ์คํ๋จhandle(req, res, next)์ ํธ์ถํ ๋ฟ์ด์ง๋ง, try-catch๋ก ๊ฐ์ธ์ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ์ํด ๋ฐ๋ก ๋ถ๋ฆฌํด ๋ ๊ฒ!handle์ ๋งค๊ฐ๋ณ์๊ฐ 4๊ฐ ์ด์์ด๋ฉด ํ์ค request handler๊ฐ ์๋๋ฏ๋ก next() ์คํํ์ฌ ํจ์คhandle_error(err, req, res, next): ์๋ฌ ๋ฏธ๋ค์จ์ด์ผ ๊ฒฝ์ฐ ์คํhandle(*error*, *req*, *res*, *next*) ํจ์ ํธ์ถhandle_request(req, res, next) ์ ๋ง์ฐฌ๊ฐ์ง๋ก try-catch๋ก ๊ฐ์ธ์ ธ์๋ค.handle์ ๋งค๊ฐ๋ณ์๊ฐ 4๊ฐ๊ฐ ์๋๋ฉด ํ์ค error handler๊ฐ ์๋๋ฏ๋ก next() ์คํํ์ฌ ํจ์คnext() ํจ์๋?๊ฐ ๋ฏธ๋ค์จ์ด์์ next()๋ฅผ ํธ์ถํ๋ฉด, ๋ค์ Layer(๋ฏธ๋ค์จ์ด ๋๋ ๋ผ์ฐํฐ)๋ก ์์ฒญ ์ฒ๋ฆฌ๊ฐ ๋์ด๊ฐ๋ค.
์ด next๋ handle ํจ์ ๋ด๋ถ์์ ์์ฑ๋ ํด๋ก์ ํจ์๋ก, ๋ ์์ปฌ ํ๊ฒฝ ์์ ์๋ stack ๋ฐฐ์ด(๋ชจ๋ Layer์ ๋ชฉ๋ก)์ ์ ๊ทผํ ์ ์๋ค. ์ด๋ฅผ ํตํด ํ์ฌ ์์ฒญ์ ์๋ง์ ๋ค์ Layer๋ฅผ ์ฐพ์ ์คํํ๋ค.
handle_request, handle_error ์ try-catch ๋ฌธ์ ํตํด ์กํ ์์ธ๋ next()๋ฅผ ํตํด ์ ๋ฌ๋๋ค.
next์ ์์ธ๊ฐ ์ ๋ฌ๋๋ฉด, Express๋ ํ์ฌ ์คํ(Layer ๋ฐฐ์ด)์ ์ํํ๋ฉด์ ํจ์์ ์ธ์๊ฐ (err, req, res, next)์ฒ๋ผ 4๊ฐ์ด๋ฉด, ์๋ฌ ์ฒ๋ฆฌ ๋ฏธ๋ค์จ์ด๋ก ํ๋จํ๊ณ ์คํํ๋ค.
next() ํจ์์ ํต์ฌ ๋ก์ง์ ์๋์ ๊ฐ๋ค.
next(err) ํํ๋ก ํธ์ถ๋๋ฉด ์๋ฌ ์ฒ๋ฆฌ ๋ฏธ๋ค์จ์ด๋ก ํ๋ฆ์ ๋๊น.err === 'router'์ด๋ฉด ํ์ฌ Router๋ฅผ ๋น ์ ธ๋๊ฐ๊ณ ์์๋ก ํ๋ฆ ์ด๋.idx >= stack.length: ๋ชจ๋ ๋ฏธ๋ค์จ์ด/๋ผ์ฐํธ๋ฅผ ์ํํ ๊ฒฝ์ฐ.done(layerError)๋ฅผ ํธ์ถํด ๋ผ์ฐํฐ ์ข
๋ฃ. (done์ Router๊ฐ ์์ฒญ ์ฒ๋ฆฌ๋ฅผ ๋๋์์ Express์ ์๋ฆฌ๋ ์ฝ๋ฐฑ.)sync > 100์ด๋ฉด ๋๊ธฐ ํธ์ถ์ด ๊ณผ๋ํ ์ํ.setImmediate(next, err)๋ก ๋ค์ ์ด๋ฒคํธ ๋ฃจํ์์ ์คํ๋๋๋ก ์ฒ๋ฆฌ.setImmediate(next, err)๋ก Stack overflow๋ฅผ ๋ฐฉ์งํ๋ค๊ณ ?Express๋ next()๊ฐ ์ฌ๊ท์ ์ผ๋ก ๊ณ์ ํธ์ถ๋ ์ ์๋ ๊ตฌ์กฐ์ด๋ค. ์๋ง์ ๋ฏธ๋ค์จ์ด๊ฐ next()๋ง ํธ์ถํ๊ณ ์ค์ ์๋ต์ ํ์ง ์์ผ๋ฉด, ๋๊ธฐ์ ์ผ๋ก ๋์์ด ํธ์ถ๋ ์๋ ์๋ค. ์ด๋ฐ ๋๊ธฐ ํธ์ถ์ด ๋๋ฌด ๋ง์ด ์์ด๋ฉด ์ฝ ์คํ์ด ์ด๊ณผ(stack overflow) ๋ ์ํ์ด ์๊ธด๋ค.
๋ฐ๋ผ์ Express๋ sync > 100์ธ ๊ฒฝ์ฐ, ๋ค์ ๋ฏธ๋ค์จ์ด๋ก ๊ฐ์ง ์๊ณ ์๋์ ๊ฐ์ด ๋น๋๊ธฐ๋ก ๋๊ฒจ๋ฒ๋ฆฐ๋ค.
if (++sync > 100) {
return setImmediate(next, err);
}
์ด๋ ๊ฒ ํ๋ฉด ์ด๋ฒคํธ ๋ฃจํ๊ฐ ์ ์ ์ฌ์๋ค๊ฐ ๋ค์ next()๋ฅผ ํธ์ถํ๋ฏ๋ก, ์ฝ ์คํ์ด ๋ฆฌ์
๋๊ณ ์์ ํ๊ฒ ์งํํ ์ ์๋ค. ์ฆ, ๋ค์ ์ด๋ฒคํธ ๋ฃจํ ์ฌ์ดํด์์ ๋ค์ ์คํํ๋๋ก ๋น๋๊ธฐ๋ก ๋๊ฒจ๋ฒ๋ฆฌ๋ ๊ฒ์ด๋ค.
Layer.path๋ฅผ ๋ฏธ๋ฆฌ ์ ์ฅํ์ง ์๊ณ , ์์ฒญ๋ง๋ค match() ๊ฒฐ๊ณผ๋ก ๋์ ์ผ๋ก ์ค์ ํ ๊น?๊ทธ ์ด์ ๋ ์์ฒญ๋ง๋ค ๋งค์นญ๋๋ ๊ฒฝ๋ก๊ฐ ๋ฌ๋ผ์ง ์ ์๊ธฐ ๋๋ฌธ!
Express๋ app.use()๋ router.get() ๊ฐ์ ํธ๋ค๋ฌ๋ฅผ ๋ชจ๋ ๋ด๋ถ์ ์ผ๋ก Layer ๊ฐ์ฒด๋ก ๊ฐ์ธ์ ์ฒ๋ฆฌํด. ์ด๋ ๊ฐ ์์ฒญ๋ง๋ค ๋งค์นญ๋ ์ค์ ๊ฒฝ๋ก(path)์ params๊ฐ ๋ฌ๋ผ์ง ์ ์๊ธฐ ๋๋ฌธ์, path๋ฅผ ๊ณ ์ ํ์ง ์๊ณ match() ์คํ ๊ฒฐ๊ณผ๋ก ๋์ ์ผ๋ก ์ค์ ํ๋ ๊ฒ.
์๋ฅผ ๋ค์ด, ์๋์ฒ๋ผ ๋ผ์ฐํฐ๋ฅผ ๊ตฌ์ฑํ๋ค๊ณ ํด๋ณด์.
const userRouter = express.Router();
userRouter.get('/:id', (req, res) => {
res.send(`User ID is ${req.params.id}`);
});
app.use('/api/users', userRouter);
ํด๋ผ์ด์ธํธ๊ฐ /api/users/1 ์์ฒญ์ ๋ณด๋์ ๋,
/api/users๋ฅผ ๋งค์นญํ๊ณ ,userRouter์ Layer๋ /:id๋ฅผ ๋งค์นญํ๋ค.์ด๋ path๋ ๋ค์๊ณผ ๊ฐ์ด ๋งค ์์ฒญ๋ง๋ค ๋ค๋ฅด๊ฒ ์ ์ฅ๋๋ค.
path = /api/userspath = /1โฌ๏ธย ์๋์ ์๋ ์ฝ๋๊ฐ ๋์ ์ผ๋ก path๊ฐ ๋ณ๊ฒฝ๋๋ ์ฝ๋์ด๋ค. (express/lib/router/index.js)
// /api/users/1 ์์ฒญ์ ๊ฒฝ์ฐ layerPath๋ /api/users/, path๋ /api/users/3!
function trim_prefix(layer, layerError, layerPath, path) {
// ํ๋ฆ ํ์ธ์ฉ
console.log("layerPath : " + layerPath + ", path : " + path);
// ๊ฒฝ๋ก ์ฒดํฌ(layerPath๋ฅผ ์ ๊ทํํ์์ผ๋ก ๋งค์นญ์ํค๊ธฐ ๋๋ฌธ์ ์ ํํ ์ผ์นํ์ง ์์ ์ ์์)
// ๋ฐ๋ผ์ path๊ฐ layerPath๋ก ์์ํ๋์ง ํ์ธ
if (layerPath !== path.slice(0, layerPath.length)) {
next(layerError)
return
}
...
removed = layerPath;
req.url = protohost + req.url.slice(protohost.length + removed.length)
...
}
์ถ๋ ฅ์ ์๋์ ๊ฐ๋ค.(๋งค์นญ๋ ๊ฒฝ์ฐ๋ง ๊ฐ์ ธ์ด) ์ ๋ง๋ก path๊ฐ ๋์ ์ผ๋ก ๋ณํ๋ค!
layerPath : /api/tasks, path : /api/tasks/3
layerPath : , path : /3
| ๋จ๊ณ | Layer | layerPath | req.url | path (๋์ ๋งค์นญ๋ ๋ถ๋ถ) |
|---|---|---|---|---|
| 1๏ธโฃ | app.use('/api/tasks/') | /api/tasks/ | /api/tasks/3 | /api/tasks/ |
| 2๏ธโฃ | taskRouter.get('/:id') | /:id | /123 | /123 |
โจ ๊ทธ๋ผ ์ path ๊ฐ์ ๊ทธ๋๋ง๋ค ์ ์ฅํด๋๊น?
Layer.path๊ฐ ํ์ํ๋ค. ๋ฐ๋ผ์, ๊ฐ Layer๋ง๋ค ์ค์ ๋ก ๋งค์นญ๋ path ๊ฐ์ ๋์ ์ผ๋ก ๊ณ์ฐํด์ ์ ์ฅํด๋๋ค.const express = require('express');
const app = express(); // Application ๊ฐ์ฒด ์์ฑ_router์ ์์ํด์ ์ฒ๋ฆฌํ๊ฒ ํ๋ค.// express/lib/express.js
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next); // ์์ฒญ ๋ค์ด์ค๋ฉด app.handle๋ก ์ฒ๋ฆฌ
};
mixin(app, EventEmitter.prototype, false); // ์ด๋ฒคํธ ์ฒ๋ฆฌ mixin
mixin(app, proto, false); // app.use, app.get, app.listen ๋ฑ mixin
// request, response ๊ฐ์ฒด ๊ตฌ์ฑ
// expose the prototype that will get set on requests
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// expose the prototype that will get set on responses
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// ๋ด๋ถ์ ์ผ๋ก _router ์์ฑ ํ ๊ธฐ๋ณธ ๋ฏธ๋ค์จ์ด(query, expressInit) ๋ฏธ๋ฆฌ ๋ฑ๋ก
// ์? ๋ฏธ๋ค์จ์ด์ ๋ผ์ฐํฐ๋ฅผ ์ ์ฅํ ๊ณต๊ฐ์ด ํ์ํ๋ฏ๋ก !
// ์ฐ๋ฆฌ๊ฐ ๋ฏธ๋ค์จ์ด์ ๋ผ์ฐํฐ๋ฅผ ๋ฑ๋กํ ๋ app._router๊ฐ ์์ผ๋ฉด ๋ฑ๋ก์ด ์๋๋ค.
app.init();
return app;
}next()๋ฅผ ํธ์ถํ ์ ์๋ค.(req, res, next) => {} ํํ์ด๋ค.(err, req, res, next) => {} ํํ์ด๋ค.app.use((req, res, next) => {
console.log("๋๋ ๋ฏธ๋ค์จ์ด!");
next();
});Layer๋ก ๊ฐ์ธ์ง MiddleWare๋ ์๋์ ๊ฐ๋ค.{
name: '<anonymous>',
path: undefined,
route: undefined, // ๋ฏธ๋ค์จ์ด์ด๊ธฐ ๋๋ฌธ์ route๋ ์์
handle: [Function (anonymous)], // <- (req, res, next) => {...}
regexp: /^\/?(?=\/|$)/i { fast_star: false, fast_slash: true },
handleType: 'function',
}stack์ ๊ฐ์ง๋ค. (router.stack)use๋ก MiddleWare๋ฅผ, get, post ๋ฑ์ผ๋ก Route๋ฅผ ๋ฑ๋กํ๋ค.router.stack)์ ์ํํ๋ฉด์ ์คํํ๋ค.const taskRouter = express.Router();
taskRouter.get('/:id', (req, res, next) => {
console.log("๋๋ taskRouter!");
next();
});
app.use('/api/tasks', taskRouter);Layer๋ก ๊ฐ์ธ์ง Router๋ ์๋์ ๊ฐ๋ค.{
name: 'router',
handle: [Function: router], // ์ด๊ฒ express.Router()์ ๋ฐํ๊ฐ
stack: [ [Layer] ] // taskRouter ๋ด๋ถ ๋ผ์ฐํธ๊ฐ ๋ค์ Layer๋ก ๊ฐ์ธ์ง
}dispatch() : ์ค์ ๋ผ์ฐํธ(Route)์ ๋ฑ๋ก๋ ํธ๋ค๋ฌ๋ฅผ ์คํtaskRouter.get('/:id', (req, res, next) => {
console.log("๋๋ taskRouter!");
next();
});Layer๋ก ๊ฐ์ธ์ง Route๋ ์๋์ ๊ฐ๋ค.{
name: 'bound dispatch',
route: { path: '/:id', methods: { get: true } },
handle: [Function: bound dispatch], // ๋ผ์ฐํธ์ ๋ฑ๋กํ ํธ๋ค๋ฌ
regexp: /^\/(?:([^\/]+?))\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
}๐
bound dispatch๋?
route.dispatch.bind(route)๋ฅผ ํตํด ๋ง๋ค์ด์ง ๋ผ์ฐํธ ์์ฒญ ์ฒ๋ฆฌ ํจ์
dispatchํจ์ ๋ด๋ถ์์this.stack,this.methods๋ฑ์ ์ฐธ์กฐํด์ผ ํ๋ฏ๋ก,route์ธ์คํด์ค๋ฅผ context๋ก ์ ์งํ๊ธฐ ์ํด ๋ฐ์ธ๋ฉํด์ผ ํ๋ค.Layer๊ฐ์ฒด์handle์ ์ ์ฅ๋๋ค.- JavaScript์์๋
Function.prototype.bind(...)๋ก ๋ฐ์ธ๋ฉํ๋ฉด ํจ์ ์ด๋ฆ์ด"bound originalName"ํํ๊ฐ ๋๋ค.
์๋ ์์๋ฅผ ํตํด ๋์ ๊ณผ์ ์ ์ดํดํด๋ณด์.
// app.js
import express from "express";
const taskRouter = express.Router();
taskRouter.get("/", (req, res) => {
console.log("GET");
});
taskRouter.get("/:id", (req, res) => {
console.log("id get");
});
taskRouter.post("/", (req, res) => {
console.log("post");
});
taskRouter.put("/:id", (req, res) => {
console.log("put");
});
taskRouter.patch("/:id", (req, res) => {
console.log("patch");
});
taskRouter.delete("/:id", (req, res) => {
console.log("delete");
});
const app = express();
app.use('/api/tasks', taskRouter);
GET /api/tasks/3 ์์ฒญapp._router.stack ์ํ (์ต์์ Layer ํ์)app.handle(req, res) ์ด ์คํ๋๋ค.app._router.handle(req, res) ํธ์ถ โ Router.prototype.handle()์ ํธ์ถํ๋ค.handle()์์ app._router.stack ๋ฐฐ์ด์ ์ํํ๋ฉด์ ๊ฐ Layer์ ๋ํด layer.match(req.path)๋ฅผ ํธ์ถํ๋ค. ์ด ๋ฐฐ์ด์๋ ์๋์ ๊ฐ์ด ๋ชจ๋ ๋ฏธ๋ค์จ์ด์ ๋ผ์ฐํฐ๊ฐ Layer ํํ๋ก ๋ค์ด๊ฐ ์๋ค. ๋ฐฐ์ด์ ๋ง์ง๋ง์ ์ฐ๋ฆฌ๊ฐ ๋ฑ๋กํ taskRouter๊ฐ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.layer.match(req.path)๊ฐ True๋ฅผ ๋ฐํํ์ฌ ๋งค์นญ๋๋ฉด, ํด๋น Layer์ layer.handle_request(req, res, next) ๋ฅผ ํธ์ถํ๋ค.req.path๋ /api/tasks/3์ด๋ฉฐ, ์ฐ๋ฆฌ๊ฐ app.use('/api/tasks', taskRouter)๋ก ๋ฑ๋กํ ๋ผ์ฐํฐ Layer๋ ์ ๊ท์ /^\/api\/tasks\/?(?=\/|$)/i์ ๋งค์นญ๋๋ค.๐ ํธ์ถ๋๋ layer.handle_request ๋ด๋ถ
Layer.prototype.handle_request = function handle(req, res, next) {
const fn = this.handle;
// ์ผ๋ฐ ํจ์์ผ ๊ฒฝ์ฐ ์คํ (middleware or router)
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
fn ์ด taskRouter์ด๊ธฐ ๋๋ฌธ์ taskRouter.handle(req, res, next)๊ฐ ์คํ๋๋ค!/api/tasks)๋ ์๋ผ์ง๊ณ , req.url์ /3์ผ๋ก ๋ณ๊ฒฝ๋์ด ํ์ ๋ผ์ฐํฐ์ ์ ๋ฌ๋๋ค.โ๏ธย ์๋๋ย app._router.stack์ ๋ํ ์ดํด๋ฅผ ์ํด ๋ด๋ถ๋ฅผ ์ฝ์๋ก ์ถ๋ ฅํ ๊ฒฐ๊ณผ์ด๋ค.
{
name: 'query',
path: undefined,
route: undefined,
handle: [Function: query],
regexp: /^\/?(?=\/|$)/i { fast_star: false, fast_slash: true },
handleType: 'function',
},
{
name: 'expressInit',
path: undefined,
route: undefined,
handle: [Function: expressInit],
regexp: /^\/?(?=\/|$)/i { fast_star: false, fast_slash: true },
handleType: 'function',
},
{
name: 'router', // ์ฐ๋ฆฌ๊ฐ ๋ฑ๋กํ taskRouter!
path: undefined,
route: undefined,
handle: [Function: router] {
params: {},
_params: [],
caseSensitive: undefined,
mergeParams: undefined,
strict: undefined,
stack: [ [Layer], [Layer], [Layer], [Layer], [Layer], [Layer] ]
},
regexp: /^\/api\/tasks\/?(?=\/|$)/i { fast_star: false, fast_slash: false },
handleType: 'function',
}
query์ expressInit ๋ฏธ๋ค์จ์ด๋ ์ ๋ค์ด๊ฐ์์๊น?
์ฌ๊ธฐ์
query์expressInit๋ฏธ๋ค์จ์ด๋ Express๊ฐ ์๋์ผ๋ก ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ณธ ๋ฏธ๋ค์จ์ด๋ค์ด๋ค.
query๋ฏธ๋ค์จ์ด (express/lib/middleware/query.js)
- ์์ฒญ URL์ ๋ถ์ QueryString์ ํ์ฑํด์
req.query๊ฐ์ฒด๋ก ๋ง๋ค์ด์ฃผ๋ ๋ฏธ๋ค์จ์ดexpressInit๋ฏธ๋ค์จ์ด (express/lib/middleware/init.js)
- Express๊ฐ ์ด๊ธฐํ๋ ๋,
req์res๊ฐ์ฒด์ ๊ธฐ๋ณธ ํ๋กํผํฐ(res,req,next)๋ฅผ ์ค์ ํด์ฃผ๋ ๋ฏธ๋ค์จ์ด
express/lib/application.js๋ฅผ ๋ณด๋ฉด ์ฐ๋ฆฌ๊ฐexpress()๋ฅผ ํธ์ถํ ๋ ๋ด๋ถ์ ์ผ๋กapp._router๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ , ๊ทธ ์์queryโexpressInit์์๋ก ๋ฏธ๋ค์จ์ด๊ฐ push๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค!app.lazyrouter = function lazyrouter() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') }); this._router.use(query(this.get('query parser fn'))); this._router.use(middleware.init(this)); } };
taskRouter.stack ์ํ (ํ์ ๋ผ์ฐํฐ ํ์)taskRouter.handle(req, res) ๋ด๋ถ์์ ๋ค์ Router.prototype.handle() ํธ์ถ๋๋ค.taskRouter.stack์ ์๋ ๊ฐ Layer์ ๋ํด ๋ค์ layer.match(req.path)๊ฐ ํธ์ถ๋๋ค.req.path๋ /3์ด๊ธฐ ๋๋ฌธ์, /:id์ธ ๋ผ์ฐํธ๊ฐ ๋งค์นญ๋๋ค.layer.handle_request(req, res, next)๊ฐ ํธ์ถ๋๋ค.๐ ์ฌ๊ธฐ์ ํธ์ถ๋๋ handle_request
์ด Layer๋ ๋ผ์ฐํฐ ๋ ๋ฒจ์ด ์๋ ๋ผ์ฐํธ์ ํด๋นํ๊ธฐ ๋๋ฌธ์, this.handle์ ๋ด๋ถ์ ์ผ๋ก route.dispatch๋ก ์ค์ ๋์ด ์๋ค. ๋ฐ๋ผ์, layer.handle_request(req, res, next) ์ ์ํด route.dispatch(req, res, next) ์คํ๋๋ค.
layer.handle = route.dispatch.bind(route)
โ๏ธย ์๋๋ย taskRouter.stack์ ๋ํ ์ดํด๋ฅผ ์ํด ๋ด๋ถ๋ฅผ ์ฝ์๋ก ์ถ๋ ฅํ ๊ฒฐ๊ณผ์ด๋ค.
{
name: 'bound dispatch',
route: { path: '/', methods: { get: true } },
handle: [Function: bound dispatch],
regexp: /^\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
methods: { get: true }
},
{
name: 'bound dispatch',
route: { path: '/:id', methods: { get: true } },
handle: [Function: bound dispatch],
regexp: /^\/(?:([^\/]+?))\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
methods: { get: true }
},
{
name: 'bound dispatch',
route: { path: '/', methods: { post: true } },
handle: [Function: bound dispatch],
regexp: /^\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
methods: { post: true }
},
{
name: 'bound dispatch',
route: { path: '/:id', methods: { put: true } },
handle: [Function: bound dispatch],
regexp: /^\/(?:([^\/]+?))\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
methods: { put: true }
},
{
name: 'bound dispatch',
route: { path: '/:id', methods: { patch: true } },
handle: [Function: bound dispatch],
regexp: /^\/(?:([^\/]+?))\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
methods: { patch: true }
},
{
name: 'bound dispatch',
route: { path: '/:id', methods: { delete: true } },
handle: [Function: bound dispatch],
regexp: /^\/(?:([^\/]+?))\/?$/i { fast_star: false, fast_slash: false },
params: undefined,
methods: { delete: true }
}
Route.prototype.dispatch ์คํRoute.prototype.dispatch = function dispatch(req, res, done) {
const method = req.method.toLowerCase();
const stack = this.stack;
// GET, POST, ๋ฑ ํด๋น ๋ฉ์๋์ ๋ฑ๋ก๋ ํธ๋ค๋ฌ ์คํ
for (let layer of stack) {
if (layer.method === method) {
layer.handle_request(req, res, next);
}
}
};
dispatch() ๋ด๋ถ์์ ๋ฑ๋ก๋ ((req, res) => { console.log("id get"); })์ Layer์ handle_request() ๋ฅผ ํธ์ถํ๋ค. ์ ๋์ ํ๋ฆ์ ์์ฝํ์๋ฉด ์๋์ ๊ฐ๋ค.
ํด๋ผ์ด์ธํธ โ GET /api/tasks/3
โ
app.handle(req, res)
โ
app._router.handle(req, res)
โ
app._router.stack ์ํ
โโ Layer: query
โ โโ layer.handle_request โ query(req, res, next)
โโ Layer: expressInit
โ โโ layer.handle_request โ expressInit(req, res, next)
โโ Layer: router (/api/tasks) // โ
๋งค์นญ๋จ
โ โโ layer.handle_request โ taskRouter.handle(req, res, next)
โ โโ req.url = '/3' ๋ก ๋ฐ๋
โ
taskRouter.stack ์ํ
โโ Layer: '/:id' // โ
๋งค์นญ๋จ
โ โโ layer.handle_request โ route.dispatch(req, res, next)
โ โโ method === 'get'์ธ Layer ์ฐพ์
โ โโ layer.handle_request โ ์คํ๋จ โ console.log("id get") ์ถ๋ ฅ
์ฆ,
app์ ๊ฑด๋ฌผ ์ ์ฒด (๊ฑด๋ฌผ ์
๊ตฌ)router๋ ๊ฑด๋ฌผ ๋ด๋ถ์ ์ธต (๊ฐ ์ธต๋ง๋ค ๋ผ์ฐํ
๋ด๋น)app._router.stack์ ์
๊ตฌ์์๋ถํฐ ๋ณต๋๊น์ง ๋ชจ๋ ๊ฒฝ๋กrouter.stack์ ๊ทธ ์ธต ๋ด๋ถ์ ์ด๋ค ๋ฐฉ์ด ์๋์ง ๋ฆฌ์คํธ๋ผ๊ณ ํ ์ ์๋ค.
Express๋ฅผ ํ๋ณด๋ฉด์, ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ ๋ง ์ ํ์ฉํ๋ ํ๋ ์์ํฌ๋ผ๋ ์๊ฐ์ด ๋ค์๋ค.
ํนํ, Express๋ Prototype์ ์์ฃผ ์ ํ์ฉํ๋๋ฐ, ์์๋ก req, res ๊ฐ์ฒด์ ๊ฐ์ข
๋ฉ์๋๋ฅผ ํ์ฅํ๋ ๋ฐฉ์์ด ์๋ค. Express๋ Node์ http.IncomingMessage์ http.ServerResponse๋ฅผ ๊ธฐ๋ฐ์ผ๋ก request์ response ๊ฐ์ฒด๋ฅผ ํ์ฅํ๋๋ฐ, ์ด๋ฅผ Object.create()์ ํ๋กํ ํ์
์ฒด์ธ์ ํตํด ๊ตฌํํ๋ค.
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
});
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
});
์ด ๊ตฌ์กฐ ๋๋ถ์ req๋ res ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๋ Express๊ฐ ์ ๊ณตํ๋ ๋ค์ํ ์ ํธ ๋ฉ์๋(req.params, res.json(), res.send() ๋ฑ)๋ฅผ ๋ง์น ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ ์์ฒญ๋ง๋ค ์ด ํ๋กํ ํ์
๊ฐ์ฒด๋ฅผ ์์ํ ์๋ก์ด ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ง๊ธฐ ๋๋ฌธ์, ๊ณตํต ๋ก์ง์ ๊ณต์ ํ๊ณ , ์์ฒญ๋ง๋ค ๋
๋ฆฝ์ ์ธ ์ํ๋ฅผ ์ ์งํ ์ ์๋ค.
๋๋ค๋ฅธ ์์๋ก Layer ๊ฐ์ฒด๊ฐ ์๋ค. Express๋ MiddleWare, Router, Route ๋ชจ๋ Layer๋ก ํต์ผํด์ ์ฒ๋ฆฌํ๋ ํ์ฅ์ฑ ์๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค. Layer ๋ก ๊ฐ์ธ๊ฒ ๋๋ฉด, ์์ฒญ์ด ๋ค์ด์์๋ ๋จ์ํ Layer ๋ฐฐ์ด์ ์ํํ๋ฉด์ ์ฒ๋ฆฌํ๋ฉด ๋๋ค. ๋ํ, ๊ฒฝ๋ก๋ฅผ ๋งค์นญํ๊ฑฐ๋ ์ค์ ๋ก ์์ฒญ์ ์ฒ๋ฆฌํ ํธ๋ค๋ฌ ํจ์๋ฅผ ์คํ์ํค๋ ๊ฒ๋ Layer ์์ ์ ์๋์ด ์์ผ๋ Layer ๋ก ๊ฐ์ธ์ง ๊ฐ์ฒด๋ค์ ๋จ์ํ Layer ์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ๋๋ค.
Layer ๊ฐ์ฒด๋ ๋ค์๊ณผ ๊ฐ์ ๋ฉ์๋๋ฅผ ๊ฐ๋๋ค.
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
์ด์ฒ๋ผ Express๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ํ๋กํ ํ์ ์ฒด์ธ์ ์ ๊ทน์ ์ผ๋ก ํ์ฉํ์ฌ
res ๊ฐ์ฒด๊ฐ ์์ฑ๋์ง๋ง, ์ด๋ค์ ๋ชจ๋ ๊ฐ์ response prototype์ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ send, json ๊ฐ์ ๋ฉ์๋๋ ๊ณตํต์ผ๋ก ์ฌ์ฌ์ฉ๋๋ค.req.params, req.query, req.body ๋ฑ์ด ๋ค๋ฅด๋ค! ๊ฐ ์์ฒญ๋ง๋ค ์๋ก์ด req ๊ฐ์ฒด๊ฐ ์์ฑ๋์ง๋ง, ๊ณตํต ๊ธฐ๋ฅ์ ํ๋กํ ํ์
์ผ๋ก ๊ณต์ ํ๊ณ ๊ฐ ์์ฒญ์ ํ์ํ ๋ฐ์ดํฐ๋ง ๋ฐ๋ก ์ถ๊ฐํ๋ค.๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๋ฅผ ๋ง๊ณ ์ฌ์ฌ์ฉ์ฑ๊ณผ ํ์ฅ์ฑ์ ๋์ด๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์๋ค.