๐Ÿช„ Express์˜ ํ•ต์‹ฌ ์š”์†Œ์™€ ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ตฌ์กฐ์— ๋Œ€ํ•ด์„œ

์ตœํ˜ธ๋นˆยท2025๋…„ 4์›” 23์ผ
1
post-thumbnail

โ“ Express๋ž€?

  • Node.js ํ™˜๊ฒฝ์—์„œ ๋™์ž‘ํ•˜๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ
  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์„ฑ์— ํ•„์š”ํ•œ Routing, View Helper, Session(์˜์†์  Session๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ๋Š”ย Redis๋“ฑ์˜ Data store๊ฐ€ ํ•„์š”ํ•˜๋‹ค)๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ž€?

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ๋“ค์„ ๋ฏธ๋ฆฌ ๊ตฌ์กฐํ™”ํ•˜์—ฌ ์ œ๊ณตํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด ํ”„๋ ˆ์ž„์›Œํฌ
์ฆ‰, ์›น ์„œ๋น„์Šค๋ฅผ ๋” ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ฐœ๋ฐœ ๋„๊ตฌ ์„ธํŠธ์ธ ๊ฒƒ.




๐Ÿ’ก Express์˜ ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ํ•ต์‹ฌ ์š”์†Œ๋“ค

1๏ธโƒฃ Layer

  • Express ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” Wrapper ๊ฐ์ฒด
  • MiddleWare, Router, Route ๋ชจ๋‘ Layer ๊ฐ์ฒด๋กœ ๊ฐ์‹ธ์ ธ์„œ ๊ด€๋ฆฌ๋œ๋‹ค.
    • ๊ทธ๋ž˜์„œ Layer๋Š” ์ตœ์†Œํ•œ์˜ ๊ณตํ†ต ๊ตฌ์กฐ๋งŒ ์ •์˜ํ•˜๊ณ , ์ƒํ™ฉ์— ๋”ฐ๋ผ ์†์„ฑ์„ ์œ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค.
  • express/lib/router/layer.js ํŒŒ์ผ์—์„œ ์ •์˜
  • ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ
    • name: ํ•จ์ˆ˜ ์ด๋ฆ„
      • MiddleWare, Router, Route๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•œ ์‹๋ณ„์ž
      • handle ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์ธ ํ•จ์ˆ˜์˜ ์ด๋ฆ„์ด๋‹ค.
      • Ex) 'router', 'bound dispatch', session', '' โ€ฆ
    • handle: ์‹ค์ œ๋กœ ์šฐ๋ฆฌ๊ฐ€ ๋“ฑ๋กํ•œ MiddleWare, Router, Route
    • regexp: ๊ฒฝ๋กœ๋ฅผ ์ •๊ทœํ‘œํ˜„์‹์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฐ’
      • MiddleWare, Router๋Š” ์‹œ์ž‘ ๋ถ€๋ถ„๋ถ€ํ„ฐ ์ •๊ทœ์‹ ๊ฒ€์‚ฌ
      • Route๋Š” ์‹œ์ž‘ ~ ๋๊นŒ์ง€ ์ •๊ทœ์‹ ๊ฒ€์‚ฌ
    • 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๋ฅผ ๋น ์ ธ๋‚˜๊ฐ€๊ณ  ์ƒ์œ„๋กœ ํ๋ฆ„ ์ด๋™.
  • ๋” ์ด์ƒ ์‹คํ–‰ํ•  Layer๊ฐ€ ์—†์„ ๋•Œ
    • idx >= stack.length: ๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด/๋ผ์šฐํŠธ๋ฅผ ์ˆœํšŒํ•œ ๊ฒฝ์šฐ.
    • done(layerError)๋ฅผ ํ˜ธ์ถœํ•ด ๋ผ์šฐํ„ฐ ์ข…๋ฃŒ. (done์€ Router๊ฐ€ ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ๋๋ƒˆ์Œ์„ Express์— ์•Œ๋ฆฌ๋Š” ์ฝœ๋ฐฑ.)
  • Stack overflow ๋ฐฉ์ง€
    • 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 ์š”์ฒญ์„ ๋ณด๋ƒˆ์„ ๋•Œ,

  • ์ฒซ ๋ฒˆ์งธ Layer๋Š” /api/users๋ฅผ ๋งค์นญํ•˜๊ณ ,
  • ๋‚ด๋ถ€ userRouter์˜ Layer๋Š” /:id๋ฅผ ๋งค์นญํ•œ๋‹ค.

์ด๋•Œ path๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งค ์š”์ฒญ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์ €์žฅ๋œ๋‹ค.

  • ์™ธ๋ถ€ Layer: path = /api/users
  • ๋‚ด๋ถ€ Layer: path = /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

ํ‘œ๋กœ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
๋‹จ๊ณ„LayerlayerPathreq.urlpath (๋™์  ๋งค์นญ๋œ ๋ถ€๋ถ„)
1๏ธโƒฃapp.use('/api/tasks/')/api/tasks//api/tasks/3/api/tasks/
2๏ธโƒฃtaskRouter.get('/:id')/:id/123/123

โœจ ๊ทธ๋Ÿผ ์™œ path ๊ฐ’์„ ๊ทธ๋•Œ๋งˆ๋‹ค ์ €์žฅํ•ด๋‘˜๊นŒ?

  • ์ด ๊ฐ’์ด ์žˆ์œผ๋ฉด, ๋‹ค์Œ Layer์—๊ฒŒ ์š”์ฒญ์„ ๋„˜๊ธธ ๋•Œ ๊ธฐ์ค€์ ์ด ๋œ๋‹ค. (์œ„ ์˜ˆ์‹œ ์ฐธ๊ณ )
  • ์ค‘์ฒฉ๋œ Router ๊ตฌ์กฐ์—์„œ๋Š” ํ˜„์žฌ Layer์—์„œ ์ฒ˜๋ฆฌํ•œ path๋ฅผ ์ œ์™ธํ•œ ๋‚จ์€ path๋งŒ ๋‹ค์Œ Router์— ๋„˜๊ฒจ์ค˜์•ผ ํ•˜๋Š”๋ฐ, ์ด๋•Œ Layer.path๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋”ฐ๋ผ์„œ, ๊ฐ Layer๋งˆ๋‹ค ์‹ค์ œ๋กœ ๋งค์นญ๋œ path ๊ฐ’์„ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐํ•ด์„œ ์ €์žฅํ•ด๋‘”๋‹ค.



2๏ธโƒฃย Application

  • Express ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์˜๋ฏธํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค.
    const express = require('express');
    const app = express(); // Application ๊ฐ์ฒด ์ƒ์„ฑ
  • ์„œ๋ฒ„ Listen, ๋ฏธ๋“ค์›จ์–ด ๋“ฑ๋ก, ๋ผ์šฐํ„ฐ ์„ค์ • ๋“ฑ์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • ์š”์ฒญ์„ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜์ง€๋Š” ์•Š๊ณ , ์š”์ฒญ์ด ์˜ค๋ฉด _router์— ์œ„์ž„ํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ•œ๋‹ค.
  • Application ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๋กœ์ง์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
    // 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;
    }



3๏ธโƒฃย MiddleWare

  • Express์—์„œ Middleware๋Š” ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋‹ค. ์š”์ฒญ-์‘๋‹ต ํ๋ฆ„ ์ค‘๊ฐ„์—์„œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค. ์ด ํ•จ์ˆ˜๋“ค์€ ํŠน์ • ๊ฒฝ๋กœ์—์„œ ์‹คํ–‰๋˜๋„๋ก ๋“ฑ๋ก๋˜๋ฉฐ, ๋‹ค์Œ ์ž‘์—…์œผ๋กœ ๋„˜๊ธฐ๊ธฐ ์œ„ํ•ด 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',
    }



4๏ธโƒฃ Router

  • ๊ฒฝ๋กœ์™€ ๋ฉ”์†Œ๋“œ์— ๋”ฐ๋ผ ์š”์ฒญ์„ ๋‚˜๋ˆ„์–ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ์ฒด
  • Router ์ž์ฒด๋„ ํ•˜๋‚˜์˜ ๋ฏธ๋“ค์›จ์–ด๋กœ ๋“ฑ๋ก๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์ž์ฒด์ ์œผ๋กœ 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๋กœ ๊ฐ์‹ธ์ง
    }



5๏ธโƒฃย Route

  • HTTP ๋ฉ”์†Œ๋“œ(GET, POST ๋“ฑ)์— ๋”ฐ๋ผ ๊ฐ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ด€๋ฆฌ
  • ์ฃผ์š” ๋ฉ”์„œ๋“œ
    • dispatch() : ์‹ค์ œ ๋ผ์šฐํŠธ(Route)์— ๋“ฑ๋ก๋œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰
  • ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•ํƒœ์ด๋‹ค. ์œ„์˜ Router ์˜ˆ์ œ์—์„œ Router์˜ stack์•ˆ์— Layerํ˜•ํƒœ๋กœ ๋“ค์–ด์žˆ๋‹ค.
    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" ํ˜•ํƒœ๊ฐ€ ๋œ๋‹ค.





๐Ÿƒ๐Ÿปโ€โ™€๏ธ Express ๋™์ž‘ ๊ณผ์ •

์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ๋™์ž‘ ๊ณผ์ •์„ ์ดํ•ดํ•ด๋ณด์ž.

// 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 ์š”์ฒญ

1. app._router.stack ์ˆœํšŒ (์ตœ์ƒ์œ„ Layer ํƒ์ƒ‰)

  1. app.handle(req, res) ์ด ์‹คํ–‰๋œ๋‹ค.
  2. ๋‚ด๋ถ€์ ์œผ๋กœ app._router.handle(req, res) ํ˜ธ์ถœ โ†’ Router.prototype.handle()์„ ํ˜ธ์ถœํ•œ๋‹ค.
  3. ์ด handle()์—์„œ app._router.stack ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ Layer์— ๋Œ€ํ•ด layer.match(req.path)๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด ๋ฐฐ์—ด์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด์™€ ๋ผ์šฐํ„ฐ๊ฐ€ Layer ํ˜•ํƒœ๋กœ ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค. ๋ฐฐ์—ด์˜ ๋งˆ์ง€๋ง‰์— ์šฐ๋ฆฌ๊ฐ€ ๋“ฑ๋กํ•œ taskRouter๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  4. layer.match(req.path)๊ฐ€ True๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋งค์นญ๋˜๋ฉด, ํ•ด๋‹น Layer์˜ layer.handle_request(req, res, next) ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
    1. 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);
  }
};
  1. ์œ„์˜ fn ์ด taskRouter์ด๊ธฐ ๋•Œ๋ฌธ์— taskRouter.handle(req, res, next)๊ฐ€ ์‹คํ–‰๋œ๋‹ค!
  2. ๊ทธ ํ›„, ๋ผ์šฐํ„ฐ์˜ ๊ฒฝ๋กœ(/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));
  }
};



2. taskRouter.stack ์ˆœํšŒ (ํ•˜์œ„ ๋ผ์šฐํ„ฐ ํƒ์ƒ‰)

  1. taskRouter.handle(req, res) ๋‚ด๋ถ€์—์„œ ๋‹ค์‹œ Router.prototype.handle() ํ˜ธ์ถœ๋œ๋‹ค.
  2. taskRouter.stack์— ์žˆ๋Š” ๊ฐ Layer์— ๋Œ€ํ•ด ๋‹ค์‹œ layer.match(req.path)๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
    1. req.path๋Š” /3์ด๊ธฐ ๋•Œ๋ฌธ์—, /:id์ธ ๋ผ์šฐํŠธ๊ฐ€ ๋งค์นญ๋œ๋‹ค.
  3. Layer์˜ 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 }
}



3. 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);
    }
  }
};
  1. dispatch() ๋‚ด๋ถ€์—์„œ ๋“ฑ๋ก๋œ ((req, res) => { console.log("id get"); })์˜ Layer์˜ handle_request() ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  2. "id get"์ด ์ถœ๋ ฅ๋œ๋‹ค.



์ „์ฒด ํ๋ฆ„ ์š”์•ฝ

์œ„ ๋™์ž‘ ํ๋ฆ„์„ ์š”์•ฝํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

ํด๋ผ์ด์–ธํŠธ โ†’ 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๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์„ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•˜์—ฌ

  1. ๊ณตํ†ต๋œ ๊ธฐ๋Šฅ(๋ฉ”์„œ๋“œ)์„ ์žฌ์‚ฌ์šฉํ•˜๊ณ 
    • Express์—์„œ๋Š” ๊ฐ ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด res ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์ง€๋งŒ, ์ด๋“ค์€ ๋ชจ๋‘ ๊ฐ™์€ response prototype์„ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— send, json ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋Š” ๊ณตํ†ต์œผ๋กœ ์žฌ์‚ฌ์šฉ๋œ๋‹ค.
  2. ๊ฐ์ฒด๋ณ„๋กœ ํ•„์š”ํ•œ ์†์„ฑ๋งŒ ๋™์ ์œผ๋กœ ๋ถ€์—ฌํ•˜์—ฌ
    • ์š”์ฒญ๋งˆ๋‹ค req.params, req.query, req.body ๋“ฑ์ด ๋‹ค๋ฅด๋‹ค! ๊ฐ ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด req ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์ง€๋งŒ, ๊ณตํ†ต ๊ธฐ๋Šฅ์€ ํ”„๋กœํ† ํƒ€์ž…์œผ๋กœ ๊ณต์œ ํ•˜๊ณ  ๊ฐ ์š”์ฒญ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋”ฐ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๋ฅผ ๋ง‰๊ณ  ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋†’์ด๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€