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); // 첫번째 분기점으로 /로 시작할 경우 indexRouter에 따라 분기지점이 세분화됨.
module.exports = app.listen(port, () => {
console.log(`Server is starting on ${port}`);
});
const express = require("express");
const router = express.Router();
const itemsRouter = require("./items");
const usersRouter = require("./users");
router.use("/items", itemsRouter);
router.use("/users", usersRouter);
module.exports= router;
const router = require("express").Router();
const controller = require("./../controllers");
router.get("/", controller.items.get);
module.exports = router;
const router = require("express").Router();
const controller = require("./../controllers");
router.get("/:userId/orders", controllers.orders.get);
router.post("/:userId/orders", controllers.orders.post);
module.exports= router;
라우팅
모든 라우터는 server/routes/index.js를 진입점으로 여기고, 여기서 endpoint에 따라 다시 분기된다.
index.js에서 /items일때, /users일때로 분기를 나눠주고 있고, items.js에서는 get 요청이 있을때 controller.items.get 메소드가 실행되도록 되어있다. users.js에서는 get과 post 요청에 따라 controllers.orders.get, controllers.orders.post 메소드가 실행되도록 되어있다.
const models = require("../models");
module.exports = {
items: {
get: (req, res) => {
models.items.get((err, result) => {
if(err){
return res.status(500).send("Internal Server Error");
}else{
return res.status(200).json(result);
}
});
},
},
orders: {
get: (req, res) => {
const userId = req.params.userId;
if(!userId){
return res.status(400).end();
}
models.orders.get(Number(userId), (err,result) => {
if(err){
return res.status(400).end();
}else{
return res.status(200).json(result);
}
});
},
post: (req, res) => {
const userId = req.params.userId;
const {orders, totalPrice} = req.body;
if(!orders || !totalPrice){
return res.status(400).end();
}else{
models.orders.post(Number(userId), orders, totalPrice, (err, result) => {
if(err){
return res.status(404).end();
}else{
return res.status(201).json(result);
}
)};
}
}
}
}
const db = require("../db");
module.exports = {
items: {
get: (callback) => {
const sql = `SELECT * from items`
db.query(sql, (err,result)=>{
callback(err,result);
})
},
},
orders: {
get: (userId, callback) => {
const sql = `
SELECT orders.id, name, image, price, total_price, order_quantity
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(sql, params, (err,result)=>{
callback(err, result);
});
},
post: (userId, orders, totalPrice, callback) => {
//orders 테이블에 먼저 데이터를 삽입한다
//그 다음에 order_items 테이블에 데이터를 삽입한다
//이유는 orders의 id를 FK로 갖는 order_items.order_id를 order_items 테이블에 데이터를 삽입할때 쓸 수 있기 때문
const sql1 = `INSERT INTO orders (user_id, total_price) VALUES (?,?)`
const params = [userId, totalPrice];
db.query(sql1, parmas, (err, result) => {
if(err) callback(err, null);
else{
const sql2 = `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`
const params2 = orders.map(order => {
return [result.insertId, order.itemId, order.quantity]
});
return db.query(sql2, [params2], (err,result) => {
if(err) callback(err, null);
else{
callback(null, result);
}
});
}
});
}
}
}
models.orders.post
endpoint: /users/1/orders
userId === 1
orders === [{itemId: 1, quantity:2}, {itemId: 2, quantity:5}]
totalPrice === 79800
첫 번째 쿼리요청에서는 orders 테이블에 데이터를 삽입하고 에러가 발생하지 않을 경우, 다음 쿼리요청이 진행된다. (orders 테이블에는 데이터가 삽입된 상태)
두번째 쿼리 요청에서는 order_items 테이블에 데이터를 삽입한다. 이때, 2개의 데이터가 삽입된다. 이것을 bulk insert라고 한다.
쿼리문(sql2
)에 ?
는 하나만 작성한다.
기존에 하나의 데이터만 삽입할 때에는 INSERT INTO (fields) VALUES (?,?,...?)
로 필드 개수만큼 물음표를 작성했다. 하지만, 여러개의 데이터를 한번에 삽입할 때에는 삼중배열의 형태이기 때문에 물음표를 하나만 작성한다.
쿼리요청의 두번째 인자로 전달되는 params는 삼중배열의 형태로 작성한다.
orders는 객체들을 요소로 갖는 배열이다. map
메소드로 각 객체에 저장된 정보를 요소로 갖는 배열을 리턴시켜주었다. 그 결과 배열 안에 배열들이 있는 이중배열의 형태가 되었고, 쿼리요청의 두번째 인자로 한번더 배열로 감싸주어 삼중배열의 형태로 만든다.
result.insertId
INSERT INTO
요청을 보냈을때, 데이터베이스에서 보내는 result 객체에는 여러 정보가 담겨있는데 그중 insertId
는 삽입된 테이블의 pk에 해당하는 id값이다. 따라서 첫번째 쿼리문인 INSERT INTO
를 성공했을때 전달받은 result 객체의 insertId
를 두번째 쿼리문에서는 orders.id 대신 사용할 수 있다.