[데이터베이스] MVC Cmarket Database - Models(2)

윤후·2022년 3월 13일
0

Section 3

목록 보기
28/41

Sprint풀이


현재 orders 테이블을 보면, id, user_id, total_price, created_at이 들어가 있는 것을 확인할 수 있다.
orders의 id는 PK로 자동생성되며 created_at도 마찬가지도 생성된 날짜에 따라서 자동으로 생성된다.

user_id와 total_price를 controller에서 들어온 값으로 넣어주기 위해 해당쿼리문을 아래와 같이 작성한다.

const queryString = `INSERT INTO orders (user_id, total_price) VALUES (?, ?)`

query에 들어갈 sql을 작성했다. 이제 두번째 인자로 들어갈 해당 값들을 적어주면 되겠다.
앞에서 말한 것과 같이 orders테이블에 들어갈 user_id의 값은 Controller에서 들어오는 userId의 값이 들어갈 것이고, total_price의 값은 Controller에서 들어오는 totalPrice가 될 것이다.

이후 세번째 인자로 err와 result를 핸들링 해줄 수 있는 콜백함수를 넣으면 되겠다.

db.query(queryString, [userId, totalPrice], (error, result) =>{   
     return callback(error, result)
   })
}

사실, 이 이후에 result의 값이 orders테이블에 값들이 추가되어 반환되는줄 알았다. 하여 result에 어떤 결과 값들이 반환되는지 찾아보기 위하여 console.log를 찍어 보았다.

하지만 출력되는 값은 undefined. 무엇이 문제인지 알 수 없었다. 그러다가 urclass에 해답이 있다는 것을 발견했다.

Example

var mysql = require('mysql');

var con = mysql.createConnection({
  host: "localhost",
  user: "yourusername",
  password: "yourpassword",
  database: "mydb"
});

con.connect(function(err) {
  if (err) throw err;
  console.log("Connected!");
  var sql = "INSERT INTO customers (name, address) VALUES ?";
  var values = [
    ['John', 'Highway 71'],
    ['Peter', 'Lowstreet 4'],
    ['Amy', 'Apple st 652'],
    ['Hannah', 'Mountain 21'],
    ['Michael', 'Valley 345'],
    ['Sandy', 'Ocean blvd 2'],
    ['Betty', 'Green Grass 1'],
    ['Richard', 'Sky st 331'],
    ['Susan', 'One way 98'],
    ['Vicky', 'Yellow Garden 2'],
    ['Ben', 'Park Lane 38'],
    ['William', 'Central st 954'],
    ['Chuck', 'Main Road 989'],
    ['Viola', 'Sideway 1633']
  ];
  con.query(sql, [values], function (err, result) {
    if (err) throw err;
    console.log("Number of records inserted: " + result.affectedRows);
  });
});

The Result Object

{
  fieldCount: 0,
  affectedRows: 14,
  insertId: 0,
  serverStatus: 2,
  warningCount: 0,
  message: '\'Records:14  Duplicated: 0  Warnings: 0',
  protocol41: true,
  changedRows: 0
}

참조
Node.js MySQL Insert into

위의 예제의 결과에서 Result Object를 볼 수 있었다. 현재, post가 제대로 되고 있지 않기 때문에 객체가 제대로 보여지지 않는 것이라고 생각했다.

이제 result객체를 찾았지만, 내가 원하는 부분을 해결하지 못했다. 스키마의 그림처럼, INSERT INTO와 JOIN을 사용하여 데이터를 모두 가져오는 것인줄 알았다.

하지만 생각해보니 이는 틀린 생각이었다. 왜냐하면 get요청으로 저장되어 있는 테이블 정보를 JOIN하여 가져올 텐데, post를 수행하는 곳에서 JOIN이 필요한가에 대한 의구심이 들었기 때문이다. 사실 post를 수행하는 곳에서는 JOIN이 필요하지 않다. 그저 INSERT INTO로 해당 테이블에 값을 저장해두기만 하면 되는 것이다.

그렇다면, 위에서 orders테이블에 정보를 저장했던것 처럼 schema로 연결되어 있는 orders_items도 같은 방법으로 query문을 날려주어야 하는 것이다.

  • ? 갑자기 orders_items에 query는 왜 날려? 할 수 있다. 왜 날릴까?

이부분은 Client파일에 구성된 UI 데이터를 보면 확실하게 알 수 있겠다. 해당 주문서에는 user_id와 total_price로만 구성되어 있지 않다. 주문서에 담긴 item이 무엇인지, item의 그림과 item의 갯수, item의 가격 등등이 필요하다. 즉, schema를 보면 알 수 있듯 items의 정보 또한 필요한 것이다.

  • 하지만 여기서 또 의구심이 든다. 아니, 데이터를 가져오는 것은 get에서 이루어 지는 것이라며? 그럼 데이터를 get에서 가져오면 되는 것이지 왜 post에서 사용하려고 하는거야?

그렇다. 데이터를 가져오기 위해서는 get을 사용하여 query를 날리고 결과를 받는 것이다. 하지면 여기서 보면 사용자가 선택하여 변경할 수 있는 값들이 몇개가 보인다. 무엇이 있을까?

주문서에 담길 item은 워낙 종류가 다양하기 때문에 사용자에 따라 담기는 물품이 달라질 수 있다. 또한, 선택한 item에 수량 또한 변경할 수 있다. 즉, item의 id, item의 quantity는 동적으로 변하는 값들이고 나머지 item의 사진, item이 가지고 있는 고유한 price, 이름은 사용자에 의해 바뀌지도 않고, 바뀌어서도 안되는 정적인값들이다.

위의 동적인 값들은 schema를 보면, orders_items의 테이블에 저장되어야 할 값들이란걸 알 수 있다. 하여 orders와 items의 join table 역할을 하고 있는 order_items 테이블에 해당 동적인 값들을 넣어주어야 하는 것이다.

자 그럼, controller에서 한번의 post요청으로 두 번의 query요청을 수행해야 하는 것이다. 한번의 요청에 query가 두번 실행이 될 수 있는지가 의문이 든다.

⇒ 알아보니 한 번의 query문에 두번이상의 요청이들어갈 수 있다.

참조
Node.js에서 MySQL 다중쿼리 방법
Node.js에서 MySQL 다중쿼리 에러 핸들링

그 다음 INSERT INTO로 데이터를 넣기 전에 각 테이블이 어떻게 만들어져 있는지 GUI를 먼저 살펴보자.

  • orders의 테이블 GUI

  • order_items 테이블 GUI

  • schema

위의 3개의 그림을 보면 orders 테이블과 order_items 테이블이 PK와 FK로 연결되어 있는 걸 볼 수 있다. 위에서 말한것과 같이 query코드를 작성해보면

`INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`

이와 같은 query문을 작성할 수 있겠다.

사실, 이 부분에서 많은 걸 틀려 헤맸던 기억이 있다. 위와 같은 코드가 아닌, 아래와 같은 코드를 사용 했었다.

`INSERT INTO order_items (id, item_id, order_quantity) VALUES (?, ?, ?)`

order_items의 id는 사실 PK로 테이블의 값이 추가되면 자동으로 생성되는 값인데, PK를 생각하지 못하고 그냥 id값만 넣어 order_id의 테이블 값이 null값으로 나오게 되었었고, 계속된 고민 끝에 null을 페어분이 잡아주셔서 넘어갈 수 있었다.

그렇다면 여기서 order_items의 order_id의 값과, orders의 id값이 서로 일치하게 하려면 어떻게 해야할까?
또한, 생각해보면 한 주문(order)에 여러 아이템이 들어가야 할텐데, 한개의 id로 여러 아이템을 넣는 방법은 없을까? (order_items의 order_id가 같고, item_id와 order_quantity가 다른경우를 말하는 것.)

사실 위의 방법을 해결하기 위해서는 앞에서 잠깐 말한 result 객체를 이용하면 된다.

Example

var mysql = require('mysql');

var con = mysql.createConnection({
  host: "localhost",
  user: "yourusername",
  password: "yourpassword",
  database: "mydb"
});

con.connect(function(err) {
  if (err) throw err;
  console.log("Connected!");
  var sql = "INSERT INTO customers (name, address) VALUES ?";
  var values = [
    ['John', 'Highway 71'],
    ['Peter', 'Lowstreet 4'],
    ['Amy', 'Apple st 652'],
    ['Hannah', 'Mountain 21'],
    ['Michael', 'Valley 345'],
    ['Sandy', 'Ocean blvd 2'],
    ['Betty', 'Green Grass 1'],
    ['Richard', 'Sky st 331'],
    ['Susan', 'One way 98'],
    ['Vicky', 'Yellow Garden 2'],
    ['Ben', 'Park Lane 38'],
    ['William', 'Central st 954'],
    ['Chuck', 'Main Road 989'],
    ['Viola', 'Sideway 1633']
  ];
  con.query(sql, [values], function (err, result) {
    if (err) throw err;
    console.log("Number of records inserted: " + result.affectedRows);
  });
});

The Result Object

{
  fieldCount: 0,
  affectedRows: 14,
  insertId: 0,
  serverStatus: 2,
  warningCount: 0,
  message: '\'Records:14  Duplicated: 0  Warnings: 0',
  protocol41: true,
  changedRows: 0
}

result 객체를 살펴보면 여러 값들이 객체로써 반환되는 것을 볼 수 있다. INSERT INTO를 사용하여 여러 테이블에 여러 값을 삽입하기 위해 VALUES(?, ?, ?)와 같은 방법을 사용하였다. 그렇다면 위에서 사용한 VALUES ?와 무엇이 다를까?

var sql = "INSERT INTO customers (name, address) VALUES (?, ?)";

위의 코드는 예시의 VALUES의 값이 들어갈 때, 한줄 한줄이 id값을 가지고 들어간다는 차이점이 있다. 즉, name과 address 테이블에 “”john”, highway”이 삽입되고, id 값으로 1이 부여되고, 다음 name과 address 테이블에 “Peter”, ”Lowstreet”이 삽입되고, id값으로 2가 부여되는 것이다.

위의 쿼리문을 날릴때에는 데이터를 배열( [ ] )로 감싸서 query문을 날려야 한다.

var sql = "INSERT INTO customers (name, address) VALUES ?";

반면 위의 코드는 name, address 테이블에 한 개의 id를 가지고 예제 데이터가 모두 삽입되는 코드이다. 즉, name과 address 테이블에 “”john”, highway”이 삽입되고, id 값으로 1이 부여되고, 다음 name과 address 테이블에 “Peter”, ”Lowstreet”이 삽입되고, id값으로 1이 부여되는 것이다. 하나의 id에 여러 값들이 들어갈 수 있는 것이다.

여기서 result객체가 받는 값중 id에 해당하는 값이 바로 insertId이다. 이 값이 곧, order_items의 order_id값이 되는 것이다.

자, 이제 모든 궁금증은 해결이 되었다고 볼 수 있다. 처음 query를 날리고 그에 대한 result객체의 insertId로 order_items의 order_id를 정해주면 되겠다.

post: (userId, orders, totalPrice, callback) => {

  const queryString = `INSERT INTO orders (user_id, total_price) VALUES (?, ?)`

  const  QueryResult = function(something) { db.query(queryString, [userId, totalPrice], (error, result) =>{
	    console.log(result)
	    return something(error, result)
	  })}

  console.log(orders)
  // [ { quantity: 1, itemId: 5 }, { quantity: 1, itemId: 4 } ]
  

  return QueryResult((error, result) => {
    const querySt =  `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`

    console.log(result.insertId)
    
    let ordersMapping = orders.map((item) => {
      return [result.insertId, item.itemId, item.quantity]
    })

    console.log(ordersMapping)

    return db.query(querySt, [ordersMapping], (error, result) => {

      return callback(error, result);
    })
  })

    }
  },

사실 위와 같이 복잡하게 코드를 만들지 않아도 된다. QueryResult라는 변수를 한번 더 만들었지만, query안에 한번 더 query문을 작성하는게 코드가 깔끔해 보이기도 할 것이다.

참조
함수 표현식과 함수 선언식

현재 post의 요청으로 return받고 있는 값은 callback(error, result)이다. 이 값은 다시 controller의 index.js의 models.orders.post로 받게 될 것이다. 해당 코드를 다시 한번 가져와 보겠다.

post: (req, res) => {
      const userId = req.params.userId;
      const { orders, totalPrice } = req.body;

      if(!userId || !orders || !totalPrice){
        return res.status(400).json({'please':'check userId'})
        
        // ! models에 orders가 undefined 일경우를 대비하여 콜백함수를 끝내기 위해서 return을 써주어야 함.
      }
      models.orders.post(userId, orders, totalPrice,(err, result) => {
        if(err){
          res.status(500).send('Internal Server Error')
        } else{
          res.status(201).send({orders : orders, totalPrice : totalPrice})
        }
      })
      // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
    },

models/index.js와 controller/index.js를 비교해보면 매개변수, 즉 파라미터의 순서와 callback함수가 일치하고 있는 것을 볼 수 있다. 혹시나 해서 말하지만, controller에서 가져온 models.orders.post의 models는

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

위와 같이 require로 models를 참조하고 있다.

자, 이제 어려운 부분은 모두 끝났다. 우리가 삽입했던 테이블을 JOIN 하여 가져오는 일만 남은 것이다. query에서 정보를 가져오기 위해서는 SELECT를 사용해야할 것이고, 이는 get에서 이루어져야 할 것이다.

get: (userId, callback) => {
      // TODO: 해당 유저가 작성한 모든 주문을 가져오는 함수를 작성하세요
      const queryString = `SELECT orders.id, orders.total_price, orders.created_at, items.name, items.price, items.image, order_items.order_quantity 
      FROM orders
      JOIN order_items ON orders.id=order_items.order_id
      JOIN items ON order_items.item_id=items.id
      WHERE orders.user_id=?`

      db.query(queryString, [userId],(error, result) => {
        callback(error, result);
      })
      
    }

처음 SELECT * 를 사용해서 모든 데이터를 가져오는 것인 줄 알았는데, 모든 데이터가 아닌 정해진 값이 urclass에 있었다.

[
  {
    "id": 1, // orders 테이블의 id
    "created_at": "2021-02-19T04:34:11.000Z",    
    "total_price": 7800,
    "name": "칼라 립스틱", 
    "price": 2900,
    "image": "../images/lip.jpg",
    "order_quantity": 1,
  },
  {
    "id": 1,
    "created_at": "2021-02-19T04:34:11.000Z",
    "total_price": 7800,
    "name": "뜯어온 보도블럭",
    "price": 4900,
    "image": "../images/block.jpg",
    "order_quantity": 1,
  },
  // ...여러 개의 주문내역
];

위의 값들을 보면 get으로 query문을 실행할 sql문이 보일것이다.

profile
궁금한걸 찾아보고 공부해 정리해두는 블로그입니다.

0개의 댓글

관련 채용 정보