본문 바로가기
데이터베이스

mongoose-express 데이터 업데이트 하기

by 즐거운코딩 2023. 8. 19.
반응형

mongoose 데이터베이스에 입력한 데이터를 조회하여 업데이트하는 방법에 대해 알아봅니다.

조회할 데이터에 해당하는 id 를 URL에서 받아와서 데이터베이스에서 해당 id를 조회하여 기존에 신규 데이터 입력하기 위해 만든 폼에 데이터를 보여주고 수정하여 저장하도록 합니다.

 

1. index.js 데이터 수정 경로 추가 하기

데이터 조회하는 app.get("/products/:id") 을 복사하여 다음과 같이 수정합니다.

const express = require("express");
const app = express();
const path = require("path");
const Product = require("./models/product");

const mongoose = require("mongoose");
mongoose
  .connect("mongodb://127.0.0.1:27017/farmStand", { useNewUrlParser: true })
  .then(() => {
    console.log("mongo connection open !!!");
  })
  .catch((err) => {
    console.log("OH NO mongo connection error !!!");
    console.log(err);
  });

app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: true }));

app.get("/products", async (req, res) => {
  const products = await Product.find({});
  res.render("products/index", { products });
});

app.get("/products/new", (req, res) => {
  res.render("products/new");
});

app.post("/products", async (req, res) => {
  const newProduct = new Product(req.body);
  await newProduct.save();
  res.redirect(`/products/${newProduct._id}`);
});

app.get("/products/:id", async (req, res) => {
  const { id } = req.params;
  const product = await Product.findById(id);
  res.render("products/show", { product });
});

app.get("/products/:id/edit", async (req, res) => {
  const { id } = req.params;
  const product = await Product.findById(id);
  res.render("products/edit", { product });
});

app.listen(3000, () => {
  console.log("APP IS LISTENING ON PORT 3000!");
});

 2. 데이터 수정 화면 만들기

기존에 만든 new.ejs 내용을 복사하여 새로 만든 edit.ejs에 복사해 넣고 데이터베이스에서 조회한 id 값에 해당하는 product 를 신규 데이터 입력 폼에  불러옵니다.

views>products>edit.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Edit Product</title>
  </head>
  <body>
    <h1>Edit Product</h1>
    <form action="/products/<%=product._id%>" method="POST">
      <label for="name">Product Name</label>
      <input
        type="text"
        name="name"
        id="name"
        placeholder="Product name"
        value="<%=product.name%>"
      />
      <label for="price">Product Price</label>
      <input
        type="number"
        name="price"
        id="price"
        placeholder="Price (Unit)"
        value="<%=product.price%>"
      />
      <label for="category">Select Category</label>
      <select name="category" id="category">
        <option value="fruit">fruit</option>
        <option value="vegetable">vegetable</option>
        <option value="dairy">dairy</option>
      </select>
      <button>Summit</button>
    </form>
  </body>
</html>

ejs 포맷인 <%=product._id%> 와 같은 형식으로 product 에 해당되는 값을 폼의 각 필드에 넣어줍니다.

다음과 같이 브라우져에서 수정화면으로 넘어가는 것을 확인해 봅니다.

product id 뒤에 edit 추가하면 수정하는 화면으로 전환

카테고리를 기존 값과 같이 dairy로 변경하는 것은 좀 복잡하여 다음에 작업하기로 합니다.

이제 데이터를 수정하고 다시 저장하기 위해서는 summit 요청시 데이터를 제출할 end point를 만듭니다.

 

3. 수정한 데이터 저장하기

데이터를 업데이트 하는 방법으로 put, patch가 있는데 전체 데이터를 바꾸는 것은 put 이고 일부 데이터를 바꾸는 것이 patch 입니다.

이번에는 동시에 여러 개 데이터를 바꿀 수 있으니 put을 사용하도록 하겠습니다.

하지만 edit.ejs 에 사용하는 폼에서는 method로 POST 로 되어 있어 summit 요청시 해당 method를 put으로 변경하기 위해서는 추가로 method-orverride 패키지를 설치해야 합니다.

  • 설치 : npm i method-overrride 

https://www.npmjs.com/package/method-override

  • express 에 사용하도록 정의 : methodOverride = require('method-override')
  • override 적용 : app.use(methodOverride('_method'))
    • html에서 사용하기위해 query문에 ?_method=PUT 적용
const express = require("express");
const app = express();
const path = require("path");
const Product = require("./models/product");
const methodOverride = require("method-override");

const mongoose = require("mongoose");
mongoose
  .connect("mongodb://127.0.0.1:27017/farmStand", { useNewUrlParser: true })
  .then(() => {
    console.log("mongo connection open !!!");
  })
  .catch((err) => {
    console.log("OH NO mongo connection error !!!");
    console.log(err);
  });

app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: true }));
app.use(methodOverride("_method"));

다음과 같이 edit.ejs 에 query method 적용합니다.

    <h1>Edit Product</h1>
    <form action="/products/<%=product._id%>?_method=PUT" method="POST">
      <label for="name">Product Name</label>
      <input
        type="text"
        name="name"
        id="name"
        placeholder="Product name"
        value="<%=product.name%>"
      />

app.put 을 다음과 같이 작성하고 method 가 POST 대신 PUT 으로 동작되는지 확인해 봅니다.

app.put("/products/:id", async (req, res) => {
  console.log(req.body);
  res.send("PUT !!!!");
});

price 를 변경하고 summit 제출시 정상적으로 PUT 으로 요청되는 것 확인
console.log로 수정한 데이터 전달되는 내용 확인

이제 PUT으로 넘겨받은 데이터를 실제 데이터베이스에 저장해봅니다.

mongosse MODEL API로 findByIdAndUpdate 를 사용하여 id를 찾아 데이터를 업데이트 합니다. 이 때 파라미터로 id 값과 저장할 데이터 req.body 를 넘겨주며 runValidator:true 옵션으로 데이터 유효성 검증을 적용해줄 수 있습니다.

추가 옵션으로 new:true 를 적용하면 저장한 결과값으로 수정된 값을 반환합니다. (new:false 가 기본이며 변경전 원래데이터를 보여줌)

그리고 저장 후 페이지 새로고침시 다시 PUT 이 적용되지 않도록 해당 product._id 에 해당하는 화면으로 redirect 합니다.

app.put("/products/:id", async (req, res) => {
  const { id } = req.params;
  const product = await Product.findByIdAndUpdate(id, req.body, {
    runValidators: true,
    new: true,
  });
  res.redirect(`/products/${product._id}`);
});

👉 redirect 시 사용하는 id는 URL 전달받은 id가 아닌 실제 mongoose에서 id를 사용하도록 합니다.  이때 서버 저장 시간을 고려하여 async/await 비동기로 처리해야 오류가 발생되지 않음

변경된 가격과 카테고리 dairy -> fruit 변경된 것 확인

4. 수정화면 연결 버튼 추가

show.ejs 화면에 edit 화면 연결하는 링크를 다음과 같이 추가합니다.

  <body>
    <h1><%= product.name %></h1>
    <ul>
      <li>Price $<%= product.price %></li>
      <li>Category <%= product.category %></li>
    </ul>
    <a href="/products">All Products</a>
    <a href="/products/<%=product._id%>/edit">Edit Product</a>
  </body>

Edit 화면으로 전환되는 Edit Product 링크 만들기

수정화면에서 데이터 변경없이 취소하는 기능을 위해 Cancel 링크를 추가합니다.

  <body>
    <h1>Edit Product</h1>
    <form action="/products/<%=product._id%>?_method=PUT" method="POST">
      <label for="name">Product Name</label>
      <input
        type="text"
        name="name"
        id="name"
        placeholder="Product name"
        value="<%=product.name%>"
      />
      <label for="price">Product Price</label>
      <input
        type="number"
        name="price"
        id="price"
        placeholder="Price (Unit)"
        value="<%=product.price%>"
      />
      <label for="category">Select Category</label>
      <select name="category" id="category">
        <option value="fruit">fruit</option>
        <option value="vegetable">vegetable</option>
        <option value="dairy">dairy</option>
      </select>
      <button>Summit</button>
    </form>
    <a href="/products/<%=product._id%>">Cancel</a>
  </body>

Cancel 링크 통해서 해당 Product 상세화면으로 다시 전환됨

다음으로 데이터에 저장된 카테고리 값으로 선택 항목이 보여질 수 있는 기능을 만들어 보겠습니다.

반응형