56, 57일차 [데이터베이스] MVC

2021. 10. 16. 15:56
반응형

2021. 10. 16 목요일

1. Today's Key Points!🔑

  • MVC

2. 정리해보자!🧹

MVC(Model-View-Controller)? 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 사용되는 소프트웨어 디자인 패턴이다. 소프트웨어의 비지니스 로직과 화면을 구분하는데 중점을 둔다. 이러한 관심사 분리를 통해 효율적으로 코드를 관리 할 수 있다.

  • Model : 데이터의 정보를 가지고 있다. 자신이 데이터를 갖고 있던지, 데이터베이스와 연결이 되어서 데이터를 갖고올 수 있다. 이 데이터를 가지고 컨트롤러와 이야기를 한다.
  • View : 레이아웃과 화면을 처리한다. 앱의 데이터를 보여주는 방식을 정의한다. 표시할 데이터를 컨트롤러에게 요청해서 모델로부터 받아서 다시 컨트롤러가 전달해준다.
  • Controller : 명령을 모델과 뷰 부분으로 라우팅 한다. 앱의 사용자로부터의 입력에 대한 응답으로 Model 또는 View를 업데이트하는 로직을 포함한다.

3. Sprint 과제 복기!🧐

쇼핑몰 애플리케이션 데이터베이스를 구축해야하는 과제이다. 클라이언트에서 보내는 요청을 데이터 베이스에 저장해서 쇼핑몰 애플리케이션이 영속적인 데이터를 가질 수 있도록 해야한다. 유저가 장바구니에서 구매하기 버튼을 누르면 주문 목록, 그 아이템들에 대한 정보들이 데이터베이스에 기록이 되어야 한다. 그리고 해당 url에 접속을 하면 목록들이 보여지도록 해주어야 한다.

app.js에서 라우팅 연결이 어떻게 시작되는지 분기가 어떻게 나뉘어져있는지를 잘 파악해서 users에 대한 분기점을 나누어 줘야한다.

//app.js

const express = require("express");
const indexRouter = require("./routes");
const cors = require("cors");
const morgan = require("morgan");
const app = express();
const port = 4000;

app.use(
  morgan("      :method :url :status :res[content-length] - :response-time ms")
);
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use("/", indexRouter);

module.exports = app.listen(port, () => {
  console.log(`      🚀 Server is starting on ${port}`);
});

app.use("/", indexRouter); 여기서 보면 indexRouter 라우터 모듈을 로드하고 있다. 그래서 routes 폴더에 index.js로 가보면,

// ./routes/index.js

const express = require('express');
const router = express.Router();
const itemsRouter = require('./items');
const usersRouter = require('./users');

// Endpoint에 따라 적절한 Router로 연결해야 한다.
router.use('/items', itemsRouter);
router.use('/users', usersRouter);
//실수했던 부분 router.use('/users/:userId/orders', usersRouter); 이렇게 여기서 엔드포인드를 모두 잡아주는 실수를 범했다.

module.exports = router;

'/items' 는 itemsRouter 라우터 모듈을 로드하고있고, '/users' 는 usersRouter 라우터 모듈을 로드하고 있다.

// ./routes/users.js

const router = require("express").Router();
const controller = require("./../controllers");

router.get("/:userId/orders", controller.orders.get);
router.post("/:userId/orders", controller.orders.post);

module.exports = router;

users.js에서 보면 결국 /users/:userId/orders에 대한 get요청을 받으면 controller.orders.get을 호출할 것이고, /users/:userId/orders에 대한 post요청을 받으면 controller.orders.post을 호출할 것이다. 그럼 controller에 가서 어떤 함수가 호출되는지를 살펴보자.

const models = require("../models");

module.exports = {
  items: {
    get: (req, res) => {
      models.items.get((error, result) => {
        if (error) {
          res.status(500).send("Internal Server Error");
        } else {
          res.status(200).json(result);
        }
      });
    },
  },
  orders: {
    get: (req, res) => {
      const userId = req.params.userId;
      if (!userId) { //사용자가 잘못된 요청을 보냈을 경우
        return res.status(400).send("Error 400"); //return을 통해서 400에러를 보내주고 함수를 종료시켜야 한다.
      }
      models.orders.get(userId, (error, result) => { // Model과 Controller가 대화하는 부분
        if (error) {
          res.status(500).send("Internal Server Error");
        } else {
          res.status(200).json(result); //Model과 대화를 통해 얻어낸 결과를 클라이언트의 요청에 대한 응답을 보내준다.
        }
      });
    },
    post: (req, res) => {
      const userId = req.params.userId;
      const { orders, totalPrice } = req.body;

      if (!orders || !totalPrice) { //사용자가 잘못된 요청을 보냈을 경우
        return res.status(400).send("Error 400");
      }
      models.orders.post(userId, orders, totalPrice, (error, result) => {
        if (error) {
          res.status(500).send("Internal Server Error");
        } else {
          res.status(201).json("success post!");
        }
      });
    },
  },
};

Model에게 클라이언트로 부터 요청 받은것을 모델에게 넘겨줘서 그것을 바탕으로 모델이 필요한 정보를 가져오게된다. 그럼 models가 어떻게 구성되어있는지를 보자. 그 전에 각 테이블의 관계도 살펴보자.

const db = require("../db");

module.exports = {
  items: {
    get: (callback) => {
      // Cmarket의 모든 상품을 가져오는 함수
      const queryString = `SELECT * FROM items`;

      db.query(queryString, (error, result) => {
        callback(error, result);
      });
    },
  },
  orders: {
    get: (userId, callback) => {
      // 해당 유저가 작성한 모든 주문을 가져오는 함수
      const queryString = `SELECT O.id, O.created_at, O.total_price, I.name, I.price, 
                           I.image, OI.order_quantity FROM orders O
                            JOIN order_items OI ON O.id = OI.order_id
                            JOIN items I ON OI.item_id = I.id 
                            WHERE O.user_id = ?`;
      const params = [userId];
      db.query(queryString, params, (err, result) => {
        callback(err, result);
      });
    },
    post: (userId, orders, totalPrice, callback) => {
      // 해당 유저의 주문 요청을 데이터베이스에 생성하는 함수
      const queryString = `INSERT INTO orders (user_id, total_price) VALUES (?, ?);`;
      const params = [userId, totalPrice];
      db.query(queryString, params, (err, result) => {
        if (result) {
          const queryString2 = `INSERT INTO order_items (item_id, order_quantity, order_id) VALUES ?;`;
          const values = orders.map((order) => [
            order.itemId,
            order.quantity,
            result.insertId,
          ]);
          return db.query(queryString2, [values], (err, result) => {
            callback(err, result);
          });
        }
        callback(err, result);
      });
    },
  },
};

query함수를 사용할때 INSERT INTO에 대한 문서는 여기를 통해 확인할 수 있다. 이 query함수를 통해서 Model이 Database와 대화해서 필요한 정보를 가져오고 그것을 Controller에게 넘겨준다.

이렇게 Model, Controller, View를 나눠서 코드를 짜보는 연습을 했고, Database에 영속적으로 데이터를 가질수 있도록 해보았다. 위처럼 해주었을 때의 결과를 아래에서 볼 수 있다.

 

 

반응형
LIST

BELATED ARTICLES

more