Cmarket Database Sprint

Jelkov Ahn·2022년 1월 26일
1

데이터베이스 MVC

목록 보기
2/2

목표

  • MySQL에 접속해 Cmarket 데이터베이스를 생성해야 합니다.

  • 미리 구성되어 있는 Cmarket 스키마를 기반으로 MySQL에 cmarket 데이터베이스의 테이블을 생성합니다.

  • 웹 서버가 MySQL 데이터베이스에 대해 CRUD 중 Create, Read가 가능해야 합니다.

    • 테스트를 통과하면 Create, Read 부분이 완성됩니다.
    • server/spec/server-spec.js에 정의되어 있는 테스트를 살펴보세요.
    • server/routes 폴더를 보고 필요한 부분을 작성하여 Router 테스트 케이스를 통과합니다.
    • server/controllers/index.js 를 작성하여 Controller 테스트 케이스를 통과합니다.
    • server/models/index.js를 작성하여 모든 테스트 케이스를 통과합니다.
    • npm test 입력 시 자동으로 서버가 실행되고 테스트가 진행됩니다.

1. 스키마, 시드 파일 확인하기

mysql에 접속해서 cmarket 데이터베이스를 생성합니다.

CREATE DATABASE cmarket;

CLI(터미널) 환경에서 레포지토리에 진입하여 커맨드를 입력 - schema 복사

mysql -u root -p < server/schema.sql -Dcmarket

CLI(터미널) 환경에서 레포지토리에 진입하여 커맨드를 입력

mysql -u root -p < server/seed.sql -Dcmarket

  • PATH/TO/YOUR/schema.sql 은 터미널의 현재 위치에서 schema.sql의 위치까지 경로를 지정해야 한다는 의미입니다.
    만약 터미널의 위치가 스프린트 폴더라면, 상대경로만 이용해도 충분하므로 server/schema.sql 을 입력합니다.

  • 잘못된 Sql 작성시 DROP DATABASE IF EXISTS [다시 생성하려는 데이터베이스] CREATE DATABASE [다시 생성하려는 데이터베이스] 으로 해결

BATCH MODE 관련 https://dev.mysql.com/doc/refman/8.0/en/batch-mode.html

2. 서버

(1) npm install 명령어로 서버폴더에 필요한 모듈 설치

  • package.json에 기록된 npm 모듈 mysql
    이 모듈은 Cmarket Server가 MySQL server와 상호작용할 수 있게 합니다.

(2) 비밀번호 '환경변수'로 분리해놓음

  • server/config/config.js에 mySQL과의 연결을 위한 환경 변수 DATABASE_SPRINT_PASSWORD 를 사용하고 있습니다. npm 모듈 dotenv 로 환경 변수를 사용하는 방법을 확인하세요.

  • .env.example 파일을 꼭 확인해주세요. YOUR_MYSQL_PASSWORD 부분에 로컬 PC의 MySQL 비밀번호를 입력하세요. 쌍따옴표(\")로 감싸거나, 세미콜론(;)으로 마무리하지 않아도 됩니다.
    DATABASE_SPRINT_PASSWORD=YOUR_MYSQL_PASSWORD

  • 비밀번호 입력을 완료했으면, 파일에 작성한 내용을 환경 변수로 인식하도록 파일의 확장자명 또는 파일의 이름을 .env로 변경해 주세요.

(3) 명령어 npm start 로 서버를 실행합니다.

  • app.js 파일은 express의 설정을 담당하는 파일입니다. express 는 Node.js 웹 서버의 진입점이자 자바스크립트의 서버 프레임워크 중 하나입니다.
    파일 구조를 파악하고, app.js 에서 라우팅 연결이 어떻게 시작되는지 확인하세요.

3. Router

웹 개발에 있어서 라우터는 컨트롤러로 진입할 수 있게 도와주는 endpoint입니다. 따라서 endpoint에 맞게 적절한 라우터를 작성해야 하고, 해당 라우터에 컨트롤러를 연결해야 합니다. 예를 들어 /items URL로 GET 요청을 보낼 경우, items 컨트롤러의 메소드가 실행되도록 만들 수 있습니다.

이때, endpoint에 따라 분리하여 파일 구성을 할 수도 있습니다.
모든 라우터는 server/routes/index.js를 진입점으로 여기고,
여기서 endpoint에 따라 다시 분기됩니다.

따라서 server/routes/index.js 와 server/routes/items.js 파일을 통해 라우팅 방식에 대해서 함께 확인하기 바랍니다.

4. Model

server/db/index.js 파일에서는 데이터베이스와 서버 인스턴스를 연결하기 위해 mysql 모듈을 사용합니다.
https://www.npmjs.com/package/mysql#establishing-connections

server/models/index.js 파일에는 controller 에서 사용할 orders, items 모델을 정의해야 합니다.
기본적인 구조만 제공되며 server/db/index.js 의 함수를 불러온 뒤, SQL 쿼리문으로 DB의 정보를 처리합니다. 데이터베이스 쿼리는 비동기 요청인 점을 반드시 고려해야 합니다.

Server Sprint

🗄 Cmarket Database

🗺 -------- Cmarket Router
🚀 Server is starting on 4000
✓ users router 파일이 존재해야 합니다
✓ orders controller에는 get, post 메소드가 각각 존재해야 합니다
GET /users/1/orders 200 2 - 5.029 ms
✓ GET /users는 orders controller의 get 메소드를 실행합니다
POST /users/1/orders 201 166 - 42.370 ms
✓ POST /users는 orders controller의 post 메소드를 실행합니다 (46ms)

(1) Router는 서버에 요청이 들어왔을때 분기를 해준다.

<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);
// Router 요청을 한다.

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

(1-1) items에 관련한 endpoint 요청이 오게 되면 itemsRouter로 갑니다.

<routes - index.js>

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

// TODO: Endpoint에 따라 적절한 Router로 연결해야 합니다.
router.use('/items', itemsRouter);

module.exports = router;

(1-2) itemsRouter로 가게되면 controller로 연결은 해준다.

<routes - itmes.js>

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

// GET /items Router와 Controller를 연결합니다.
router.get('/', controller.items.get);

module.exports = router;

(1-3) 여기로 연결이 된다.

<controllers index.js>

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;
      models.orders.get(userId, (error, result)=> {
        if (error) {
          res.status(500).send('Internal Server Error');
        } else {
          res.status(200).json(result);
        }
      })

      // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
    },
    post: (req, res) => {
      const userId = req.params.userId;
      const { orders, totalPrice } = req.body;
      if(!orders){
        return res.status(400).end();
      }else{
      models.orders.post(userId, orders, totalPrice, (error,result) => {
        if (error) {
          console.log(error)
          res.status(200).send('Internal Server Error');
        } else {
          res.status(201).json(result);
        }      
      })
    }
      // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
    },
  },
};

(2) 이와 같이 users도 연결을 해줘야 한다.

<routes index.js>

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

// TODO: Endpoint에 따라 적절한 Router로 연결해야 합니다.
router.use('/items', itemsRouter);
router.use('/users', usersRouter);

module.exports = router;

🕹 -------- Cmarket Controller
GET /items 200 617 - 1.548 ms
✓ GET /items 요청에 성공했을 경우 상태코드 200을 보내야합니다.
GET /users/1/orders 200 2 - 1.608 ms
✓ GET /users/:userId/orders 요청에 성공했을 경우 상태코드 200을 보내야합니다.
POST /users/1/orders 400 - - 0.704 ms
✓ POST /users/:userId/orders 요청에서 클라이언트가 잘못된 요청을 했을 경우 상태코드 400을 보내야합니다.

POST /users/1/orders 201 166 - 27.312 ms
✓ POST /users/:userId/orders 요청에 성공했을 경우 상태코드 201을 보내야합니다.

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;
      models.orders.get(userId, (error, result)=> {
        if (error) {
          res.status(500).send('Internal Server Error');
        } else {
          res.status(200).json(result);
        }
      })

      // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
    },
    post: (req, res) => {
      const userId = req.params.userId;
      const { orders, totalPrice } = req.body;
      if(!orders){
        return res.status(400).end();
      }else{
      models.orders.post(userId, orders, totalPrice, (error,result) => {
        if (error) {
          console.log(error)
          res.status(200).send('Internal Server Error');
        } else {
          res.status(201).json(result);
        }      
      })
    }
      // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
    },
  },
};

✨-------- Cmarket Model
GET /items 200 617 - 0.934 ms
✓ 데이터베이스에 저장된 상품 목록을 가져와야합니다.
POST /users/1/orders 201 166 - 25.909 ms
✓ 주문내역을 데이터베이스에 저장해야합니다.
POST /users/1/orders 201 166 - 43.013 ms
POST /users/1/orders 201 166 - 53.448 ms
GET /users/1/orders 200 622 - 1.261 ms
✓ 데이터베이스에 저장된 주문내역을 가져와야합니다. (104ms)

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

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

      db.query(queryString, (error, result) => {
        callback(error, result);
      });
    },
  },
  orders: {
    get: (userId, callback) => {
      const queryString = `SELECT orders.id, name, image, price, total_price, order_quantity, created_at
      FROM orders
      LEFT JOIN users
      ON users.id = orders.user_id
      LEFT JOIN order_items
      ON orders.id = order_items.order_id
      LEFT JOIN items
      ON items.id = order_items.item_id
      WHERE users.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 ?`
      console.log(queryString);
      const params = [[userId, totalPrice]];
      
      db.query(queryString, [params], (err, result) => {
        if(result){
          const queryString2 = `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`;
          const params2 = orders.map(el => {
            return [result.insertId, el.itemId, el.quantity];
          });
          db.query(queryString2, [params2], (err, result)=>{
            callback(err, result);
          })
        }
        else{        
          callback(err, result)
        }
      });
    }
  }
}  
profile
끝까지 ... 가면 된다.

0개의 댓글