๐Ÿ˜Ž ๊ณต๋ถ€ํ•˜๋Š” ์ง•์ง•์•ŒํŒŒ์นด๋Š” ์ฒ˜์Œ์ด์ง€?

[CRUD์„ ์ด์šฉํ•œ File upload Web] (8) Datatables ๋กœ ๋‹ค์Œ ํŽ˜์ด์ง€ ์ด๋™ํ•˜๊ธฐ & user ๋ฐ์ดํ„ฐ html์— ๋„์šฐ๊ธฐ ๋ณธ๋ฌธ

๐Ÿ‘ฉ‍๐Ÿ’ป ๋ฐฑ์—”๋“œ(Back-End)/Node js

[CRUD์„ ์ด์šฉํ•œ File upload Web] (8) Datatables ๋กœ ๋‹ค์Œ ํŽ˜์ด์ง€ ์ด๋™ํ•˜๊ธฐ & user ๋ฐ์ดํ„ฐ html์— ๋„์šฐ๊ธฐ

์ง•์ง•์•ŒํŒŒ์นด 2023. 3. 11. 12:42
728x90
๋ฐ˜์‘ํ˜•

<๋ณธ ๋ธ”๋กœ๊ทธ๋Š” DCodeMania ์˜ ์œ ํŠœ๋ธŒ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๊ณต๋ถ€ํ•˜๋ฉฐ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ :-)>

=> CRUD App With Image Upload Using NodeJs, ExpressJs, MongoDB & EJS Templating Engine

 

 

๐Ÿฅ• datatables 

https://datatables.net/download/

: ๊ฒ€์ƒ‰๊ธฐ๋Šฅ, ์ด๊ฑด์ˆ˜, ํŽ˜์ด์ง€๋‹น๊ฑด์ˆ˜, ์ด์ „/๋‹ค์Œ ํŽ˜์ด์ง€ ์ด๋™, ์ปฌ๋Ÿผ๋ณ„ ์ •๋ ฌ, ํ•„๋“œ ํฌ๋งท, ์™ผ์ชฝ/๊ฐ€์šด๋ฐ/์˜ค๋ฅธ์ชฝ ์ •๋ ฌ ๋“ฑ์„ ์ œ๊ณต

 

๐Ÿฅ• CDN 

https://cdnjs.com/

: Content Delivery Nerwork์˜ ์•ฝ์ž

: ์›น์‚ฌ์ดํŠธ์˜ ์ ‘์†์ž๊ฐ€ ์ฝ˜ํ…์ธ ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•  ๋•Œ ์ž๋™์œผ๋กœ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์„œ๋ฒ„์—์„œ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ์ˆ 

 

๐Ÿฅ• MongoDB์— user Data ๋„ฃ์–ด์ง€๊ณ , html์— ๋“ฑ๋ก๋˜๋Š” ๊ณผ์ •

 

๐Ÿง views/index.ejs

<%- include("layout/header") %>

  <div class="container">
    <div class="row my-4">
      <div class="col-lg-12">
        <!-- router ์— ์žˆ๋Š”  locals.message ๊ฐ€์ ธ์˜ด -->
        <!-- 
        1. <% %> : EJS ์—์„œ ์†Œ์Šค๋‚ด ์—์„œ ์‹คํ–‰๋˜์ง€๋งŒ ๋ณด์ด์ง€๋Š” ์•Š๋Š” ํƒœ๊ทธ
        2. <%- %> : <% %> ํƒœ๊ทธ์™€๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ HTML ์ฝ”๋“œ๋ฅผ ๋‚ ๊ฒƒ(Raw)๋กœ ๋ณด์ž„
        3. <%= %> : ์ด์Šค์ผ€์ดํ”„ ๋ฌธ์ž๋ฅผ ํฌํ•จ, ๋‹ค๋ฅธ ํŽ˜์ด์ง€์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์ค„ ๋•Œ
       -->
        <% if (message) { %>
          <div class="alert alert-dismissible fade show alert-<%= message.type %>" role="alert">
            <button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
            <strong>
              <%= message.message %>
            </strong>
          </div>
          <% } %>

            <div class="table-responsive">
              <% if (users !="" ) { %>
                <!-- ํ™”๋ฉด ํฌ๊ธฐ์— ๋”ฐ๋ผ ํšจ๊ณผ์ ์œผ๋กœ ์ฝ˜ํ…์ธ ๊ฐ€ ํ‘œ์‹œ๋˜๋„๋ก ํ…Œ์ด๋ธ”์„ ์กฐ์ • -->
                <table class="table table-striped">
                  <!-- ํ…Œ์ด๋ธ”์˜ ์—ด์˜ ๋จธ๋ฆฌ๊ธ€์ธ ํ–‰๋“ค์˜ ์ง‘ํ•ฉ -->
                  <thead class="table table-striped text-center my-3">
                    <tr class="table-dark">
                      <th>ID</th>
                      <th>Image</th>
                      <th>Name</th>
                      <th>E-mail</th>
                      <th>Phone</th>
                      <th>Action</th>
                    </tr>
                  </thead>
                  <tbody>
                    <% users?.forEach((row, index)=> { %>
                      <tr class="align-middle">
                        <td>
                          <%= index %>
                        </td>
                        <td><img src="<%= row.image %>" width="50" class="img-thumbnail"></td>
                        <td>
                          <%= row.name %>
                        </td>
                        <td>
                          <%= row.email %>
                        </td>
                        <td>
                          <%= row.phone %>
                        </td>
                        <td>
                          <a href="/edit/<%= row._id %>" class="text-success"><i class="fas fa-edit fa-lg mx-1"></i></a>
                          <a href="/delete/<%= row._id %>" class="text-danger"><i
                              class="fas fa-trash fa-lg mx-1"></i></a>
                        </td>
                      </tr>
                      <% }) %>
                  </tbody>
                </table>
                <% } else { %>
                  <h1 class="text-center text-secondary mt-S">No users found in the databases!</h1>
                  <% } %>
            </div>
      </div>
    </div>
  </div>

  <%- include("layout/footer") %>

 

๐Ÿง views/layout/headr.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>
        <%= title %>
    </title>

    <!-- ๋ถ€ํŠธํŠธ๋žฉ ์‚ฌ์šฉ -->
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-beta2/css/bootstrap.min.css"
        integrity="sha512-aqT9YD5gLuLBr6ipQAS+72o5yHKzgZbGxEh6iY8lW/r6gG14e2kBTAJb8XrxzZrMOgSmDqtLaF76T0Z6YY2IHg=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css"
        integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
    <!-- ๋‹ค์Œ ํŽ˜์ด์ง€ ์ด๋™ํ•˜๋Š” ํ…Œ์ด๋ธ” ์‚ฌ์šฉ -->
    <link href="https://cdn.datatables.net/v/bs4/dt-1.13.3/datatables.min.css" rel="stylesheet" />
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a href="/" class="navbar-brand"><i class="fas fa-code me-2"></i>GANI</a>

            <button class="navbar-toggler" data-bs-target="#my-nav" data-bs-toggle="collapse" aria-controls="my-nav"
                aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>


            <div id="my-nav" class="collapse navbar-collapse">
                <ul class="navbar-nav ms-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="/"><i class="fas fa-home me-1"></i>Home</a>
                    </li>
                    <li class="nav-item active">
                        <a class="nav-link" href="/add"><i class="fas fa-user-plus me-1"></i>Add user</a>
                    </li>
                    <li class="nav-item active">
                        <a class="nav-link" href="#"><i class="fas fa-globe me-1"></i>About</a>
                    </li>
                    <li class="nav-item active">
                        <a class="nav-link" href="#"><i class="fas fa-envelope me-1"></i>Contact</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

 

๐Ÿง views/layout/footer.ejs

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
  integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-beta2/js/bootstrap.bundle.min.js"
  integrity="sha512-43iShtbiyImxjjU4a9rhXBy7eKtIsrpll8xKhe1ghKqh5NyfME8phZs5JRFZpRBe1si44WM3tNmnqMym7JRmDQ=="
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- ์ด์ „/๋‹ค์Œ ํŽ˜์ด์ง€ ์ด๋™ -->
<script src="https://cdn.datatables.net/v/bs4/dt-1.13.3/datatables.min.js"></script>

<script>
  // ํ‘œ์‹œ๋œ ํ…Œ์ด๋ธ”์„ ์ดˆ๊ธฐํ™”ํ‘œ์‹œ๋œ ํ…Œ์ด๋ธ”์„ ์ดˆ๊ธฐํ™” => ์ด์ „/๋‹ค์Œ ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ ํ•ด์คŒ
  $(document).ready(function () {
    $("table").DataTable({
      order: [0, "desc"]
    });
  });
</script>
</body>

</html>

 

๐Ÿง routes/routes.js

const express = require("express");
const router = express.Router();
const User = require("../models/user");
const multer = require("multer");

// image upload
var storage = multer.diskStorage({
  // ํŒŒ์ผ์ด ์—…๋กœ๋“œ๋  ๊ฒฝ๋กœ ์„ค์ •
  destination: function (req, file, cb) {
    cb(null, "./uploads");
  },
  // timestamp๋ฅผ ์ด์šฉํ•ด ์ƒˆ๋กœ์šด ํŒŒ์ผ๋ช… ์„ค์ •
  filename: function (req, file, cb) {
    cb(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
  }
})

var upload = multer({
  storage: storage,
}).single("image");   // ํ•œ ๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ

// Insert an user into database route
router.post("/add", upload, (req, res) => {
  const user = new User({
    name: req.body.name,
    email: req.body.email,
    phone: req.body.phone,
    image: req.file.filename,
  });

  user.save().then((err) => {
    // index.js ์— res.locals.message ์žˆ์Œ
    req.session.message = {
      type: "success",
      message: "User added successsfull!"
    };
    // ์ง€์ •๋œ ๋‹ค๋ฅธ URL๋กœ ์žฌ์š”์ฒญ (ํ™ˆ)
    res.redirect("/");
  }).catch((err) => {
    res.json({ message: err.message, type: "danger" });
  })
});

// Get all users route
router.get("/", (req, res) => {
  // Query๋ฅผ ์ด์šฉํ•  ๋•Œ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๋ฆฌํ„ด๋ฐ›๊ณ  ์‹ถ๋‹ค๋ฉด exec() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉ
  // find()๋ฅผ ์‹คํ–‰ํ•˜์—ฌ Query์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฆฌํ„ด
  User.find({}).then((error, users) => {
    res.render("index", {
      title: "Home Page",
      // ๊ถ๊ธˆ : ๋„ˆ ์™œ error ์žกํžˆ๋‹ˆ?
      users: error
    });
  }).catch((error) => {
    res.json({ message: error.message });
  });
});

router.get("/add", (req, res) => {
  res.render("addUsers", { title: "Add users" });
})

module.exports = router;

 

๐Ÿง index.js

// imports
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const session = require("express-session");
// express-session 
// 1. ํŒŒ์ผ์— ์ €์žฅ
// 2. DB์— ์ €์žฅ
// 3. Memory ์— ์ €์žฅ

const app = express();
const PORT = process.env.PORT || 8000;

// Database ์—ฐ๊ฒฐ
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true });   // useNewUrlParser : ์—๋Ÿฌ ๋ฐฉ์ง€
const db = mongoose.connection;
db.on("error", (error) => console.log(error));
db.once("open", () => console.log("๐Ÿ’š Connected to the database!"));

// Middleware ์—ฐ๊ฒฐ
app.use(express.urlencoded({ extended: false }));
app.use(express.json());        // JSONํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•ด์„
app.use(session({
  secret: process.env.SECRET,   // ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•œ keygen. ๋ณดํ†ต env์— ๋„ฃ์–ด์„œ ์ „๋‹ฌ
  saveUninitialized: true,      // ์„ธ์…˜์— ์ €์žฅํ•  ๋‚ด์—ญ์ด ์—†๋”๋ผ๋„ ์ฒ˜์Œ๋ถ€ํ„ฐ ์„ธ์…˜์„ ์ƒ์„ฑํ• ์ง€ ์„ค์ •
  resave: false,                // ์„ธ์…˜์„ ์–ธ์ œ๋‚˜ ์ €์žฅํ• ์ง€ ์„ค์ •ํ•จ 
  cookie: {                     // ์ฟ ํ‚ค์˜ ์œ ํšจ์‹œ๊ฐ„
    maxAge: 86400000,           // 24 hours (= 24 * 60 * 60 * 1000 ms)
  },
})
);

// ๊ณตํ†ต EJS ํŒŒ์ผ(header๋‚˜ footer)์—์„œ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์„ธํŒ…
app.use((req, res, next) => {
  // ejs ํŒŒ์ผ์—์„œ ์‚ฌ์šฉํ•˜๋ ค๋ฉด 'res.locals.๋ณ€์ˆ˜๋ช… = ๊ฐ’' ์„ ์ด์šฉ
  res.locals.message = req.session.message;
  delete req.session.message;
  next();         // ์•ฑ ๋‚ด์˜ ๊ทธ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ
});

// uploads ํด๋”์˜ ์‚ฌ์ง„ html์— ๋ณด์ด๊ฒŒ ํ•˜๊ธฐ
app.use(express.static("uploads"));

// set template engine
app.set("view engine", "ejs");

// router
app.use("", require("./routes/routes"));

app.get("/", (req, res) => {
  res.send("Hello World");
});

app.listen(PORT, () => {
  console.log(`server started at ๐Ÿš€ http://localhost:${PORT}`);
});

 

์ฒ˜์Œ์—๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด์š”~~~ add user ํ•ด๋ณด์‡ผ~~~
๋ฉ”์ธ ํ™ˆ์— user ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์™€์œ ~~~ mongodb์—์„œ๋„ ๋ณด์ด๋„ค์šฉ!

 

 


๊ทผ๋ฐ ์›ƒ๊ธด๊ฑด router ์—์„œ user ์ •๋ณด ๋„˜๊ธฐ๋Š”๋ฐ ์™œ error์—์„œ user json ์ด ์žกํž๊นŒ~? ^_^

users์—์„œ๋Š” undefined ๋‚˜์˜ดใ…‹ใ…‹

 

728x90
๋ฐ˜์‘ํ˜•
Comments