티스토리 뷰

항해/주특기 1주차

[node js] 3주차 -3

타올이 2022. 1. 23. 19:52
반응형

08. 장바구니 구현 (1)

    1. 장바구니를 구현하기 위해서는 어떤 기능들이 필요할까요?
    처음에는 잘 감이 안올 수 있겠지만 연습하다보면 어떤 기능이 필요할지 알 수 있습니다! 오늘은 그 첫걸음!
  • 장바구니 기능을 구현하려면 보통 아래의 기능이 필요합니다. 
    1. 장바구니 목록 조회
    2. 장바구니에 상품 추가
    3. 장바구니의 상품 제거
    4. 장바구니의 상품 수량 수정
    생각보다 많죠? 오늘 전부 구현해야 하니 마음 단단히 먹고 시작해봅시다!
    1. 장바구니 모델 작성
    • 이제 상품을 장바구니에 담기 위한 모델을 작성합니다.
    • 어떤 데이터를 넣어야 할까요?
      • 어떤 상품을 담았는지, 몇개를 담았는지 알 수 있어야 합니다!
    • /schemas/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);
      
    1. 장바구니 목록 조회 API 작성
    • 첫째로 장바구니의 데이터를 찾아와줍니다.
    • 그리고 장바구니 데이터베이스에는 goodsId와 quantity 정보밖에 담겨있지 않기 때문에 장바구니에 담겨있는 상품의 아이디에 맞는 상품 정보를 한번 더 가져와 줍니다.
    • 아래의 모양으로 응답하는것이 목적입니다.
{
	"carts": [
		{
			"quantity": 10,
			"goods": {
		    "goodsId": 3,
		    "name": "시원한 콜라3333",
		    "thumbnailUrl": "<https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRk7JqMw7ZYZP4ZW136wcoMTmLzbrMIJzUWb1Dhu9cHwCPp0gA&usqp=CAc>",
		    "category": "drink",
		    "price": 3000
		  },
		},
		{
			"quantity": 3,
			"goods": {
		    "goodsId": 1,
		    "name": "시원한 콜라1",
		    "thumbnailUrl": "<https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRk7JqMw7ZYZP4ZW136wcoMTmLzbrMIJzUWb1Dhu9cHwCPp0gA&usqp=CAc>",
		    "category": "drink",
		    "price": 3000
		  },
		}
	]
}
  • 그럼 routes/carts.js 파일을 생성해서 한번 작성해보세요!
    • /routes/carts.js 예시
  • const express = require("express");
    const Cart = require("../schemas/cart");
    const router = express.Router();
    
    router.get("/carts", async (req, res) => {
      const carts = await Cart.find();
      const goodsIds = carts.map((cart) => cart.goodsId);
    
      const goods = await Goods.find({ goodsId: goodsIds });
    
      const results = carts.map((cart) => {
    		return {
    			quantity: cart.quantity,
    			goods: goods.find((item) => item.goodsId === cart.goodsId)
    		};
      });
    
      res.json({
        carts: results,
      });
    });
    ​
     
    • /app.js 예시
  • const cartsRouter = require("./routes/carts");
    
    app.use("/api", [goodsRouter, cartsRouter]);
    ​
     
    • API가 잘 동작하는지 확인해봅니다!
      • 이전에 Thunder Client로 요청했던것처럼 GET 메소드로 http://localhost:3000/api/carts 를 호출해봅시다.
      • 아래 스크린샷처럼 Status는 200 이 나왔지만 Response 안에는 아무것이 정상입니다! 이는 장바구니안에 아무것도 아직 추가를 하지 않았기 때문입니다. 😇

장바구니 구현 (2)

    1. 장바구니에 상품 추가 API 작성
    이 API는 상품 추가 API와 거의 비슷하게 구현할 수 있습니다!
    • 구현하려면 어떤 값이 필요할까요?
      • goodsId
      • quantity
    • 어떻게 구현하면 될까요?
    • POST 메서드와 /goods/:goodsId/cart 주소에 대응하는 API를 만들어보세요!
    • 이후 받아온 goodsId 값을 통해 이미 장바구니에 상품이 들어가 있는지 확인 후, 존재하면 수량만 수정하고, 존재하지 않는다면 새로운 카트에 상품정보를 새로 생성하도록 작성해볼거예요!
    • /routes/goods.js 예시
  • router.post("/goods/:goodsId/cart", async (req, res) => {
      const { goodsId } = req.params;
      const { quantity } = req.body;
    
      const existsCarts = await Cart.find({ goodsId: Number(goodsId) });
      if (existsCarts.length) {
        return res.json({ success: false, errorMessage: "이미 장바구니에 존재하는 상품입니다." });
      }
    
      await Cart.create({ goodsId: Number(goodsId), quantity: quantity });
    
      res.json({ result: "success" });
    });
    ​

  • 위와 같이 success 메시지가 나온 뒤 Robo3T에서 새로고침을 해주면 아래와 같이 carts document 에 장바구니 데이터가 저장되어 있는것을 확인할 수 있습니다.

  • 이번 API는 장바구니에 상품 추가 API 와 동일한 URI를 사용하게 됩니다.
  • 하지만 이번에는 간단하게 goodsId를 통해 상품이 장바구니 안에 존재한다면 장바구니내에서 상품 정보를 삭제해줍니다.
  • /routes/goods.js 예시
router.delete("/goods/:goodsId/cart", async (req, res) => {
  const { goodsId } = req.params;

  const existsCarts = await Cart.find({ goodsId });
  if (existsCarts.length > 0) {
    await Cart.deleteOne({ goodsId });
  }

  res.json({ result: "success" });
});
더보기

👉 데이터를 지울 때는 보통 DELETE method 를 사용합니다. 지우는 행동에서는 URL 만으로 지워야할 리소스를 명시하는데 충분하다는 이유가 있습니다. 😎

DELETE 메소드를 통해 호출을 하면 아래와 같이 success 메시지와 Robo3T 내에서 장바구니가 사라진것을 볼 수 있습니다.

    1. 장바구니의 상품 수량 수정 API 작성
    • 이번 API는 장바구니에 상품 추가 API와 굉장히 비슷한 코드의 모습을 하고 있습니다.
    • 하지만 이번에는 POST 메소드가 아닌 PUT 메소드를 사용해 데이터를 update 해줄것 입니다.
    • URI는 추가, 삭제 API와 같은 포맷을 가지고 있지만, 이번 API에서는 간단하게 goodsId를 이용해서 상품정보가 장바구니내에 존재한다면 body를 통해 받아온 quantity 값에 맞게 상품 수량을 수정해 줍니다.
    • /routes/goods.js 예시
    • router.put("/goods/:goodsId/cart", async (req, res) => {
        const { goodsId } = req.params;
        const { quantity } = req.body;
      
        const existsCarts = await Cart.find({ goodsId: Number(goodsId) });
        if (existsCarts.length) {
          await Cart.updateOne({ goodsId: Number(goodsId) }, { $set: { quantity } });
        }
      
        res.json({ result: "success" });
      })
      
  • http://localhost:3000/api/goods/:goodsId/cart
  • New Request로 PUT 메소드를 설정해준뒤 goodsId에 맞는 상품의 수량을 body에 담아 요청해 수정해봅니다.
  • 입력한 값과 마찬가지로 수량이 5로 수정되면 성공입니다!

 

app.js

const express = require('express');
// 14) connect 사용하기 위한 작업, index는 생략가능
const connect = require("./schemas")
const { nextTick } = require('process');
const app = express();
const port = 3000;

// connect 실행
connect();

// 4) app.js파일은 goods.js의 존재를 모르기에 적어줌, 확장자는 생략가능
// routers는 폴더명 goods는 파일명, ./는 상대경로 app.js있는 곳 안에서 라는 뜻
const goodsRouter = require('./routes/goods')
// 28) carts router 생성
const cartsRouter = require('./routes/carts')

// // 3) 미들웨어를 사용하는 함수, 순서가 중요!
// app.use((req,res,next)=>{
//     // console.log("미들웨어가 구현됐나?")
//     // 미들웨어사용시 가장 중요한 것, next()를 써야 다음 미들 웨어로 넘어감
//     // next()가 없으면 서버가 끊나지 않는 무한로딩에 빠짐
//     // next();
//     if(req.path==='/test'){
//         // path는 주소창 옆에 나오는 값
//         res.send('테스트 주소로 왔구나!');
//     } else {
//         next();
//     }
// });
const requstMiddleware = (req,res,next)=>{
    // originalUrl은 보낸 Url 그대로 나오는 값
    console.log('Request URL:', req.originalUrl, " - ", new Date());
    // res이 없이 next()를 안쓰면 문제가 커짐
    next();
};

// 18) body에 들어오는 json을 사용할수 있게 도와주는 미들웨어
app.use(express.json());

// log를 남기는 미들 웨어
app.use(requstMiddleware);

// 6) router도 미들 웨어이기 때문에 이곳에 적어줌, /api는 api가 붙었을 때 goodsRouter가 실행된다는 뜻
app.use("/api",[goodsRouter,cartsRouter]);

// 2) get은 응답하는 것
app.get('/',(req,res)=>{
    res.send("Hello world");
});

// 1) listen은 서버를 켜는 것
app.listen(port, ()=>{
    console.log(port, "포트로 서버 연결!")
});

goods.js/router

const express = require('express')
// 16) 내가 만든 스키마를 가져오기, ./는 현재 위치 ../는 상위위치
const Goods = require('../schemas/goods')
// 34) Cart를 가지고 옴
const Cart = require('../schemas/cart')
// router 대문자 주의
const router = express.Router();

// 7) "/"는 "api/"가 숨겨진 것
router.get("/", (req, res) => {
  res.send('this is root page')
});

// 8) 데이터 가져오기
// // 24)지움
// const goods = [
//     {
//       goodsId: 4,
//       name: "상품 4",
//       thumbnailUrl:
//       category: "drink",
//       price: 0.1,
//     },
//     {
//       goodsId: 3,
//       name: "상품 3",
//       thumbnailUrl:
//       category: "drink",
//       price: 2.2,
//     },
//     {
//       goodsId: 2,
//       name: "상품 2",
//       thumbnailUrl:
//       category: "drink",
//       price: 0.11,
//     },
//     {
//       goodsId: 1,
//       name: "상품 1",
//       thumbnailUrl:
//       category: "drink",
//       price: 6.2,
//     },
//   ];

router.get("/goods", async (req, res) => {
  // 24) 쿼리 스트링 만들기
  const {category} = req.query;

  // 21) 기존 api지우기
  // 25) category 쿼리 추가
  const goods = await Goods.find({ category });
  // res.send('this is goods page')
  // 9) 위에 있는 데이터를 그래도 응답해줌
  res.json({
    // 10) goods라는 키에 goods배열 값을 넣어준것
    // 키 값이 똑같으면 goods:goods, -> goods, 약식임
    goods,
  });
});

// 상세 페이지 만들기(한 배열만 조회), 고유한 goodsId이용
// :의 의미는 아무값이나 입력받겠다는 뜻
router.get("/goods/:goodsId", async (req, res) => {
  // 11) goodsId에 접근하는 방법
  const { goodsId } = req.params;

  // 22) goods를 참조하지 않고 직접가져옴
  const [detail] = await Goods.find({ goodsId: Number(goodsId) });

  // console.log(goodsId);
  // // 서버는 응답을 줘야함, 무한 로딩을 피하기 위해 그래서 send를 넣음
  // res.send("goods id 확인용!")

  // 13) 정리
  // const filteredItems = goods.filter((item)=> item.goodsId === Number(goodsId));
  // 23) 지움
  // const [detail] = goods.filter((item)=> item.goodsId === Number(goodsId));

  // 12) 상품데이터를 가져와야함 goods.filter()사용, goods는 배열
  res.json({
    // detail: filteredItems[0]
    detail,
  });
});

// 33) 장바구니 구현 2 수량과 함께 카트에 담는다.
router.post('/goods/:goodsId/cart',async (req,res)=>{
  const { goodsId } = req.params;
  const { quantity } = req.body;

  const existscarts = await Cart.find({ goodsId: Number(goodsId) });
  if (existscarts.length){
    return res.status(400).json({ success: false, erroerMessge: "이미 장바구니에 들어있는 상품입니다."})
  }

  await Cart.create({ goodsId: Number(goodsId), quantity});
  res.json({success: true})
});

// 35) delete 구현
router.delete('/goods/:goodsId/cart',async (req,res) => {
  const { goodsId } = req.params;

  const existscarts = await Cart.find({ goodsId: Number(goodsId) });
  if (existscarts.length){
   await Cart.deleteOne({ goodsId: Number(goodsId) });
  }
  res.json({ success: true });
});

// 36) put 구현
router.put('/goods/:goodsId/cart',async (req,res)=>{
  const { goodsId } = req.params;
  const { quantity } = req.body;

  const existscarts = await Cart.find({ goodsId: Number(goodsId) });
  if (!existscarts.length){
    return res.status(400).json({ success: false, erroerMessge: "장바구니에 해당 상품이 없습니다."})
  }

  await Cart.updateOne({ goodsId: Number(goodsId) },{ $set: { quantity } });

  res.json({ success: true });

});

// 17) 리소스를 생성하는 post, goodId를 생성하는 api
// get을 제외한 모든 메서드는 body를 가져올 수 있음
router.post("/goods", async (req, res) => {
  const { goodsId, name, thumbnailUrl, category, price } = req.body;

  const goods = await Goods.find({ goodsId });
  // 20) goods가 빈 배열일때 넣어주면 됨
  if (goods.length) {
    return res.status(400).json({ success: false, erroerMessge: "이미 있는 데이터입니다." })
  }
  // 21) goods가 빈 값이 아닐때 create를 해준다.
  const createdGoods = await Goods.create({ goodsId, name, thumbnailUrl, category, price });

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


// 5) app.js에서 goods.js의 require를 하려면 모듈로 내보야함
module.exports = router;

 

goods.js/schema

const mongoose = require('mongoose');

// 15) mongoose schema 생성
const goodsSchma = mongoose.Schema({
    goodsId: {
        type: Number,
        required: true,
        unique: true,
    },
    name: {
        type: String,
        required: true,
        unique: true,
    },
    thumbnailUrl: {
        type: String,
    },
    category: {
        type: String,
    },
    price: {
        type: Number,
    },
});

// Goods는 모델이름
module.exports = mongoose.model("Goods",goodsSchma)

index.js

const mongoose = require("mongoose")

// 컨넥트
// 26) category 쿼리 때 undefinde를 무시하라는 말을 넣음
const connect = () => {
    mongoose.connect("mongodb://localhost:27017/spa_mall",{ ignoreUndefined: true }).catch((err)=>{
        // 에러가 뜰경우 콘솔에 err가 찍임
        console.error(err);
    });
};

module.exports = connect;

carts.js

// 27) cart router 생성
const express = require('express')
// 31) Goods모델을 가져와야함 사용하기 위해서
const Goods = require('../schemas/goods')
// 29) cart schema파일가져오기
const Carts = require('../schemas/cart')
const router = express.Router();

router.get("/carts",async(req,res)=>{
    // 30) 목록 조회 api
    const carts = await Carts.find();
    // 31) goods 을 가져옴 근데 조건이 있음
    const goodsIds = carts.map((cart) => cart.goodsId)

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

    // 32) 핵심
    res.json({
        carts: carts.map((cart) => {
            return{
                quantity: cart.quantity,
                goods: goods.find((item)=>{
                    return item.goodsId === cart.goodsId;
                })
            };
        }),
    })
});

module.exports = router;

cart.js

// 26) cart schema 생성
const mongoose = require('mongoose');

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

module.exports = mongoose.model("Cart",schema);
반응형

'항해 > 주특기 1주차' 카테고리의 다른 글

[node js] 4주차 -1  (0) 2022.01.23
[node js] 3주차 -4  (0) 2022.01.23
[node js] 3주차 -2  (0) 2022.01.23
[node js] 3주차 -1  (0) 2022.01.23
[node js] 2주차 -4  (0) 2022.01.23
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
링크
글 보관함