コード状態12週/CLARketデータベース


入る前は史上最も難しいスプレーだったので文章が長かったですが….🙈🙉🙊
今回のSprintショッピングモールアプリCmarketは、習ったMVCモードで作成したアプリです.
それぞれのパターンがどのような役割を果たしているのかを考えて、スプレーを行うと、より解きやすくなります.
次に、データベースを使用して構築し、実装されたCmarketがオブジェクトのメモリ形式ではなく持続的なデータを持つことができます.
今回のスプレーで解決すべき問題 1.データベースからプロジェクトをインポート -->レポートの例として使用できるように作成されています 2.受注履歴をデータベースに保存 3.データベースからの受注履歴のインポート

シンプルなアプリケーションドライバの概要

  • クライアントから「受注履歴」(orderlist)ページに接続すると、次のコード
  • が実行されます.
      useEffect(() => {
        dispatch(
          fetchData(`http://localhost:4000/users/${userId}/orders`, setOrders)
        );
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, []);
    
  • http://localhost:4000/users/${userId}/ordersこのような要求があれば、要求はサーバに転送されます.

  • server > app.jsに移動後appjsでは,次のコードにより,まず/要求がルーティングを用いて分岐する.
  • const indexRouter = require('./routes');
    app.use('/', indexRouter);

  • server > routers > index.まずjsです.
    ここでusersに再分岐します

  • user.jsでは、これらの応答は、対応するコントローラで応答処理を行えばよいことを示しています.
  • const controller = require('./../controllers');
    router.get('/:userId/orders',controller.orders.get);
    router.post('/:userId/orders',controller.orders.post);
  • コントローラに注文のgetを検索し、対応する関数
  • を実行します.

    1.初期設定


    1)mysqlの設定(モード、シードファイルのチェック)
    # mysql 접속 후 cmarket 데이터베이스 생성
    mysql> create database cmarket;
    
    미리 구성되어 있는 Cmarket 스키마를 기반으로 MySQL에 cmarket 데이터베이스의 테이블을 생성
    다음 명령어로 cmarket 데이터베이스에 테이블을 생성
    
    # CLI(터미널) 환경에서 (mysql 접속 종료 한 뒤) 레포지토리에 진입하여 커맨드를 입력
    # cmarket 데이터베이스에 구성되어 있는 스키마 복사, -D는 데이터베이스 지정 옵션
    mysql -u root -p < server/schema.sql -Dcmarket;
    # 복사된 테이블에 데이터 복사
    mysql -u root -p < server/seed.sql -Dcmarket;
    2) .envファイルの設定
    .env.サンプルファイル->名前をenvファイル/msqlに変更してパスワードを入力

    2. server


    ここで大切なのは npm testを入力すると、サーバが自動的に実行され、テストが行われます。 劉語班は事前に通知した。 サーバーをシャットダウンし、テストを開始する必要があります。

    1) users router 파일이 존재해야 합니다このテストボックスは何がほしいか分かりません.
    ここで、テストケース(server/spec/server-spec.js)を見てみましょう.
      it('users router 파일이 존재해야 합니다', () => {
          let hasUsersRouter = fs.existsSync('./routes/users.js');
          expect(hasUsersRouter).to.be.true;
        });
    
    routionsフォルダのuser.jsファイルを生成するのは簡単な問題です.2 GET /users는 orders controller의 get 메소드를 실행합니다 3 POST /users는 orders controller의 post 메소드를 실행합니다server -> app.js
    サーバがクライアントにリクエストを送信したと仮定すると、次のコードでリクエストが見つかります.
    indexRouterに移動
    const indexRouter = require('./routes');
    // routes 라는 폴더를 지정했지만 기본으로 그 안의 index.js 폴더로 이동하게 된다 
    app.use('/', indexRouter);
    
    indexRouterはrouter->indexです.jsに移動
    ここではそれぞれのルーターで接続すればいいです.
    ルータがユーザーに接続されていないため
    新しく作成されたユーザー.jsには何もないのでitems.jsを参考に書いてもいいです.
    routers -> 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;
    
    routers -> users.js
    
    const router = require('express').Router();
    const controller = require('./../controllers');
    
    // POST /users/1/orders 
    // GET /users/1/orders
    // 테스트 케이스를 보면 users엔드포인트 뒤에 들어오는 형식이
    // 있기 때문에 똑같이 맞춰줘야 컨트롤러로 분기 할 수 있다
    router.get('/:userId/orders', controller.orders.get);
    router.post('/:userId/orders', controller.orders.post);
    
    module.exports = router;
    このように作成すると、実行後応答がテストケースに合格するため、まずコントローラから実行後の一時応答を発行します.
    server -> controllers -> index.js
    res.end()に記入し、一時的に応答を送信すると、テストケースは合格します.
    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;
          // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
          res.end()
        },
        post: (req, res) => {
          const userId = req.params.userId;
          const { orders, totalPrice } = req.body;
          // TODO: 요청에 따른 적절한 응답을 돌려주는 컨트롤러를 작성하세요.
          res.end()
        },
      },
    };
    

    1)受注履歴をデータベースに保存する


    1) POST /users/:userId/orders 요청에서 클라이언트가 잘못된 요청을 했을 경우 상태코드 400을 보내야합니다. 2) POST /users/:userId/orders 요청에 성공했을 경우 상태코드 201을 보내야합니다.specフォルダのテストケースから見ると、クライアントがエラーリクエストを発行する場合はpostの本体にすべてのデータを送信せず、totalPriceのみを送信する場合
       it('POST /users/:userId/orders 요청에서 클라이언트가 잘못된 요청을 했을 경우 상태코드 400을 보내야합니다.', (done) => {
          chai
            .request(app)
            .post('/users/1/orders')
            .send({ totalPrice: 79800 })
            .end((err, res) => {
              expect(res).to.have.status(400);
              done();
            });
        });
    
        it('POST /users/:userId/orders 요청에 성공했을 경우 상태코드 201을 보내야합니다.', (done) => {
          chai
            .request(app)
            .post('/users/1/orders')
            .send({
              orders: [
                { itemId: 1, quantity: 2 },
                { itemId: 2, quantity: 5 },
              ],
              totalPrice: 79800,
            })
            .end((err, res) => {
              expect(res).to.have.status(201);
              done();
            });
        });
      });
    server -> controllers -> index.js
    まずpostを書きましょう
    注文がない場合、または合計価格がない場合は、クライアントのエラーであるため、400が送信されます.
    残りの場合は、データベースに受注履歴を格納します.
    モデル関数を実行します.
    サーバがデータベースに要求を送信すると、どのような応答があるか分からないため、非同期で処理する必要があります.server -> models -> index.jsに行くと、リクエストの結果がコールバックで送信されるので、ここでコールバックを定義できます.
    const models = require('../models');
    
    module.exports = {
        post: (req, res) => {
          const userId = req.params.userId;
          const { orders, totalPrice } = req.body;
          if(!orders || !totalPrice){
            return res.status(400).send('Bad Request');
          }else{
            models.orders.post(userId, orders, totalPrice, (err, result) =>{
              if(err){
                res.status(500).send('Internal Server Error');
              } else {
                res.status(201).send('Order has been placed.');
              }
            })
            res.status(201);
          }
          res.end()
        },
      },
    };
    
    次に、モデルで、ユーザーの受注要求をデータベースに保存するクエリーを作成します.
    postリクエストを受信したときに受注リクエストをデータベースに保存するには、次の手順に従います.
    表にレコードを保存するかどうかを考えるべきだ.orders 는 주문결과 , order_items는 장바구니다パラメータから見るとpost:(userId,orders,totalPrice,callback)はユーザID、注文結果、総額を受信しており、それぞれデータベースに追加する必要がある
    上記のテストケースは、次のコードに示されています.
    orders.itemId, orders.クエリーを数量で送信する表示
    orders: [
                { itemId: 1, quantity: 2 },
                { itemId: 2, quantity: 5 },
              ]
    
    データベースを見ながら考えてみましょう

    Ordersテーブルにレコードを保存する orders->user idとtotal priceの追加 受注のid(pk)の取得 order itemsテーブルにレコードを保存する order items->order id、item id、order quantityの追加 ordersのid(pk)をorder itemsのorder id(FK)に入れる 接続が完了するには、この順序でクエリー関数を記述する必要があります

    server -> models -> index.js
    const db = require('../db');
    
    module.exports = {
        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(err){
              return callback(err);
            }
            // 여기서 방금 넣어준 레코드의 id를 가져오기 위해 
            // result를 콘솔에 찍어보자 ! 
            if(result){
              console.log('result',result)
            }
            const orderId = result.insertId;
            // 유저가 주문하는 갯수가 지정되어 있지 않기 때문에 한번의 쿼리로 여러개의 레코드를 생성 할 때 ? 하나를 이용한다 
            const queryString = `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`;
            //const params = [[orderId, itemId, order_quantity], [], []] 
            const params = [orders.map((order) => {
              return [orderId, order.itemId, order.quantity];
            })];
            db.query(queryString, params, (err, result) => {
              if(err){
                return callback(err)
              }
              return callback(null, result);
            })
          })
        }
      },
    };
    
    このクエリーを作成するときは、クラスのヒントを参照してください.

    Insert Intoで返されるオブジェクトからid(pk)値を取得できます.
    上のリンクは、insert intoを使用して返されるオブジェクトを定義しています.insertIdを使用してid値を取得できます.
    if(result){
      console.log('result',result)
    }
    --> 결과 
    /*result OkPacket {
      fieldCount: 0,
      affectedRows: 1,
      insertId: 2,
      serverStatus: 2,
      warningCount: 0,
      message: '',
      protocol41: true,
      changedRows: 0
    }*/

    ユーザの注文数が指定されていないため、1回のクエリで複数のレコードが生成された場合?ひとつ利用して
    (1つしか購入できないし、複数も購入できるので、リクエストされたアイテム履歴を1つずつ実行してinsert intoを記録することができますが、クエリーとして実行するために配列別にグループ化されたボディにデータを送信することが望ましいです)
    const db = require('../db');
    
    module.exports = {
      orders: {
        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(err){
              return callback(err);
            }
            console.log('orders =====', orders);
            
            const orderId = result.insertId;
            // 유저가 주문하는 갯수가 저장되어 있지 않기 때문에 한번의 쿼리로 여러개의 레코드를 생성 할 때 ? 하나를 이용한다 
            const queryString = `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`;
            //const params = [[orderId, itemId, order_quantity], [], []] 
            const params = [orders.map((order) => {
              const result = [orderId, order.itemId, order.quantity];
              console.log('order =====',order)
              console.log('result =====',result)
              return result
            })];
            db.query(queryString, params, (err, result) => {
              if(err){
                return callback(err)
              }
              return callback(null, result);
            })
          })
        }
      },
    };
    
    コンソールでの受注(受注リスト)と個別受注(受注リスト)の印刷
    コンソールでorderを撮ると、オブジェクトの形で入るので、下の資料と同じ形で入れる必要がありますので、mapを使って並べる形にする必要がありますし、上で受け取ったorderIdも含める必要があるので、最初のパラメータとして入れればいいです.
    orders ===== [ { itemId: 5, quantity: 1 }, { itemId: 6, quantity: 2 } ]
    order ===== { itemId: 5, quantity: 1 }
    result ===== [ 2, 5, 1 ]


    この操作が完了すると、受注履歴がデータベースに格納されます.
    今は注文書の明細を持って来ればいいです.

    2)データベースからの受注履歴のインポート



    クラスから見ると、注文の詳細を取得するには上のフィールドが必要だと教えてあげます.
    彼に基づいて必要なフィールドを書いて、それらを統合することができます.
    const db = require('../db');
    
    module.exports = {
      orders: {
        get: (userId, callback) => {
          // TODO: 해당 유저가 작성한 모든 주문을 가져오는 함수를 작성하세요
          const queryString = `SELECT orders.id, orders.created_at, orders.total_price, items.name, items.price, items.image, order_items.order_quantity FROM items
          INNER JOIN order_items ON (order_items.item_id = items.id)
          INNER JOIN orders ON (orders.id = order_items.order_id)
          WHERE (orders.user_id = ?)`;
    
          // 현업에서는 긴 쿼리문을 쓰는 경우가 많기 때문에 AS를 이용해서 축약해서 쓰는 연습이 필요하다 
          const queryString = `
            SELECT o.id, o.created_at, o.total_price, i.name, i.price, i.image, oi.order_quantity FROM items AS i
            INNER JOIN order_items AS oi ON (oi.item_id = i.id)
            INNER JOIN orders AS o ON (o.id = oi.order_id)
            WHERE (o.user_id = ?)`;
    
          db.query(queryString, [userId], (err,result) =>{
            if(err){
              return callback(err)
            } else {
              return callback(null, result)
            }
          })
        }
    };
    
    最後に、コントローラに注文のgetを記入すると、スプレーが終わります.
    const models = require('../models');
    
    module.exports = {
      orders: {
        get: (req, res) => {
          const userId = req.params.userId;
          models.orders.get(userId, (err, result) =>{
            if(err){
              res.status(500).send('Internal Server Error');
            } else {
              res.json(result);
            }
          })
        },
    };
    

    閉じてからサーバーを開けてもオーダーリストがずっと維持されているアプリが見えます!!!

    他の同期の方も理解しづらいかもしれませんが、スプレーコメントの時間が終わった後、質問時間がほぼ30分を超えていて、これは私には理解しにくいので、あと5回やる必要があるようです。