Express ์‹œ์ž‘ํ•˜๊ธฐ

์ต์Šคํ”„๋ ˆ์Šค(Express)๋Š” Node.js๋ฅผ ์œ„ํ•œ ์›นํ”„๋ ˆ์ž„์›Œํฌ์˜ ํ•˜๋‚˜๋กœ, ์›น์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, API ๊ฐœ๋ฐœ์„ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ๋‹ค. ์ˆ˜๋งŽ์€ HTTP์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์†Œ๋“œ์™€ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐ•๋ ฅํ•œ API๋ฅผ ๋งŒ๋“ค์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‚ฌ์‹ค์ƒ Node.js์˜ ํ‘œ์ค€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋ถˆ๋ฆฌ๊ณ  ์žˆ๋‹ค.


Express๋Š” ์–ด๋– ํ•œ ํ˜•ํƒœ๋ฅผ ๊ฐ•์š”ํ•˜์ง€ ์•Š๋Š”(Unopinionated) ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. ๋‚ด๋ถ€์—์„œ ์ œ์•ˆํ•˜๋Š” ํˆด๋“ค์ด ํฌํ•จ๋˜์–ด์žˆ๋Š” ๋ฃจ๋น„์˜จ๋ ˆ์ผ์ฆˆ์™€ ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ์™€๋Š” ๋‹ฌ๋ฆฌ, ์•„๋ฌด๊ฒƒ๋„ ์ •ํ•ด์ ธ์žˆ์ง€ ์•Š์•„ ์ž์œ ๋กญ๊ฒŒ ์›ํ•˜๋Š” ํˆด์„ ์ถ”๊ฐ€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋™์‹œ์— ํ”„๋ ˆ์ž„์›Œํฌ ์ž์ฒด์—์„œ ๋„์›€ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.


Express๋กœ ๊ฐ„๋‹จํ•œ ์›น์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

ํ”ํžˆ Express ๊ด€๋ จ ํŠœํ† ๋ฆฌ์–ผ์„ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜จ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ์›น์„œ๋ฒ„์ด๋‹ค. express ๋ชจ๋“ˆ์€ module.exports ๋ฅผ ํ†ตํ•ด createApplication ์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด Express ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•œ๋‹ค.


expressServer.js

const express = require('express');
const app = express(); // createApplication()

app.all('*', (req,res) => {
  // Express๊ฐ€ header์— ๋Œ€ํ•œ ๋ถ€๋ถ„์„ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.
  // ๊ฐœ๋ฐœ์ž๋Š” body๋ถ€๋ถ„๋งŒ ์‹ ๊ฒฝ์“ฐ๋ฉด๋œ๋‹ค.
  res.sendFile('./index.html');
});

app.listen(3000);

Express๋กœ ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค๋ฉด HTTP์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์— ๋Œ€ํ•ด ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ ๊ฒฝ์จ์•ผํ•˜๋Š” ๋ฒ”์œ„๊ฐ€ ์ค„์–ด๋“ ๋‹ค. Node.js ๋‚ด์žฅ ๋ชจ๋“ˆ์ธ http๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด HTTP์‘๋‹ต์˜ header ๋ถ€๋ถ„ ๊นŒ์ง€๋„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ ๊ฒฝ์จ์•ผํ•œ๋‹ค.

server.js

const http = require('http');
const server = http.createServer((req,res) => {
  // ์‹œ์ž‘์ค„์€ Node.js๊ฐ€ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.
  // header์™€ body๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ ๊ฒฝ์จ์•ผ ํ•œ๋‹ค.
  // ์ƒํƒœ์ฝ”๋“œ์™€ mime-type ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค˜์•ผํ•œ๋‹ค.
  res.writeHead(200, {
    'content-type': 'text/html'
  });
  const html = fs.createReadStream('./index.html');
  html.pipe(res);
});

server.listen(3000);

ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์ด ์—†์Œ์—๋„ http๋ชจ๋“ˆ๊ณผ์˜ ์ฐจ์ด๊ฐ€ ๋Š๊ปด์ง„๋‹ค. ๊ธฐ๋Šฅ์„ ๋” ์ถ”๊ฐ€ํ• ์ˆ˜๋ก http ๋ชจ๋“ˆ๋งŒ์œผ๋กœ๋Š” ๋ถˆํŽธํ•ด์ง„๋‹ค.


HTTP์š”์ฒญ

ํ˜„์žฌ๋Š” ๋ชจ๋“  HTTP ์š”์ฒญ๊ณผ ๊ฒฝ๋กœ์— ๋Œ€ํ•˜์—ฌ index.html ํŒŒ์ผ์„ ์ „๋‹ฌํ•˜์ง€๋งŒ, ์‹ค์ œ ์›น์„œ๋ฒ„๋Š” ๋‹ค์ˆ˜์˜ HTTP์š”์ฒญ๊ณผ ๊ฒฝ๋กœ๋“ค์„ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค. all() ๋ฉ”์†Œ๋“œ๋Š” ์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ ๋„˜๊ฒจ์ค€ ๊ฒฝ๋กœ(path)์— ๋Œ€ํ•˜์—ฌ GET, POST, DELETE, PUT ๋“ฑ์„ ๋น„๋กฏํ•œ ๋ชจ๋“  HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. Express๋Š” ๊ฐ HTTP Method๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ get(),post(), put(), delete()์˜ ํ˜•ํƒœ๋กœ ์ œ๊ณตํ•œ๋‹ค.


์ด๋Ÿฌํ•œ ๋ฉ”์†Œ๋“œ๋“ค์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‘๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค.

  • path : ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๊ฒฝ๋กœ์ด๋‹ค.
  • callback : ํ•ด๋‹น ๋ผ์šฐํŠธ ์š”์ฒญ ์ดํ›„ ์‹คํ–‰๋  ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜ ํ˜น์€ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์˜ ๋ฐฐ์—ด์ด๋‹ค.

app.all('/', (req,res) => {
  // Express๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ header๋ฅผ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ์‹ ๊ฒฝ์“ธ ํ•„์š” ์—†๋‹ค.(status code, mime-type ๋“ฑ)
  // ํŒŒ์ผ์„ ๋ณด๋‚ผ์ˆ˜๋„ ์žˆ๋‹ค.
  res.sendFile(path.resolve(__dirname, '../example1.html'))

  // res.send(`<div>Hello</div>`);
  // express๋Š” end๋„ ํ•ธ๋“ค๋ง ํ•œ๋‹ค. ์—ญ์‹œ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„๋œ๋‹ค. 
});

์ฒ˜์Œ ํŠน์ • ๊ฒฝ๋กœ๋ฅผ ์š”์ฒญํ•˜๋ฉด ์ƒํƒœ์ฝ”๋“œ 200 OK์„ ๋ฐ›๊ฒŒ๋˜์ง€๋งŒ, ์ดํ›„๋กœ๋Š” 304 Not modified๋ฅผ ๋ฐ›๋Š”๋‹ค. Express๊ฐ€ HTTP Body๋ฅผ ์ธ์ฝ”๋”ฉํ•ด ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  Etag๋ฅผ ์ƒ์„ฑํ•ด ๋ฒ„์ „์„ ์‹๋ณ„ํ•˜๊ณ  ์ƒํƒœ์ฝ”๋“œ๋ฅผ ์•Œ์•„์„œ ์„ค์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


static ํŒŒ์ผ ๋‹ค๋ฃจ๊ธฐ

๋งŒ์•ฝ http๋ชจ๋“ˆ๋งŒ์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ค๋ฃฌ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•˜์—ฌ ๊ฐ๊ฐ์„ ๋‹ค๋ฃจ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค.

const server = http.createServer((req, res) => {
  if(req.url === '/image1.png' ||
     req.url === '/image2.png' ||
     req.url === '/image3.png'
    }){
    res.writeHead(200, {
      'content-type': 'image/png'
    });
    const image = fs.createReadStream(`.${req.url}`)
    image.pipe(res);
  }else {
    //...
  }
}

๋ธŒ๋ผ์šฐ์ €๋Š” HTML์— ํฌํ•จ๋œ ๋ฆฌ์†Œ์Šค๋“ค์„ GET ์š”์ฒญ์„ ํ†ตํ•ด ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์ด ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€๋ ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๋ผ์šฐํŠธ๋ฅผ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค๋Š”๊ฒƒ์€ ๋ง๋„ ์•ˆ๋˜๋Š” ์ผ์ด๋‹ค. Express์˜ ๋‚ด์žฅ ๋ฏธ๋“ค์›จ์–ด์ธ express.static()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ €์žฅ๋œ ํด๋”๋ฅผ ์ง€์ •ํ•˜๋ฉด ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ GET ์š”์ฒญ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

// express์— ๋‚ด์žฅ๋˜์–ด์žˆ๋Š” static๋ฏธ๋“ค์›จ์–ด ์‚ฌ์šฉ.
app.use(express.static(root, [options]))

๋ฏธ๋“ค์›จ์–ด(Middleware)

๋ฏธ๋“ค์›จ์–ด๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ(Request Obejct)๊ณผ ์„œ๋ฒ„์˜ ์‘๋‹ต(Response Object)์— ์ ‘๊ทผํ•˜์—ฌ ํŠน์ •ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ 3๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค. ์ด๋•Œ 3๋ฒˆ์งธ ์ธ์ž์ธ next๋Š” ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

function middleware(req,res,next){
  doSomethind();
}

๋ฏธ๋“ค์›จ์–ด๋Š” ์ง€์ •๋œ ๊ฒฝ๋กœ(path)์— ๋Œ€ํ•ด์„œ ์‹คํ–‰๋˜๋ฉฐ ํ•˜๋‚˜์˜ ๊ฒฝ๋กœ์— ๋Œ€ํ•˜์—ฌ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ํŠน์ • ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋‚ด์—์„œ nextํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ํ•ด๋‹น ๋ฏธ๋“ค์›จ์–ด ์ดํ›„๋กœ ์ง€์ •๋œ ๋ฏธ๋“ค์›จ์–ด๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.

app.use('/posts', (req,res,next) => {
  logger(req,res);
  next(); // ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.});

app.all('/posts', (req, res, next) => {
  doSomethingWithDB();
  next();});

app.get('/posts', (req, res, next) => {
  res.send('Response: GET /posts');
  // nextํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ดํ›„๋กœ ์ง€์ •๋œ ๋ฏธ๋“ค์›จ์–ด๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
});

app.all('/posts', (req, res, next) => {
  console.log('์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.');
});

์ฐธ๊ณ