Skip to content

Latest commit

 

History

History
789 lines (639 loc) · 17.8 KB

File metadata and controls

789 lines (639 loc) · 17.8 KB

목차

  1. Waht is MongoDB?

1. What is mongoDB

  • NoSQL을 대표하는 데이터베이스
  • JSON 형태로 데이터를 저장한다.

2. 프로젝트에 mongoDB 적용하기

  • mongodb cluster 가입하기
    https://cloud.mongodb.com/ : 클라우드 데이터 베이스를 생성하기
  • mongodb+srv://kooks7:<password>@cluster0-s8bpd.gcp.mongodb.net/test?retryWrites=true&w=majority

처음 세팅

  1. $ npm i --save mongodb

  2. app.js 에 기존에 있던 'sequealize' 코드 지우기

    const path = require('path');
    
    const express = require('express');
    const bodyParser = require('body-parser');
    
    const errorController = require('./controllers/error');
    
    const app = express();
    
    app.set('view engine', 'ejs');
    app.set('views', 'views');
    
    const adminRoutes = require('./routes/admin');
    const shopRoutes = require('./routes/shop');
    
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(express.static(path.join(__dirname, 'public')));
    
    // 이 함수는 즉시 실행되는 것이 아니라 요청이 들어오면 실행됨으로 서버 실행 후 실행된다.
    // req.user라는 객체를 생성한다.
    app.use((req, res, next) => {
      // User.findByPk(1)
      //   .then(user => {
      //     req.user = user;
      //     next();
      //   })
      //   .catch(err => console.log(err));
    });
    
    app.use('/admin', adminRoutes);
    app.use(shopRoutes);
    
    app.use(errorController.get404);
    
    app.listen(4000);
  3. util/database.js 에서 mongodb 연결하고 app.js 에 가져오기

    • uilt/database.js

      const mongodb = require('mongodb');
      const MongoClient = mongodb.MongoClient;
      
      const mongoConnect = callback => {
        MongoClient.connect(
          'mongodb+srv://kooks7:123123a@cluster0-s8bpd.gcp.mongodb.net/test?retryWrites=true&w=majority'
        )
          .then(client => {
            console.log('connected!!');
            callback(client);
          })
          .catch(err => {
            console.log(err);
          });
      };
      
      module.exports = mongoConnect;
    • app.js

      const mongoConnect = require('./util/database');
      
      mongoConnect(client => {
        console.log(client);
        app.listen(4000);
      });

    models 수정

    이러한 패턴으로 사용하자!!!!

    • models에서 mongoDB에 계속 연결을 하기 위해 uilt/database.js 수정

      const mongodb = require('mongodb');
      const MongoClient = mongodb.MongoClient;
      
      let _db;
      
      const mongoConnect = callback => {
        MongoClient.connect('mongodb://localhost:27017/test')
          .then(client => {
            console.log('connected!!');
            _db = client.db();
            callback(client);
          })
          .catch(err => {
            console.log(err);
            throw err;
          });
      };
      
      const getDb = () => {
        if (_db) {
          return _db;
        }
        throw 'No database found!';
      };
      
      exports.mongoConnect = mongoConnect;
      exports.getDb = getDb;
    • app.js 수정

      const mongoConnect = require('./util/database').mongoConnect;
      
      mongoConnect(() => {
        app.listen(4000);
      });
    • models.product.js 수정

      const getDb = require('../util/database').getDb;
      
      class Product {
        constructor(title, price, description, imageUrl) {
          this.title = title;
          this.price = price;
          this.description = description;
          this.imageUrl = imageUrl;
        }
      
        save() {
          const db = getDb();
          return db.collection('products')
            .insertOne(this)
            .then(result => {
              console.log(result);
            })
            .catch(err => {
              console.log(err);
            });
        }
      }
      
      module.exports = Product;

    add-product 수정하기

  • controllers/admin.js 에 postAddProduct 수정하기

    exports.postAddProduct = (req, res, next) => {
      const title = req.body.title;
      const imageUrl = req.body.imageUrl;
      const price = req.body.price;
      const description = req.body.description;
      const product = new Product(title, price, description, imageUrl);
      product
        .save()
        .then(result => {
          console.log('Created Product');
          res.redirect('/');
        })
        .catch(err => {
          console.log(err);
        });
    };
  • 우리 홈페이지에서 새로운 product를 입럭하면 Compass에서 볼 수 있다.

Shop page 수정

  • models/product.js 에서 데이터베이스에 있는 모든 데이터 가져오는 fetchAll 메서드 만들기

    class Product {
      constructor(title, price, description, imageUrl) {
        this.title = title;
        this.price = price;
        this.description = description;
        this.imageUrl = imageUrl;
      }
    
      save() {
        const db = getDb();
        return db
          .collection('products')
          .insertOne(this)
          .then(result => {
            console.log(result);
          })
          .catch(err => {
            console.log(err);
          });
      }
    
      static fetchAll() {
        const db = getDb();
        // promise를 반환하지 않고 cursor를 반환한다. cursor는 일종의 object이다.
        return db
          .collection('products')
          .find()
          .toArray()
          .then(products => {
            console.log(products);
            return products;
          })
          .catch(err => {
            console.log(err);
          });
      }
    }
  • controllers/shop.js 에 getProducts 함수 수정

    exports.getProducts = (req, res, next) => {
      Product.fetchAll()
        .then(products => {
          res.render('shop/product-list', {
            prods: products,
            pageTitle: 'All Products',
            path: '/products'
          });
        })
        .catch(err => {
          console.log(err);
        });
    };

product 하나 가져오는 함수 수정하기

  • models/product.js 수정하기

     static findById(prodId) {
        const db = getDb();
    
        return db
          .collection('products') 
         // mongoDB는 Id 값을 특별한 object로 비교하기 때문에 이렇게 해줘야 한다.
          .find({ _id: new mongodb.ObjectID(prodId) })
          .next()
          .then(product => {
            console.log(product);
            return product;
          })
          .catch(err => {
            console.log(err);
          });
      }
    }

    이렇게 수정하는게 더 나아보인다.

     static findById(prodId) {
        const db = getDb();
    
        return db
          .collection('products')
          .findOne({ _id: new mongodb.ObjectId(prodId) })
          .then(product => {
            // console.log(product);
            return product;
          })
          .catch(err => {
            console.log(err);
          });
      }
    }

Update 기능 추가하기

  • controllers/admin.js 에 있는 getEditProduct함수 수정하기

    exports.getEditProduct = (req, res, next) => {
      const editMode = req.query.edit;
      if (!editMode) {
        return res.redirect('/');
      }
      const prodId = req.params.productId;
      Product.findById(prodId)
        .then(product => {
          if (!product) {
            return res.redirect('/');
          }
          res.render('admin/edit-product', {
            pageTitle: 'Edit Product',
            path: '/admin/edit-product',
            editing: editMode,
            product: product
          });
        })
        .catch(err => {
          console.log(err);
        });
    };
  • models/product.js 수정하기

    // constructor에 id 추가하기
    constructor(title, price, description, imageUrl, id) {
        this.title = title;
        this.price = price;
        this.description = description;
        this.imageUrl = imageUrl;
        this._id = id ? new mongodb.ObjectId(id) : null; // 여기서 mongodb object로 바꿔주기
      }
    
    // 기존에 존재하면 업데이터 하고 없으면 생성
    save() {
        const db = getDb();
        let dbOp;
        if (this._id) {
          // update the product
          dbOp = db
            .collection('products')
            .updateOne({ _id: new mongodb.ObjectId(this._id) }, { $set: this });
        } else {
          dbOp = db.collection('products').insertOne(this);
        }
        return dbOp
          .then(result => {
            console.log(result);
          })
          .catch(err => {
            console.log(err);
          });
      }
  • controllers/admin.js 수정

    // 함수 
    exports.postEditProduct = (req, res, next) => {
      const prodId = req.body.productId;
      const updatedTitle = req.body.title;
      const updatedPrice = req.body.price;
      const updatedImageUrl = req.body.imageUrl;
      const updatedDesc = req.body.description;
    
    exports.postEditProduct = (req, res, next) => {
      const prodId = req.body.productId;
      const updatedTitle = req.body.title;
      const updatedPrice = req.body.price;
      const updatedDesc = req.body.description;
      const updatedImageUrl = req.body.imageUrl;
      console.log(prodId);
    
      const product = new Product(
        updatedTitle,
        updatedPrice,
        updatedDesc,
        updatedImageUrl,
    	prodId
      );
      product
        .save()
        .then(result => {
          console.log('Updated');
          res.redirect('/admin/products');
        })
        .catch(err => {
          console.log(err);
        });
    };

Delete 기능 추가하기

  • models/product.js 에 deleteById 메서드 추가

      static deleteById(prodId) {
        const db = getDb();
    
        return db
          .collection('products')
          .deleteOne({ _id: new mongodb.ObjectId(prodId) })
          .then(result => {
            console.log('Delete!');
          })
          .catch(err => {
            console.log(err);
          });
      }
    }
  • controllers/admin.js 수정

    exports.postDeleteProduct = (req, res, next) => {
      const prodId = req.body.productId;
      Product.deleteById(prodId)
        .then(() => {
          res.redirect('/admin/products');
        })
        .catch(err => console.log(err));
    };

User 생성하기

  • models/user.js 생성하기

    const mongodb = require('mongodb');
    const getDb = require('../util/database').getDb;
    
    class User {
      constructor(username, email) {
        this.name = username;
        this.eamil = email;
      }
      save() {
        const db = getDb();
        return db.collection('users').insertOne(this);
      }
    
      static findById(userId) {
        const db = getDb();
        return db
          .collection('users')
          .findOne({ _id: new mongodb.ObjectId(userId) });
      }
    }
    
    module.exports = User;
  • comapss에 수동으로 users 컬렉션 생성해주고 app.js에 하드코딩으로 유저 넣기

    app.use((req, res, next) => {
      User.findByPk('5e4bab8f7f305654f0adac0f')
        .then(user => {
          req.user = user;
          next();
        })
        .catch(err => console.log(err));
      next();
    });
  • 제품 생성할 때 생성한 User 정보 넣기 위해 models/product 수정

      constructor(title, price, description, imageUrl, id, userId) {
        this._id = id ? new mongodb.ObjectId(id) : null;
        this.title = title;
        this.price = price;
        this.description = description;
        this.imageUrl = imageUrl;
        this.userId = userId;
      }
  • controllers/admin.js 수정

    exports.postAddProduct = (req, res, next) => {
      const title = req.body.title;
      const imageUrl = req.body.imageUrl;
      const price = req.body.price;
      const description = req.body.description;
      const product = new Product(
        title,
        price,
        description,
        imageUrl,
        null,
        req.user._id
      );
      product
        .save()
        .then(result => {
          console.log('Created Product');
          res.redirect('/');
        })
        .catch(err => {
          console.log(err);
        });
    };

Cart 기능 추가하기

  • 각각의 유저는 각자의 카트를 가지므로 model/user.js 클래스에 카트 담는 메서드 넣기

     addToCart(product) {
        // const cartProduct = this.cart.items.findIndex(cp => {
        //   return cp._id === product._id;
        // });
         
        // Cart에 아무것도 담겨 있지 않을 때
        const updatedCart = {
          items: [{ productId: new mongodb.ObjectId(product._id), quantity: 1 }]
        };
        const db = getDb();
        return db
          .collection('users')
          .updateOne(
            { _id: new mongodb.ObjectId(this._id) },
            { $set: { cart: updatedCart } }
          );
      }
  • app.js 데이터베이스에서 가져온 user 정보로 새 User 인스턴스 만들기

    app.use((req, res, next) => {
      User.findById('5e4bab8f7f305654f0adac0f')
        .then(user => {
          req.user = new User(user.name, user.email, user.cart, user._id);
          next();
        })
        .catch(err => console.log(err));
    });
  • controllers/shop.js 에 postCart 함수 재 작성

    exports.postCart = (req, res, next) => {
      const prodId = req.body.productId;
      Product.findById(prodId)
        .then(product => {
          return req.user.addToCart(product);
        })
        .then(result => {
          console.log(result);
        });
    };
  • modles/user.js 에 addToCart 함수 수정 ( 카트에 제품 추가하는 기능)

      addToCart(product) {
        // cart에 제품 있는지 조회 ? 양수 : -1
        const cartProductIndex = this.cart.items.findIndex(cp => {
          return cp.productId == product._id;
        });
        let newQuantity = 1;
        const updatedCartItems = [...this.cart.itmes];
    
        // cart에 제품 있으면
        if (cartProduct >= 0) {
          newQuantity = this.cart.items[cartProductIndex].quantity + 1;
          updatedCartItems[cartProductIndex].quantity = newQuantity;
    
          // cart에 제품 없으면
        } else {
          updatedCartItems.push({
            productId: new mongodb.ObjectId(product._id),
            quantity: newQuantity
          });
        }
        const updatedCart = {
          items: updatedCartItems
        };
        const db = getDb();
        return db
          .collection('users')
          .updateOne(
            { _id: new mongodb.ObjectId(this._id) },
            { $set: { cart: updatedCart } }
          );
      }
  • models/user.js 카트 보여주는 기능 추가

     getCart() {
        const db = getDb();
        const productsIds = this.cart.items.map(i => {
          return i.productId;
        });
        return db
          .collection('products')
          .find({ _id: { $in: productsIds } })
          .toArray()
          .then(products => {
            return products.map(p => {
              return {
                ...p,
                quantity: this.cart.items.find(i => {
                  return i.productId.toString() === p._id.toString();
                }).quantity
              };
            });
          });
      }
  • controllers/shop.js 수정

    exports.postCart = (req, res, next) => {
      const prodId = req.body.productId;
      Product.findById(prodId)
        .then(product => {
          return req.user.addToCart(product);
        })
        .then(result => {
          console.log(result);
        });
    };

Cart 삭제 기능 추가

  • models/user.js delete 함수 추가

     deleteItemFromCart(productId) {
        const updatedCartItems = this.cart.items.filter(item => {
          return item.productId.toString() !== productId.toString();
        });
        const db = getDb();
        return db
          .collection('users')
          .updateOne(
            { _id: new mongodb.ObjectId(this._id) },
            { $set: { cart: { items: updatedCartItems } } }
          );
      }
  • controllers/shop.js 수정

    exports.postCartDeleteProduct = (req, res, next) => {
      const prodId = req.body.productId;
      req.user
        .deleteItemFromCart(prodId)
        .then(result => {
          res.redirect('/cart');
        })
        .cathc(err => {
          console.log(err);
        });
    };

Order 기능 추가

  • models/user.js 에 addOrder() 추가

    addOrder() {
        const db = getDb();
        return this.getCart()
          .then(products => {
            const order = {
              items: products,
              user: {
                _id: new mongodb.ObjectId(this._id),
                name: this.name
              }
            };
            return db.collection('orders').insertOne(order);
          })
          .then(result => {
            this.cart = { items: [] };
            return db
              .collection('users')
              .updateOne(
                { _id: new mongodb.ObjectId(this._id) },
                { $set: { cart: { items: [] } } }
              );
          });
      }
  • controllers/shop.js 에 postOrder 수정

    exports.postOrder = (req, res, next) => {
      let fetchedCart;
      req.user
        .addOrder()
        .then(result => {
          res.redirect('/orders');
        })
        .catch(err => {
          console.log(err);
        });
    };

Order 페이지 수정

  • models/user.js getOrder 함수 수정

      getOrders() {
        const db = getDb();
        return db
          .collection('orders')
          .find({ 'user._id': new mongodb.ObjectId(this._id) }) //''를 이용해 중첩된 데이터를 찾음
          .toArray();
      }
  • controllers/shop.js 수정

    exports.getOrders = (req, res, next) => {
      req.user
        .getOrders()
        .then(orders => {
          res.render('shop/orders', {
            path: '/orders',
            pageTitle: 'your Orders',
            orders: orders
          });
        })
        .catch(err => console.log(err));
    };