[TIL] 211128


📝 今日作った

  • 復習-node.js/express/routes/templates/MongoDBとMongoose/CURD
  • 📚 学識


    userセクションに入る前に、これまで学んだことを再確認してください(node.js~ビデオセクション)
    混同または再表示したい部分を詳しく説明し、残りの部分を簡単にまとめてプロセス全体を熟知します.

    1. node.js


  • git init/npm init

  • 端末で梱包する.jsonファイルを含むフォルダに移動したら、npm i expressと入力します.

  • npm i(package.jsonファイルは保存して閉じる必要があります)

  • babel/nodemon[TIL] 211113をインストールするには、「
  • 2. express


    1)サーバの作成

  • express import
  • app作成
  • オープンサーバ
  • import express from "express";
    
    const PORT = 4000;
    
    const app = express();
    
    const handleListening = () => console.log("서버가 열렸다");
    
    app.listin(PORT, handleListening);

    2) GET request & response


    「appの作成」と「サーバを開く」の間にあります.
    appを作成したら、「準備完了」サーバを開きます.
    app.get("route", handler 함수);

    3)ミドルウェア(ミドルウェア)


    (1) next()


    requestとresponseの間にrequestが続く関数.
    expressがリクエストをチェックすると、handler関数が順次呼び出されます.
    本明細書にnext()がある場合、この関数はプロセッサであり、ミドルウェアである.
    const middleware = (req, res, next) => {
      console.log("이건 미들웨어다");
      next();
    };
    
    const handleHome  = (req, res, next) => {
      return res.end();
    };
    
    app.get("route", middleare, handleHome);

    (2) app.use()


    グローバルミドルウェアを作成します.
    app.use()はappです.get()の前にある必要があります.
    const middleware = (req, res, next) => {
      console.log("이건 모든 요청에 대해 서버가 응답하기 전에 실행되는 글로벌 미들웨어다");
      next();
    };
    
    const handleHome = (req, res, next) => {
      return res.end();
    };
    
    app.use(globalMiddleware);
    app.get("/", handleHome);

    (3) protectionMiddleware


    保護されたページにアクセスできません.
    const privateMiddleware = (req, res, next) => {
      const url = req.url;
      if (url === '/protected') {
        return res.send("<h1>보호된 페이지라 접근할 수 없음.</h1>");
      }
      next();
    };
    
    const handleProtected = (req, res, next) => {
      return res.send("여기는 보호된 페이지 입니다. 환영합니다.");
    };
    
    app.use(privateMiddleware);
    app.get("/protected", handleProtected);

    (4)morganミドルウェア


    コンソールウィンドウにloggerを表示する
    import morgan from "morgan"; // morgan import
    
    const logger = morgan("dev"); // 설정이 "dev"인 morgan 미들웨어를 return 함
    
    app.use(logger); // 글로벌 미들웨어로 사용 설정

    3.ルーター


    routerはurlをグループ化します.

    1)どのデータを使うか


    これに基づいてドメインを考える.
    globalRouter/で始まる
    ビデオルータ/ビデオで始まる
    userRouter/usersで始まる

    2)ルータとコントローラの作成と使用


    まずはサーバーjsファイルに作成します.
    (以下の路線は一部のみで、全てではありません)
    import express from "express";
    
    // (중략)
    
    // 라우터 만들기
    const globalRouter = express.Router();
    const videoRouter = express.Router();
    const userRouter = express.Router();
    
    // 라우터 사용하기
    app.use("/", globalRouter);
    app.use("/videos", videoRouter);
    app.use("/users", userRouter);
    
    // 페이지 만들기
    globalRouter.get("/", home);
    globalRouter.get("/join", join);
    globalRouter.get("/login" login);
    globalRouter.get("/search", search);
    
    videoRouter.get("/:id", watch);
    videoRouter.get("/id/edit", edit);
    videoRotuer.get("/:id/upload", upload);
    videoRotuer.get("/:id/delete", deleteVideo);
    
    userRouter.get("/:id", edit);
    userRouter.get("/:id/delete", deleteUser);
    
    // 컨트롤러 만들기
    const home = (req, res) => {
      return res.send("Home");
    }
    // ( ...이하 생략 )

    3)モジュール化


    ルータフォルダとコントローラフォルダを作成し、各ファイルにルータとコントローラを割り当てます.
    エクスポート&importルータとコントローラ.
    +expressも各ファイルをインポートする必要があります.

    4)urlパラメータ


    urlパラメータを使用してurlで変数を使用できます.
    req.params: { 파라미터 이름: "파라미터 값" }
    req.paramsはurlパラメータ名とその値を含むオブジェクトです.
    // videoRouter.js
    videoRouter("/:id([0-9a-f]{24})", watch);
    videoRouter.jsファイルにurlパラメータ名idを指定します.
    req.params.出力idはurlの:id部分を出力します.
    //- video.pug
    
    mixins video(video)
      div
        h4
          a(href=`/videos/${video.id}`)=video.title
    //- home.pug
    
    extends base
    include mixins/video
    
    block content
      each video in videos 
        +video(video)
      else
        li sorry nothing found
    一方、ビデオ.pugファイルのビデオmixinで書かれたコードに基づいて、
    ホームにリストされている各ビデオのtitleをクリックすると、各ビデオのビデオが表示されます.idを含むurlに移動します.
    この時のビデオビデオはpugファイル内のidパラメータ(:id)の位置にあります.idは
    idパラメータ(:id)はビデオです.idになります.
    req.params.id==idパラメータの値==video.idになります.
    🔥 mongose-Schema中option:id&Mongoose-知らない内容を埋め込む参照
    しかし、私はビデオSchemaとビデオDocumentにidフィールドを作成したことがありません.
    ただし、偽データベーステストで使用したコードがそのままであれば、エラーは発生しません.
    これはどういうことですか.
    →MongoDBは基本的に各ビデオにidというフィールドと値を追加し、一意の識別番号とする.이 _id 필드의 값은 .id 의 형식으로 불러서 사용할 수 있다.

    4.テンプレート(templates)


    1) pug


    URLにアクセスするときに必要なページを表示するようにコントローラを変更する必要があります.
    pugは、これらのビューの作成を支援するテンプレートです.

    (1)ビューエンジン設定

  • pugはview engineに設定されています.
    Expressはpugを使用してHTMLを返します.
  • // server.js
    app.set("view engine", "pug");

    (2)ビューフォルダパスの設定

  • srcフォルダ内にviewsフォルダを作成し、expressがpugファイルを参照するviewsフォルダの場所を設定します.(デフォルトはcwdなので、変更後のみエラーが表示されます)
  • // server.js
    app.set("views", process.cwd() + "/src/views");

    (3)pugファイルの作成


  • =変数/#{変数}

  • include(partials)/extends/block

  • じょうけんステートメント

  • 繰り返し文(反復)
  • ul
      each video in videos
        li=video

    (4)混入(mixin)


    異なるデータを持つ同じ形式のHTMLを返します.
    includeと一緒に使用する必要があります.

    5.MongoDBとMongoose


    1)偽データベース


    ビデオの表示/編集/アップロード

    (1)三項条件演算子


    pugファイルでJavaScriptの3つの条件演算子を使用する
    block content
      h3 #{video.views} #{video.views === 1 ? "view" : "views"}

    (2) req.body

    req.body: { input의 name: "input의 value" }
    req.bodyを使用してform(method="POST")が送信した値を受信します.
    formのbodyをexpressで理解させて上記JavaScriptオブジェクトに変換するには、ミドルウェアを使用する必要があります.
    ※注意!すべてのミドルウェアはrouterの前に作成されます.
    例えば、reqは、ルータの前に、要求がvideoRouterに到達したときにミドルウェアが要求される必要があります.bodyはもう準備ができているかもしれないからです.
    // server.js
    app.use(express.urlencoded({ extended: true }));

    2)MongoDBとMongoseのインストールと接続


    [TIL] 211125を参照
    // db.js
    import mongoose from "mongoose";
    
    mongoose.connect("mongodb://127.0.0.1:27017/wetube");
    Mongooseをデータベース(MongoDB)に接続し、サーバとデータベースを接続します.
    // server.js
    import "./db";
    server.jsファイルのdb.jsファイルをインポートします.
    db.jsファイルから何もエクスポートされていませんが、db.jsファイルをインポートするだけで、サーバはデータベースに接続できます.

    3)モデル


    (1)パターンとモデルの作成と使用

    // Video.js
    import mongoose from "mongoose";
    
    const videoSchema = new mongoose.Schema({
      title: String,
      description: String,
      createdAt: Date,
      hashtags: [{ type: String }],
      mata: {
        views: Number,
        rating: Number,
      }
    });
    
    const Video = mongoose.model("Video", videoSchema);
    
    export default Video;
    モデルフォルダを作成すると、ビデオが含まれます.jsファイルを作成します.
    Video.jsファイルにVideo SchemaとVideoモデルを作成した後、デフォルトでエクスポートします.
    サーバはすべての人が使用できます.jsファイルをインポートします.
    // server.js
    import "./db";
    import "./models/Video";
    サーバがデータベースに接続されると、接続に成功すると、データベースはビデオモードを認識します.
    server.私はjsファイルですぐにビデオモデルを使用しないので、変数として受け入れませんでしたが、ビデオモデルを事前にコンパイルして必要なときにロードするには、このように書く必要があります.

    (2) server.jsとinit。jsファイルに分割


    server.jsファイルは、expressなどのサーバ構成のみに関連します.
    init.jsファイルは、データベースに関連するものをインポートした後、異常がなければサーバを開く役割を果たします.
    ※現在appを実行しているファイルはinitです.jsファイルなのでpackage.jsonファイルでsrc/init dev script.jsに変更します.

    4) home video


    (1) promise: async & await


    これにより、JavaScriptは、データベースからデータを受信すると、コード内でデータを停止して待機します.
    作成した順序でコードを実行すると、コールバック関数よりも読みやすくなります.
    // home 컨트롤러
    export const home = async (req, res) => {
      try {
        const videos = await Video.find({});
        return res.render("home", { pageTitle: "Home", videos });
      } catch {
        return res.render("server-error");
      }
    };

    5) upload video


    (1)getUploadコントローラ

    export const getUpload = async (req, res) => {
      return res.render("upload", { pageTitle: "Upload Video" });
    };

    (2)PostUploadコントローラ

    export const postUpload = async (req, res) => {
      const { title, description, hashtags } = req.body;
      try {
        await Video.create({
          title,
          description,
          hashtags: hashtags.split(",").map(word => `#${word}`),
          createdAt: Date.now();
          meta: {
            views: 0,
            rating: 0,
          },
        });
      } catch(error) {
        return res.render("upload", { pageTitle: "Upload Video", errorMessage: error._message });
      }
      return res.redirect("/");
    };

    (3)ビデオモードの変更


    schema options
    required/default/trim/maxLength/minLength
    ラベルをフォーマットする方法(これはもともとeditビデオ部分に現れたもので、ここで一緒に修正しただけです)
    1~3を選択
    [ Video.js ]
    import mongoose from "mongoose";
    
    // 선택2. export formatHashtags & import in videoController.js
    export const formatHashtags = (hashtags) => hashtags.split(",").map(word => word.startsWith("#") ? word : `#${word}`);
    
    // videoSchema 수정
    const videoSchema = new mongoose.Schema({
      title: { type: String, required: true, trim: true, maxLength: 80 },
      description: { type: String, required: true, trim: true, minLength: 20 },
      hashtags: [{ type: String, required: true, trim: true }],
      createdAt: { type: Date, required: true, default: Date.now },
      meta: {
        views: { type: Number, required: true, default: 0 },
        rating: { type: Number, required: true, default: 0 },
      },
    });
    
    // 선택1. mongoose middleware (pre hook) → 한계: "save"에만 사용 가능
    videoSchema.pre("save", async function() {
      this.hashtags = this.hashtags[0].split(",").map(word => word.startsWith("#") ? word : `#${word}`);
    });
    
    // 선택3. static 메서드 만들기
    videoSchema.static("formatHashtags", function (hashtags) {
      return hashtags.split(",").map(word => word.startsWith("#") ? word : `#${word}`);
    });
    
    const Video = mongoose.model("Video", videoSchema);

    (4)watchコントローラ

    export const watch = async (req, res) => {
      const { id } = req.params;
      const video = await Video.findById(id);
      if (!video) {
        return res.render("404", { pageTitle: "Video not found" });
      }
      return res.render("watch", { pageTitle: video.title, video });
    };

    6) edit video


    (1)getEditコントローラ

    export const getEdit = async (req, res) => {
      const { id } = req.params;
      const video = await Video.finById(id);
      if (!video) {
        return res.render("404", { pageTitle: "Video not found" });
      }
      return res.render("edit", { pageTitle: `Editing: ${video.title}`, video });
    };

    (2) edit.pug水晶


    ビデオを再作成するのではなく、ビデオを変更するので、valueプロパティを追加して既存の値をinputに入力します.
    extends base
    
    block content
      h4 Update Video
      form(method="POST")
        input(name="title", placeholder="title", value=video.title, required, maxlength=80)
        input(name="description", placeholder="description", value=video.description, required, minlength=20)
        input(name="hashtags", placeholder="hashtags separated by comma", value=video.hashtags.join(), required)
        input(value="edit video", type="submit")

    (3)PostEditコントローラ


    Model.exists()はidではなくパラメータとしてフィルタ({id:id})を受け入れる
    export const postEdit = async (req, res) => {
      const { id } = req.params;
      const { title, description, hashtags } = req.body;
      const video = await Video.exists({ _id: id });
      if (!video) {
        return res.render("404", { pageTitle: "Video not found" });
      }
      await Video.findByIdAndUpdate(id, {
        title,
        description,
        hashtags: Video.formatHashtags(hashtags),
      });
      return res.redirect(`/videos/${id}`);
    };

    7) delete video


    (1)VideoRouterへのルーティングの追加


    通常、データの削除時にpost requestは発生しません.
    urlにアクセスするとすぐにビデオを削除します.
    videoRouter.get("/:id([0-9a-f]{24})/delete", deleteVideo);

    (2)deleteVideoコントローラ

    export const deleteVideo = async (req, res) => {
      const { id } = req.params;
      await Video.findByIdAndDelete(id);
      return res.render("/");
    };

    8) search video


    (1) search.pugの作成


    formの作成(method="GET")
    extends base
    
    block content
      form(method="GET")
        input(name="keyword", placeholder="search by keyword", required, maxlength=80)
        input(value="search", type="submit")

    (2)コントローラの検索


    ビデオシーケンスを空のシーケンスに設定します.
    keywordが入力されている場合は、条件に合致するビデオを検索し、ビデオシーケンスを更新します.
    条件を満たすビデオがある場合は、検索ページに表示され、ない場合は検索ページのみが表示されます.
    ※初めてsearchページに来たときreq.queryには未定義の値があります.if (keyword) {}
    export const search = async (req, res) => {
      const { keyword } = req.query;
      let videos = [];
      if (keyword) {
        videos = await Video.find({
          title: {
            $regex: new RegExp(keyword, "i");
          }
        }); 
      }
      return res.render("search", { pageTitle: "Search", videos });
    };

    明日作った

  • user部分開始!