KDT/TIL

9/5 TIL : express๋กœ API ๋งŒ๋“ค๊ธฐ, view engine(EJS), express ํด๋” ๊ตฌ์กฐ

ebulsok 2022. 9. 18. 20:11

๐Ÿ”Ž Express๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ„๋‹จํ•œ api ๋งŒ๋“ค๊ธฐ(DB ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)

// @ts-check
const express = require('express');

const app = express();
const PORT = 4000;
const userRouter = express.Router();
const postsRouter = express.Router();
const USER = [
  {
    id: 'bulsok',
    name: 'ebulsok',
  },
  {
    id: 'test',
    name: 'ํ…Œ์ŠคํŠธ',
  },
];

app.use('/users', userRouter);
app.use('/posts', postsRouter);

 

๐Ÿšฉ ํšŒ์› ๋ชฉ๋ก ๋ณด์—ฌ์ฃผ๊ธฐ api

GET localhost:4000/users

userRouter.get('/', (req, res) => {
  res.send(USER);
});

 

๐Ÿšฉ ํŠน์ • ํšŒ์› ์ •๋ณด ๋ณด์—ฌ์ฃผ๊ธฐ api

GET localhost:4000/users/:id

userRouter.get('/:userID', (req, res) => {
  const userData = USER.find((user) => user.id === req.params.id);
  if (userData) res.send(userData);
  else res.end('ID not found');
});

 

๐Ÿšฉ ํšŒ์› ์ถ”๊ฐ€ํ•˜๊ธฐ api

POST localhost:4000/users?id=test&name=test

userRouter.post('/', (req, res) => {
  if (req.query.id && req.query.name) {
    const newUser = {
      id: req.query.id,
      name: req.query.name,
    };
    USER.push(newUser);
    res.send('ํšŒ์› ๋“ฑ๋ก ์™„๋ฃŒ');
  } else res.end('Unexpected query');
});

 

๐Ÿšฉ ํšŒ์› ์ˆ˜์ •ํ•˜๊ธฐ api

PUT localhost:4000/users/:id?id=test&name=test

userRouter.put('/:userID', (req, res) => {
  if (req.query.id && req.query.name) {
    const arrIndex = USER.findIndex((user) => user.id === req.params.userID);
    if (arrIndex !== -1) {
      USER[arrIndex].id = req.query.id;
      USER[arrIndex].name = req.query.name;
      res.send('ํšŒ์›์ •๋ณด ์ˆ˜์ • ์™„๋ฃŒ');
    } else res.end('ํšŒ์›์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.');
  } else res.end('Unexpected query');
});

 

๐Ÿšฉ ํšŒ์› ์‚ญ์ œํ•˜๊ธฐ api

DELETE localhost:4000/users/:id

userRouter.delete('/:userID', (req, res) => {
  const arrIndex = USER.findIndex((user) => user.id === req.params.userID);
  if (arrIndex !== -1) {
    USER.splice(arrIndex, 1);
    res.send('ํšŒ์› ์‚ญ์ œ ์™„๋ฃŒ');
  } else res.end('ํšŒ์›์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.');
});

๐Ÿ”Ž View Engine

๐Ÿšฉ EJS

  • ๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” view engine
  • HTML ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉ
  • ๋‹จ, ๋ ˆ์ด์•„์›ƒ ๊ธฐ๋Šฅ X

๐Ÿšฉ pug

  • HTML ๋ฌธ๋ฒ•์„ ๋‹จ์ˆœํ™”ํ•˜์—ฌ ์‚ฌ์šฉ
  • ๋ ˆ์ด์•„์›ƒ ๊ธฐ๋Šฅ O

๐Ÿšฉ NUNJUCKS

  • HTML ๋ฌธ๋ฒ•์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
  • ๋ ˆ์ด์•„์›ƒ ๊ธฐ๋Šฅ O

 

  • [ํ„ฐ๋ฏธ๋„] npm i -D ejs
  • express์—๊ฒŒ ์–ด๋–ค view engine์œผ๋กœ ์›นํŽ˜์ด์ง€๋ฅผ ๊ทธ๋ฆด ๊ฒƒ์ธ์ง€ ์•Œ๋ ค์ฃผ๊ธฐ
app.set('view engine', 'ejs');
app.set('views', 'views');
  • ํด๋” ๊ฐ€์žฅ ์™ธ๋ถ€์— views ํด๋” ๋งŒ๋“ค๊ธฐ
  • views ํด๋” ๋‚ด๋ถ€์— index.ejs ํŒŒ์ผ ๋งŒ๋“ค๊ณ  ์ฝ”๋“œ ์ž…๋ ฅํ•˜๊ธฐ
  • localhost:4000/users ๊ธฐ๋ณธ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด index.ejs ํŒŒ์ผ ๋„์šฐ๊ธฐ
userRouter.get('/', (req, res) => {
  // res.send(USER);
  res.render('index');
});
  • res.render('ejs ํŒŒ์ผ๋ช…', { ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ๋ฐ์ดํ„ฐ });

 

  • ์˜ค๋ธŒ์ ํŠธ๋กœ ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ๋Š” ejs ํŒŒ์ผ ๋‚ด๋ถ€์—์„œ <%= %>๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ธŒ์ ํŠธ์˜ ํ•„๋“œ๋ช…์œผ๋กœ ํ˜ธ์ถœ
<body>
    <h1>ํšŒ์› ๋ชฉ๋ก</h1>
    <h2>์ด ํšŒ์› ์ˆ˜: <%= userCounts %></h2>
    <ul>
        <li>
            <p>ID: <%= USER[0].id %></p>
            <p>NAME: <%= USER[0].name %></p>
        </li>
    </ul>
</body>

 

๐Ÿšฉ if / for ๋ฌธ ์‚ฌ์šฉํ•˜๊ธฐ: <% %>

<body>
  <h1>ํšŒ์› ๋ชฉ๋ก</h1>
  <h2>์ด ํšŒ์› ์ˆ˜: <%= userCounts %></h2>
  <ul>
    <% if (userCounts > 0) { %>
    <% for(let i = 0; i < userCounts; i++) { %>
    <li>
      <p>ID: <%= USER[i].id %></p>
      <p>NAME: <%= USER[i].name %></p>
    </li>
    <% } %>
    <% } else { %>
    <li>
      ํšŒ์› ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค!
    </li>
    <% } %>
  </ul>
</body>

 

๐Ÿšฉ CSS ์ ์šฉํ•˜๊ธฐ

  • views/css/style.css ์ƒ์„ฑ
  • index.js ํŒŒ์ผ์— <link rel="stylesheet" href="./css/style.css"> ๊ฑธ์—ˆ์„ ๋•Œ ์•ˆ๋˜๋Š” ์ด์œ : URL๊ณผ back-end ํด๋” ๊ตฌ์กฐ๊ฐ€ ๋™์ผํ•˜๋‹ค๋ฉด ํ•ดํ‚น ์œ„ํ—˜์„ฑ์ด ๋†’๊ธฐ ๋•Œ๋ฌธ์— express ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” static์ด๋ผ๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ œ๊ณต
app.use(express.static('views'));
  • static์„ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํด๋” ๊ฒฝ๋กœ์˜ ์‹œ์ž‘์ ์€ localhost:4000/views๊ฐ€ ๋จ
  • ์—ฌ๋Ÿฌ๊ฐœ ํด๋”๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋ณดํ†ต public์ด๋ผ๋Š” ํด๋”๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •ํ•ด๋†“๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉํ•˜๋Š” css, js, ์ด๋ฏธ์ง€ ๋“ฑ์„ ์œ„์น˜์‹œํ‚ด

 

๐Ÿ”Ž express ๊ธฐ๋ณธ ํด๋” ๊ตฌ์กฐ

  • /public : ์™ธ๋ถ€(๋ธŒ๋ผ์šฐ์ € ๋“ฑ ํด๋ผ์ด์–ธํŠธ)์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํŒŒ์ผ ๋ชจ์•„๋‘ (/images, /js, /css)
  • /routes : ํŽ˜์ด์ง€ ๋ผ์šฐํŒ…๊ณผ ๊ด€๋ จ๋œ ํŒŒ์ผ ์ €์žฅ
  • /views : jade, ejs ํŒŒ์ผ ๋“ฑ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ ๋ชจ์•„๋‘ . ์›น ์„œ๋ฒ„ ์‚ฌ์šฉ ์‹œ ์ด ํด๋”์˜ ํŒŒ์ผ๋“ค์„ ์‚ฌ์šฉํ•ด ๋ Œ๋”๋ง
  • /app.js : ํ•ต์‹ฌ์ ์ธ ์„œ๋ฒ„ ์—ญํ• , ๋ฏธ๋“ค์›จ์–ด ๊ด€๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง, ๋ผ์šฐํŒ…์˜ ์‹œ์ž‘์ 
  • /package.json : npm์˜ ์˜์กด์„ฑ ํŒŒ์ผ, ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ๋œ ๋ชจ๋“ˆ์„ ์„ค์น˜(npm install)ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ๋‚ด์šฉ์„ ๋‹ด์Œ
  • /bin/www : ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ, ํ”„๋กœ์ ํŠธ ๋Œ์•„๊ฐ€๋Š” ํฌํŠธ๋ฒˆํ˜ธ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ

 

๐Ÿšฉ ๊ธฐ๋Šฅ์„ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ๋ชจ๋“ˆํ™”ํ•˜๊ธฐ

// app.js
const userRouter = require('./routes/users');
app.use('/users', userRouter);

// routes/users.js
module.exports = router;

 

 

<!-- index.ejs -->

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Index</title>
</head>

<body>
  <h1>Hello, Express Service</h1>
  <h2><a href="/users">Move to USER service</a></h2>
</body>

</html>

 

๐Ÿ”Ž Error ํ•ธ๋“ค๋ง

  • ์„œ๋ฒ„ err๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ •์„์ ์œผ๋กœ๋Š” err๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ๋‹ค์Œ ๋ฉ”์‹œ์ง€์™€ statuscode๋ฅผ ์ „๋‹ฌํ•ด์ค˜์•ผ ํ•จ
// ํšŒ์› ๋ชฉ๋ก
router.get('/', (req, res) => {
  const userLen = USER.length;
  res.render('users', { USER, userCounts: userLen, imgName: 'img1.jpg' });
});

// ํšŒ์› ์ •๋ณด(id)
router.get('/:userID', (req, res) => {
  const userData = USER.find((user) => user.id === req.params.userID);
  if (userData) res.send(userData);
  else {
    const err = new Error('ID not found');
    err.statusCode = 404;
    throw err;
    // res.end('ID not found');
  }
});

// ํšŒ์› ๋“ฑ๋ก
router.post('/', (req, res) => {
  if (req.query.id && req.query.name && req.query.email) {
    const newUser = {
      id: req.query.id,
      name: req.query.name,
      email: req.query.email,
    };
    USER.push(newUser);
    res.send('ํšŒ์› ๋“ฑ๋ก ์™„๋ฃŒ');
  } else {
    const err = new Error('Unexpected query');
    err.statusCode = 404;
    throw err;
  }
});

// ํšŒ์› ์ •๋ณด ์ˆ˜์ •
router.put('/:userID', (req, res) => {
  if (req.query.id && req.query.name && req.query.email) {
    const arrIndex = USER.findIndex((user) => user.id === req.params.userID);
    if (arrIndex !== -1) {
      USER[arrIndex].id = req.query.id;
      USER[arrIndex].name = req.query.name;
      USER[arrIndex].email = req.query.email;
      res.send('ํšŒ์›์ •๋ณด ์ˆ˜์ • ์™„๋ฃŒ');
    } else {
      const err = new Error('ID not found');
      err.statusCode = 404;
      throw err;
    }
  } else {
    const err = new Error('Unexpected query');
    err.statusCode = 404;
    throw err;
  }
});

// ํšŒ์› ์‚ญ์ œ
router.delete('/:userID', (req, res) => {
  const arrIndex = USER.findIndex((user) => user.id === req.params.userID);
  if (arrIndex !== -1) {
    USER.splice(arrIndex, 1);
    res.send('ํšŒ์› ์‚ญ์ œ ์™„๋ฃŒ');
  } else {
    const err = new Error('ID not found');
    err.statusCode = 404;
    throw err;
  }
});
  • app.js์˜ ๋ฏธ๋“ค์›จ์–ด ์ค‘ ๋งˆ์ง€๋ง‰์— throw๋œ err๋ฅผ ๋ฐ›๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ถ”๊ฐ€
app.use((err, req, res, next) => {
  console.log(err.stack);
  res.status(err.statusCode);
  res.send(err.message);
});
  • ํ•ด๋‹น ๋ฏธ๋“ค์›จ์–ด๋Š” err์ธ์ž์™€ res๋ฅผ ๊ฐ™์ด ์จ์•ผ ํ•˜๋ฏ€๋กœ app.use() ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋ฅผ ์ „๋ถ€ ์‚ฌ์šฉํ•ด์•ผ ํ•จ.
  • 3๊ฐœ๋งŒ ์“ธ ๊ฒฝ์šฐ req, res, next๋กœ ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— err๋ฅผ ๋ฐ›์ง€ ๋ชป ํ•จ