[TIL] 211128
📝 今日作った
📚 学識
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)サーバの作成
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)ビューエンジン設定
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");
}
// ( ...이하 생략 )
req.params: { 파라미터 이름: "파라미터 값" }
// videoRouter.js
videoRouter("/:id([0-9a-f]{24})", watch);
//- 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
1) pug
URLにアクセスするときに必要なページを表示するようにコントローラを変更する必要があります.
pugは、これらのビューの作成を支援するテンプレートです.
(1)ビューエンジン設定
Expressはpugを使用してHTMLを返します.
// server.js
app.set("view engine", "pug");
(2)ビューフォルダパスの設定
// 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 });
};
明日作った
block content
h3 #{video.views} #{video.views === 1 ? "view" : "views"}
req.body: { input의 name: "input의 value" }
// server.js
app.use(express.urlencoded({ extended: true }));
// db.js
import mongoose from "mongoose";
mongoose.connect("mongodb://127.0.0.1:27017/wetube");
// server.js
import "./db";
// 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;
// server.js
import "./db";
import "./models/Video";
// 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");
}
};
export const getUpload = async (req, res) => {
return res.render("upload", { pageTitle: "Upload Video" });
};
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("/");
};
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);
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 });
};
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 });
};
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")
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}`);
};
videoRouter.get("/:id([0-9a-f]{24})/delete", deleteVideo);
export const deleteVideo = async (req, res) => {
const { id } = req.params;
await Video.findByIdAndDelete(id);
return res.render("/");
};
extends base
block content
form(method="GET")
input(name="keyword", placeholder="search by keyword", required, maxlength=80)
input(value="search", type="submit")
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 });
};
Reference
この問題について([TIL] 211128), 我々は、より多くの情報をここで見つけました https://velog.io/@leesyong/TIL-211128テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol