37. Node.js 입문주차 - MongoDB, Mongoose, Studio 3T, 실습 (항해 13일차)

코이그·2023년 4월 15일

항해99

목록 보기
36/54

Studio 3T

Thunder client가 API 동작들을 테스트할 수 있는 편리한 툴인 것처럼 Studio 3T는 database 관련 동작들을 테스트할 수 있는 툴이다.

데이터의 조회, 삽입, 삭제 등이 가능하다.

Mongoose

Node.js에서 MongoDB 데이터베이스에 연결하기 위해 설치해야하는 라이브러리.

npm install mongoose

Schema

Collections에 들어가는 문서에 어떤 종류의 값이 들어가는지 정의한다.
null, String, Number, Date, Buffer, Boolean, ObjectId, Array

Model

데이터베이스의 구조.

MongoDB 연결

프로젝트 구조:

  • app.js (루트 파일)
  • routes/ (라우터 폴더)
  • schemas/ (스키마 폴더)

spa_mall 실습

schemas/

index.js

// 1
const mongoose = require("mongoose");

// 2
const connect = () => {
  mongoose
    .connect("mongodb://localhost:27017/spa_mall")
    .catch((err) => console.log(err));
};

// 3
mongoose.connection.on("error", (err) => {
  console.error("몽고디비 연결 에러", err);
});

// 4
module.exports = connect;
  1. 다른 모듈과 동일하게 우선 모듈을 require로 불러온다.
  2. connect라는 변수에 mongoose의 connect가 반환하는 값을 저장한다.
  3. 결이 실패됐을 때 오류를 출력한다.
  4. connect 변수를 외부로 내보낸다.

goods.js

const mongoose = require("mongoose");

// 1
const goodsSchema = new mongoose.Schema({
  // 2
  goodsId: {
    // 3
    type: Number,
    required: true,
    unique: true,
  },
  name: {
    type: String,
    required: true,
    unique: true,
  },
  thumbnailUrl: {
    type: String,
  },
  category: {
    type: String,
  },
  price: {
    type: Number,
  },
});
// 4
module.exports = mongoose.model("Goods", goodsSchema);
  1. schema 생성한다.
  2. key 속성 할당한다.
  3. type: 데이터 타입, required: 필수성 여부, unique: 고유성 여부.
  4. goodsSchema를 사용해 Goods라는 모델 생성 후 외부로 내보낸다.

cart.js

const mongoose = require("mongoose");

const cartSchema = new mongoose.Schema({
  goodsId: {
    type: Number,
    required: true,
    unique: true,
  },
  quantity: {
    type: Number,
    required: true,
  },
});

module.exports = mongoose.model("Cart", cartSchema);

routes/goods.js

const express = require("express");
const router = express.Router();

// 1
const Goods = require("../schemas/goods");
// 2                // 7
// goods 추가 API
router.post("/goods", async (req, res) => {
  // 3
  const { goodsId, name, thumbnailUrl, category, price } = req.body;

  // 4
  const goods = await Goods.find({ goodsId });
  // 5
  if (goods.length) {
    return res.status(400).json({ success: false, errorMessage: "이미 있는 데이터입니다." });
  }

  // 6
  const createdGoods = await Goods.create({ goodsId, name, thumbnailUrl, category, price, });

  res.json({ goods: createdGoods });
});

// 장바구니 상품 추가 API
// 1
const Cart = require("../schemas/cart.js");
// 2
router.post("/goods/:goodsId/cart", async (req, res) => {
  // 3
  const { goodsId } = req.params;
  const { quantity } = req.body;

  // 4
  const existsInCart = await Cart.find({ goodsId });
  if (existsInCart.length) {
    return res.status(400).json({
      success: false,
      message: "카트에 상품이 존재합니다.",
    });
  }

  // 5
  await Cart.create({ goodsId, quantity });
  res.json({ result: "success" });
});

// 장바구니 상품 수량 수정 API
// 1
router.put("/goods/:goodsId/cart", async (req, res) => {
  // 2
  const { goodsId } = req.params;
  const { quantity } = req.body;

  // 3
  const existsInCart = await Cart.find({ goodsId });
  if (existsInCart.length) {
    await Cart.updateOne(
      { goodsId: goodsId },
      { $set: { quantity: quantity } }
    );
  }
  res.status(200).json({ success: true });
});

// 장바구니 상품 삭제 API
// 1
router.delete("/goods/:goodsId/cart", async (req, res) => {
  // 2
  const { goodsId } = req.params;

  // 3
  const existsInCart = await Cart.find({ goodsId });
  if (existsInCart.length) {
    await Cart.deleteOne({ goodsId });
  }
  res.status(200).json({ success: true });
});

module.exports = router;

goods 추가 API

  1. goods 모델을 사용하기 위해 schemas/goods를 require한다.
  2. router의 POST 메소드를 정의한다.
  3. request의 body를 구조 분해해서 각각 변수에 저장한다.
  4. Goods에서 goodsId를 검색한다.
  5. goods에 길이가 0이 아니라면 해당 데이터가 존재하므로 오류를 반환한다.
  6. 그게 아니라면 Goods에 위의 설정한 변수들로 구성된 데이터를 추가하고 새로운 createdGoods에 데이터를 저장 후 res.json으로 반환한다.
  7. 동기적으로 처리해야 하기 때문에 익명 함수 (req, res) 앞에 async를 붙여준다.

장바구니 상품 추가 API

  1. 장바구니를 사용해야 하기 때문에 schemas/car를 require한다.
  2. router의 POST 메소드를 정의하는데, 이 때 goodsId를 params로 받고 뒤에 cart를 붙여준다.
  3. params로 받은 goodsId와 body로 받은 quantity 데이터를 각각 변수에 저장한다.
  4. 해당 goodsId의 상품이 Cart에 존재한다면 오류를 반환한다.
  5. 존재하지 않는다면 goodsId와 quantity 값을 가진 데이터를 데이터베이스에 추가한다.

장바구니 상품 수량 수정 API

  1. 수정을 해야하기 때문에 PUT 메소드를 사용한 점 외에는 위의 2번과 동일하다.
  2. 위의 3번과 동일하다.
  3. goodsId의 상품이 Cart에 존재한다면 데이터를 수정한다.
    Cart.updateOne(
      // 1
      { goodsId: goodsId }, 
      // 2
      { $set: { quantity: quantity } 
    }
    (1) Cart에서 goodsId가 goodsId인 데이터를 찾아서
    (2) 그 안의 quantity 값을 quantity로 수정

장바구니 상품 삭제 API

  1. 삭제는 DELETE 메소드. 그 외에는 위와 동일하다.
  2. 역시 위와 동일하다 (quantity는 필요하지 않기 때문에 body로 보내지 않고 req.body도 사용하지 않는다)
  3. goodsId의 상품이 있다면 Cart에서 해당 데이터를 삭제한다.

routes/carts.js

const express = require("express");
const router = express.Router();

// 1
const Cart = require("../schemas/cart");
const Goods = require("../schemas/goods");

// 2
// 장바구니 조회 API
router.get("/carts", async (req, res) => {
  // 3
  const carts = await Cart.find({});
  
  // 4
  const goodsIds = carts.map((cart) => {
    return cart.goodsId;
  });

  // 5
  const goods = await Goods.find({ goodsId: goodsIds });

  // 6
  const results = carts.map((cart) => {
    return {
      quantity: cart.quantity,
      goods: goods.find((item) => item.goodsId === cart.goodsId),
    };
  });

  // 7
  res.json({
    carts: results,
  });
});

module.exports = router;

장바구니 조회 API

  1. 필요한 스키마 모델을 가져온다. (Cart, Goods)
  2. 조회 목적이니까 GET 메소드를 구현한다.
  3. Cart의 전체 데이터를 가져온다.
  4. 전체 데이터에서 goodsId만 가져와서 새로운 배열에 저장한다.
  5. Goods에서 goodsIds에 포함되는 goodsId만 가져와서 새로운 배열에 저장한다.
  6. 각 cart 항목에 대해 quantity와 goods 키로 이루어진 객체를 results 배열에 저장한다. (goods 키에는 위의 goods 배열(5)의 id와 cart의 id와 일치하는 goods를 할당한다)
  7. resuts를 키-객체로 반환한다.

app.js

const express = require("express");
const app = express();
const port = 3000;
const goodsRouter = require("./routes/goods.js");
// 1
const connect = require("./schemas");

// 2
connect();

// 3
app.use(express.json());

app.use("/api", [goodsRouter]);

app.listen(port, () => {
  console.log(port, "포트로 서버가 열렸어요!");
});
  1. schemas 폴더의 index.js를 require하는데, 이때 폴더만 지정해줘도 알아서 index.js를 require하게 된다.
  2. connect 함수를 호출해 MongoDB에 연결한다.
  3. routes/goods.js의 POST 메소드에서 body를 사용할텐데 이를 위해 json 미들웨어를 전역에 불러온다.
profile
COYG🔴⚪

0개의 댓글