티스토리 뷰
반응형
08. 장바구니 구현 (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);
- 장바구니 목록 조회 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 안에는 아무것이 정상입니다! 이는 장바구니안에 아무것도 아직 추가를 하지 않았기 때문입니다. 😇
- API가 잘 동작하는지 확인해봅니다!
장바구니 구현 (2)
-
- 장바구니에 상품 추가 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" }); });
- http://localhost:3000/api/goods/:goodsId/cart
- URL에 수정을 원하는 goodsId값을 넣어준후 body에 quantity 값을 json형태로 입력한뒤 POST method로 요청을 해줍니다.
- http://localhost:3000/api/goods/:goodsId/cart
- 위와 같이 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 내에서 장바구니가 사라진것을 볼 수 있습니다.
-
- 장바구니의 상품 수량 수정 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 |