[개발심화] SQL, Database - 6주차 (3)

Hong·2022년 10월 22일
0

🏕
우연히 벨로그를 돌다가
부트캠프 과장광고를 싫어한다는 제목으로 글을 썼지만
내용은 부트캠프 출신 개발자들을 일반화하며 결국엔 사람을 비난하고 깎아 내리는 글을 봤다
그 사람의 주장은 일정 부분 동의하고, 일반화는 사람을 큰 에너지 쏟지 않고 효율적으로 구분해내는 방법임을 나도 알고 있다 하지만
일을 잘하더라도 말이 거칠고 배려가 없으며 생각의 깊이가 짧은 사람과는 일하기 싫다(내 스타일이 아니다)


[지금 뒤처져 있다고 생각하는 모든 사람들에게]

https://www.youtube.com/watch?v=g4vD2OWJlag&list=WL&index=3&t=15s
나는 역경을 딛고 일어나 무엇인가를 성취해낸 사람들의 이야기로부터 알 수 없는 울림을 느낀다 삶의 큰 고비들을 수차례 넘어왔기 때문인지 모르겠다


✒️
이 문제를 생각하며 나는 부트캠프에 왜 지원했으며 왜 이 교육과정이 왜 나에게 꼭 필요한지, 어떤 마음을 가지고 이 커리큘럼을 따라가고 있는지는 다음에 적을 수 있는 여유가 생기면 적어보겠다

뿐만 아니라 블록체인의 미래 가능성, 내가 이 산업에 뛰어들어도 되는 것일까에 대한 의문 등에 대해서도 글을 남겨놓을 것이다




🤓
수요일부터 금요일 까지는
-sql문법
-스키마 디자인(Schema design)
-Node.js에서 데이터베이스를 사용하는 방법
에 대해서 배웠다

SQL에 대해서 새로 배우기는 했지만 결국 월요일과 화요일에 http와 express프레임워크로 서버를 구축했을 때처럼 서버를 만들고 앱을 구동시켜야 했는데
이번 과제에서 가장 흥미로웠던 부분은 depths가 3단계인 앱구조를 이해할줄 알아야 했다는 것이다



📄
SQL(Structured Query Language)은 데이터베이스 언어로, 주로 관계형 데이터베이스에서 사용한다
데이터베이스에 query(저장되어 있는 데이터를 필터하기 위한 질의문)를 보내 원하는 데이터를 가져오거나 삽입할 수 있다


SQL 기본 쿼리문

https://velog.io/@ye050425/SQL-SQL-%EC%A0%95%EB%A6%AC

SQL 쿼리문 퀴즈

https://www.w3schools.com/quiztest/quiztest.asp?qtest=SQL
https://www.w3schools.com/sql/sql_exercises.asp

매우 많은 쿼리문이 있었지만 기본적으로 위의 블로그와
쿼리문 퀴즈를 통해 처음에 코드를 익혔다



💿 관계형 데이터베이스

관계형 데이터베이스의 종류

1:1

1:N

N:N

N:N 방법은 Join table을 만들어 관리한다

이 테이블을 통해 어떤 고객이 몇 개의 여행 상품을 구매했는지 또는, 어떤 여행 상품이 몇 명의 고객을 가지고 있는지 등을 확인할 수 있다

뿐만 아니라 자기참조 관계도 있다




🦞 과제를 살펴보자

⛰ 이 앱에는 백엔드 부분이 3의 뎁스로 이뤄져 있다

[1단계]

app.js에서 app.use("/", indexRouter)를 통해 클라이언트가 서버에 접속하면 indexRouter를 실행한다(indexRouter는 routes폴더로부터 왔다).
그러면 이제 여기서 다시 분기가 나눠진다
/items, /orders 두개의 url로 클라이언트가 get, post요청을 보내면 처리해주는 방법을 적는다

[2단계]

근데 이 처리해주는 itemsRouter, userRouter는 controller로부터 온다

[3단계]

근데 또 이 controller는 models로부터 온다
결국 App.js > Routes(items, users) > controllers > models 형식으로 들어가게 된다
우리는 마지막인 models에서 query문을 작성하고 원하는 데이터를 database에서 가져오거나(get) 추가한다(post)


제일 중요한 앱의 기능을 알아보자

app.js에서 client의 요청을 1차적으로 받으면 routes에서 분기를 나눠준다음 분기에 따라 database에 쿼리문을 보내주는 모듈을 사용해서 cmarket이라는 이름의 database에 데이터를 추가하거나 조회한다
이 앱은 쇼핑리스트가 있고 각 상품을 장바구니에 추가할 수 있는 기능이 있다


과제를 진행하기 전에 사전작업이 좀 필요했다

우선 macOS를 사용하고 있다면 homebrew를 통해 mysql을 깔아주자
(설치 방법은 생략함니다)


brew services start mysql

sql서버에 접속한다


mysql -u root -p

mysql로 들어간다(여기서부터 mysql언어로 작성해야함, 나갈려면 exit, 그리고 ;쳐줘야 문장이 끝남을 인식함 enter만 치면 안됨)


CREATE DATABASE cmarket;

cmarket이라는 데이터 베이스를 만든다


USE cmarket;

cmarket이라는 데이터 베이스를 내가 사용하겠다고 알려준다(이걸 해줘야지 해당 database를 쓸 수 있음)


mysql -u root -p < server/seed.sql -D cmarket

server폴더에 seed.sql이라는 파일을 -D cmarket database와 연결한다

brew services stop mysql

mysql을 다썼다면 exit해준다음 터미널에서 위 명령어를 써주자 그래야 mysql프로세스가 내려간다




🗺 Database Schema


https://dbdiagram.io/home 을 이용해서 작성했다



app.js

const express = require("express");
const indexRouter = require("./routes");
const cors = require("cors");
const morgan = require("morgan");
const app = express();
const port = process.env.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);

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

module.exports = server;


index.js

(router로써 item.js와 users.js를 관리함)

const express = require('express');
const router = express.Router();
const itemsRouter = require('./items'); //usersRouter는 items폴더로 부터 온다
const usersRouter = require('./users'); //usersRouter는 users폴더로 부터 온다


router.use('/items', itemsRouter); //client가 url items로 요청을 보내면 itemRouter를 처리해준다
router.use('/users', usersRouter); //client가 url users로 요청을 보내면 usersRouter를 처리해준다

module.exports = router;


item.js (router)

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

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

module.exports = router;


users.js (router)

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

// GET /users Router와 Controller를 연결합니다.
router.get('/:userId/orders', controller.orders.get);
router.post('/:userId/orders', controller.orders.post); //:userID가 나중에 params로 들어간다(콜론뒤에 오는자리는 param으로 들어감)

module.exports = router;


controller.js

const models = require('../models');
//이 파일은 client가 서버로 request를 보냈을 때 어떻게 request를 처리하고 response를 돌려줘야할지 정하는 역할을 한다.
//model의 함수를 들고와서 백엔드의 맨 앞에서 client와 상호작용하는 부분이라고 생각하면 된다

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);
        }
      });
    },

    post: (req, res) => {
      const userId = req.params.userId;
      const { orders, totalPrice } = req.body; //구조분해 할당임, 유어클래스에 있는 cmarketAPI를 보면 body에 orders와 totalprice가 있는것을 알 수 있다. 우리는 이것을 코드에 변수로 적용하기 위해서 끌어온 것이다.
      
      if(!orders || !totalPrice) res.status(400).send('Fault'); //request에 orders나 totalPrice가 들어오지 않으면 에러처리 해준다
      else{
        models.orders.post(userId, orders, totalPrice, (error, result) => {
          if (error) {
            res.status(500).send('Internal Server Error');
          } else {
            res.status(201).json(result);
          }
        });
      }
    },
  },
};


models.js

(백엔드 뎁스의 제일 아래에서 들어온 요청에 따라 database에 쿼리문을 보내준다)

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

//이 파일은 client로부터 요청이 들어왔을 떄 get, post요청에 대해 어떻게 처리해 줄것인지 함수를 적는 파일이다(controller의 프로세스를 담당한다고 생각하면 된다).
//그리고 database에 query를 보내는 역할도 한다

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

      db.query(queryString, (error, result) => { //데이터 베이스(db)에 queryString(get요청이 들어왔을 때 보내줄 query문)을 보내준다
        callback(error, result); //이 callback함수는 에러를 관리한다
      });
    },
  },

  orders: {
    get: (userId, callback) => { //여기의 callback은 models.orders.get의 (error, result) => { }가 된다
      const queryString = `SELECT orders.id, orders.created_at, orders.total_price, items.name, items.price, items.image, order_items.order_quantity
                          FROM users
                          JOIN orders ON users.id = orders.user_id
                          JOIN order_items ON orders.id = order_items.order_id
                          JOIN items ON order_items.item_id = items.id
                          WHERE users.id = ${userId}
                          `;
                          //cmarketAPI를 보고 models.order.get이 실행될 때 어떤 데이터를 db에서 가져와야하는지 적어줬다

      db.query(queryString, (error, result) => { //데이터 베이스에 queryString(get요청이 들어왔을 때 보내줄 query문)을 보내준다
        callback(error, result);
      });
    },


    post: (userId, orders, totalPrice, callback) => {
      const queryString = `INSERT INTO orders (user_id, total_Price) VALUES (${userId}, ${totalPrice})`; //orders table에 user_id와 total_Price를 넣어준다

      db.query(queryString, (error, result) => { //데이터 베이스에(db) query문을 날려준다(query문을 통해서 database에 어떠한 요청을한다) 이 줄에서는 queryString에 작성된 query문이 database로 전달되었다. 그래서 result는 orders table에 user_id와 total_Price가 입력된 값이 나올 것이다.
        if(error) return callback(error, null) //여기 callback은 controller의 callback 함수에서 온것이다, response처리를 해준다
        else{
          const sql = `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`; /* ?는 prepared statement 보안때문에 쓴다고 생각하면됨 hacker가 expected data에(?자리에 ${}해서 변수를 직접적으로 넣어준다는 말) DROP TABLE이런 식으로 넣어버리면 table날라감 */
          const param = orders.map((el)=>{
            return [result.insertId, el.itemId, el.quantity]; //요청받은 orders 배열안에 quantity, itemId에 대한 숫자를 들고옴 result.InsertId는 db.query(queryString, (error, result) => { })의 결과값인 result에서 들고옴(여기에는 query문을 통해 걸러진 data가 들어옴)
          });
          console.log(param);
          db.query(sql, [param], (error, result) => {
            callback(error, result);
          });
        }
      });
    }
  },
}


💤
새벽 2시다 잠온다

profile
Notorious

0개의 댓글