mongooseλ‘ CRUD operation ꡬννκΈ° - search video / upload video
schema / model / mongoose queries / mongoose validation
CRUDλ μμ±(Create), μ½κΈ°(Read), μμ (Update), μμ (Delete)λ₯Ό λ§νλ€.
μ§κΈλΆν° video(λ°μ΄ν°)λ₯Ό CRUD νκΈ° μν΄ video λͺ¨λΈ
μ λ§λ€κ³ μ νλ€.
Mongooseλ‘ λ°μ΄ν° CRUDλ₯Ό ꡬννκΈ° μν΄μλ Mongooseμ μ ν리μΌμ΄μ
μ λ°μ΄ν°λ€μ΄ μ΄λ»κ² μκ²Όλμ§
(λͺ¨λΈ) μλ €μ€μΌ νλ€.
ex) 'λΉλμ€μλ μ λͺ©μ΄ μλλ° κ·Έ μ λͺ©μ λ¬Έμμ΄μ΄κ³ , μ‘°νμκ° μλλ° κ·Έκ±΄ μ«μμΌ.'
videoSchema
λ₯Ό λ§λ λ€.π‘ Schema(μ€ν€λ§)
λͺ¨λΈμ μμ±νκΈ° μ μ λ°μ΄ν°μ νμκ³Ό ννλ₯Ό μ μν΄λμ κ²μ λ§νλ€.
mongoose.Schema({}) μμ μ μν΄μ€λ€.// Video.js import mongoose from "mongoose"; const videoSchema = new mongoose.Schema({ title: String, // String === { type: String } description: String, createdAt: Date, hashtags: [{ type: String }], });
Video λͺ¨λΈ
μ μμ±ν ν μ΄λ₯Ό export default νλ€.π‘ Model(λͺ¨λΈ)
μ€ν€λ§λ₯Ό ν΅ν΄ λ§λλ μΈμ€ν΄μ€λ₯Ό λ§νλ€.
μ΄λ₯Ό μ΄μ©ν΄ μ€μ λ°μ΄ν°λ² μ΄μ€μ CRUD μμ μ ν μ μλ€.
mongoose.model("λͺ¨λΈ μ΄λ¦", μ€ν€λ§ μ΄λ¦); μ ν΅ν΄ λ§λ λ€.// Video.js const Video = mongoose.model("Video", videoSchema); export default Video;
Video.js νμΌμ import
ν΄μΌ νλ€.π server.jsμ init.js λΆλ¦¬
β src ν΄λ μμ λͺ¨λ κ²μ μ΄κΈ°νν΄μ£Όλ init.js νμΌμ λ§λ λ€.
init.js νμΌμ΄ λ°μ΄ν°λ² μ΄μ€μ λͺ¨λΈλ€μ import ν ν μ΅μ’
μ μΌλ‘ μ ν리μΌμ΄μ
(app)μ μ€ννλλ‘ server.js νμΌλ‘λΆν° κ΄λ ¨ μ½λλ€μ κ°μ Έμ¨λ€.
ννΈ, init.js νμΌμλ appμ λν μ λ³΄κ° μκΈ° λλ¬Έμ server.js νμΌμμ appμ λ§λ€κ³ export default ν ν, μ΄λ₯Ό init.js νμΌμμ import ν΄μ€μΌ νλ€.
// server.js β expressμ appμ ꡬμ±νλ€
import express from "express";
import morgan from "morgan";
import globalRouter from "./routers/globalRouter";
import userRouter from "./routers/userRouter";
import videoRouter from "./routers/videoRouter";
const PORT = 4000;
const app = express();
const logger = morgan("dev");
app.set("view engine", "pug");
app.set("views", process.cwd() + "/src/views");
app.use(logger);
app.use(express.urlencoded({ extended: true }));
app.use("/", globalRouter);
app.use("/users", userRouter);
app.use("/videos", videoRouter);
export default app; // μΆκ° β
// init.js β databaseμ modelsλ₯Ό import ν ν, μ΄μμ΄ μλ€λ©΄ appμ μ€νμν¨λ€
import "./db";
import "./models/Video";
import app from "./server";
const PORT = 4000;
const handleListening = () =>
console.log(`Server listening on port http://localhost:${PORT} π`);
app.listen(PORT, handleListening);
β‘ μ΄μ server.js νμΌμ appμ ꡬμ±λ§ ν λΏ μ€νν μ μμ΄μ κΈ°μ‘΄ μ½λλ‘λ nodemonμ΄ μ€νλμ§ μμ κ²μ΄λ€.
λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ package.json νμΌμμ dev scriptμ src/server.jsλ₯Ό src/init.jsλ‘ μμ νλ€.
// package.json
"scripts": {
"dev": "nodemon -L --exec babel-node src/init.js"
},
λ¨Όμ , videoController.js νμΌμμ 미리 λ§λ€μ΄λ κ°μ§ λ°μ΄ν°λ² μ΄μ€ κ΄λ ¨ μ½λλ€μ μ λΆ μμ νλ€.
video λͺ¨λΈμ μ¬μ©νκΈ° μν΄ videoController.jsμ video λͺ¨λΈμ import νλ€.
// videoController.js
import Video from "../models/Video";
π‘ Mongoose Queries
Mongoose Queries μ°Έκ³
μ½λ°± ν¨μ νΉμ Promiseλ₯Ό μ΄μ©ν΄ mongoose query objectλ₯Ό λ°ννλ€.
μ¬κΈ°μλ Model.find()
λΌλ Mongoose Queryλ₯Ό μλ‘ μ€λͺ
ν΄λ³΄κ² λ€.
π μ½λ°± ν¨μ μ΄μ©
Model.find({}, μ½λ°± ν¨μ);
{}λ search terms(κ²μ 쑰건)λ₯Ό λ§νλ€. λΉμ΄ μμΌλ©΄ λͺ¨λ νμμ μ°Ύκ³ μμμ λ»νλ€.
μ½λ°± ν¨μλ errorμ docsλ₯Ό κ°μ§λ€. μ΄ κ²½μ°μ docs λμ μ videosλΌκ³ μΈ μ μλ€.
Video.find({}, (error, videos) => {} );
mongooseκ° λͺ¨λ νμμ Videoλ₯Ό λ°μ΄ν°λ² μ΄μ€μμ λΆλ¬μ€κ³ databaseκ° λ°μνλ©΄, mongooseλ μ½λ°± ν¨μλ₯Ό μ€νμν¨λ€.
μ½λλ₯Ό μμ±νλ©΄ λ€μκ³Ό κ°λ€.
// videoController.js
import Video from "../models/Video";
const handleSearch = (error, videos) => {
console.log("error", error);
console.log("videos", videos);
}
export const home = (req, res) => {
Video.find({}, handleSearch);
return res.render("home", { pageTitle: "Home", videos: [] });
}
μ΄μ localhost:4000μ λ€μ΄κ°λ©΄, console μ°½μλ λ€μκ³Ό κ°μ λ΄μ©μ΄ μΆλ ₯λλ€.
λ°μ΄ν°λ² μ΄μ€κ° videoλ₯Ό μ°Ύμ λ μλ¬λ μμκ³ video(μ¦, λ°μ΄ν°)λ μμλ€λ λ»μ΄λ€.
μμ§ λ°μ΄ν°κ° μμ΄μ κ°μ Έμ¨ 건 μμ§λ§, μ΄μ¨λ μλ°μ€ν¬λ¦½νΈ μ½λλ₯Ό μ΄μ©ν΄ λ°μ΄ν°λ² μ΄μ€μ ν΅μ μ μ±κ³΅ν κ²μ΄λ€.
error null
videos []
μ¬κΈ°μ μ κΉ !
μ½λ°± ν¨μμ λν΄ μμ보기 μν΄ operation(Video.find()) λ€μμ console.log()λ₯Ό μΆκ°ν΄λ΄€λ€.export const home = (req, res) => { Video.find({}, handleSearch); console.log("Hello"); // μΆκ° β return res.render("home", { pageTitle: "Home", videos: [] }); };
κ·Έλ°λ° μ΄μ²λΌ μ½λλ [ operation, Hello, res.render ] μμΌλ‘ μμ±νλλ°
console μ°½μλ [ Hello, res.render, operation ] μμΌλ‘ μ°ν κ²μ νμΈν μ μλ€.Server listening on port http://localhost:4000 π β Connected to DB Hello GET / 304 79.991 ms - - error null videos []
μ¦, μ£Όμ μ°½μ localhost:4000μ μ λ ₯ν΄ λΈλΌμ°μ κ° home νμ΄μ§λ₯Ό request νλ©΄ console.log("Hello") κ° μ€νλλ€.
requestκ° μμ±λμ΄ responseλ₯Ό λ°μΌλ©΄(μ¦, home ν νλ¦Ώμ΄ render λλ©΄) Morgan middlewareμ μν΄ loggerκ° μΆλ ₯λλ€.
κ·Έ ν λ°μ΄ν°λ² μ΄μ€μ ν΅μ ν κ²°κ³ΌμΈ errorμ videosκ° μΆλ ₯λλ€.μ΄λ λ°μ΄ν°λ₯Ό μ μ‘λ°μ λκΉμ§ μκ°μ΄ 걸리기 λλ¬Έμ΄λ€. handleSearch μ½λ°± ν¨μλ λ°μ΄ν°κ° μμ ν μ μ‘λλ©΄ κ·Έμ μμΌ μ€νλλ€.
μ΄ μ μ μμ©ν΄ home 컨νΈλ‘€λ¬λ₯Ό λ€μκ³Ό κ°μ΄ μμ ν μ μλ€.
λ°μ΄ν°λ₯Ό κ°μ Έμ€λ λ° μ±κ³΅ν κ²½μ°μ μλ¬κ° λ¬ κ²½μ°λ₯Ό λͺ¨λ κ³ λ €ν΄ μ½λλ₯Ό μμ±νλ€.
// videoController.js (μ½λ°± ν¨μ μ΄μ© - μ΅μ’
)
import Video from "../models/Video";
export const home = (req, res) => {
Video.find({}, (error, videos) => {
if(error) {
return res.render("server-error");
}
return res.render("home", { pageTitle: "Home", videos });
});
};
μ΄μ home.pug νμΌ μμ λ³μ videosμλ, λ°μ΄ν°λ² μ΄μ€λ‘λΆν° μ μ‘λ°μ videosκ° ν λΉλλ€.
λΈλΌμ°μ λ ν΄λΉ μμ
μ΄ λλ λκΉμ§ κΈ°λ€λ €μ€λ€.
μ¦, λ°μ΄ν°λ² μ΄μ€ searchκ° λλμΌλ§ renderκ° μμλλ€.
νμΈμ μν΄ μλμ²λΌ μ½λλ₯Ό μμ±νλ©΄
μ½λλ₯Ό μμ±ν μμλλ‘ μ¦, [ search start, search finished, logger ] μμΌλ‘ μΆλ ₯λλ κ²μ νμΈν μ μλ€.
λ¨, μ½λμ λ°λΌ μ€ν μλμ μ°¨μ΄κ° μμ μλ μλ€.
// videoController.js (μ½λ°± ν¨μ μ΄μ© - νμΈμ©)
export const home = (req, res) => {
console.log("search start"); // νμΈμ μν΄ μΆκ°
Video.find({}, (error, videos) => {
console.log("search finished"); // νμΈμ μν΄ μΆκ°
return res.render("home", { pageTitle: "Home", videos });
});
};
π promise μ΄μ©
κ·Έλ¬λ, video λͺ¨λΈμ μ¬μ©ν λ μ½λ°± ν¨μλ₯Ό μ΄μ©νλ©΄, ν¨μ μμ λ λ€λ₯Έ ν¨μλ₯Ό λ£μ΄μΌ ν΄μ λ²κ±°λ‘λ€.
μ΄ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ μ½λ°± ν¨μ λμ promiseλ₯Ό μ΄μ©ν μ μλ€.
// videoController.js (promise μ΄μ© - νμΈμ©)
export const home = async (req, res) => {
console.log("search start");
const videos = await Video.find({});
console.log("search finished");
console.log(videos);
return res.render("home", { pageTitle: "Home", videos });
}
Server listening on port http://localhost:4000 π
β Connected to DB
search start
search finished
[]
GET / 304 87.183 ms - -
μ½λλ₯Ό μμ±ν μμλλ‘ μ¦, [ search start, search finished, videos, logger ] μμΌλ‘ μΆλ ₯λλ κ²μ νμΈν μ μλ€.
Video.find() μμ awaitμ μ μ΄μ£Όλ©΄, find()λ μΈμλ‘ μ½λ°± ν¨μκ° λ€μ΄μ€μ§ μμΌλ¦¬λ κ±Έ μκ³ μ°ΎμλΈ videoλ₯Ό λ°λ‘ μΆλ ₯ν΄μ€λ€.
awaitμ λ°μ΄ν°λ² μ΄μ€λ‘λΆν° κ²°κ³Ό κ°μ λ°μ λκΉμ§ κΈ°λ€λ €μ€λ€.
ννΈ, μ½λ°± ν¨μλ₯Ό μ¬μ©νλ©΄ μΆκ° μ½λ μμ΄ λ°λ‘ μλ¬λ₯Ό κ°μ Έμ μΆλ ₯ν μ μμ§λ§, promiseλ₯Ό μ¬μ©νλ©΄ μλ¬λ₯Ό λ€λ£¨κΈ° μν΄ try catch λ¬Έμ μ΄μ©ν΄μΌ νλ€.
β» catch(error) { }λ₯Ό ν΅ν΄ error λ©μμ§λ₯Ό κ°μ Έμ¬ μλ μμ§λ§ μ¬κΈ°μλ κ°μ Έμ€μ§ μμλ€.
// videoController.js (promise μ΄μ© - μ΅μ’
)
import Video from "../models/Video";
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");
}
};
π promiseλ μ½λ°± ν¨μμ λΉν΄ ν¨μ¬ μ§κ΄μ μ΄λΌλ μ₯μ μ΄ μλ€.
π μ½λ°± ν¨μλ μ€νμ κΈ°λ€λ Έλ€κ° λμ§λ§, μλ°μ€ν¬λ¦½νΈλ μ΄ μ€ μ μ€μ μλ€κ°λ€νλ©΄μ μ½λλ₯Ό μ€ννλ€.
π λ°λ©΄, promiseλ₯Ό μ¬μ©νλ©΄ awaitμ΄ μλ λΆλΆμμ μλ°μ€ν¬λ¦½νΈλ κ³μ κΈ°λ€λ Έλ€κ° κΈ°λ€λ¦Όμ΄ λλλ©΄ μμλλ‘ μ½λλ₯Ό μ€ννλ€.
μ¬κΈ°μ μ κΉ !
returnκ³Ό res.renderμ λνμ¬export const home = (req, res) => { Video.find({}, (error, videos) => { return res.render("home", { pageTitle: "Home", videos }); }); };
res.render() μμ returnμ, ν¨μ μμ λ λ€λ₯Έ ν¨μ μμ μμΉν΄ μκΈ° λλ¬Έμ μλ¬΄λ° κΈ°λ₯λ νμ§ μλλ€.
κ·ΈλΌμλ res.render() μμ returnμ μ¨μ£Όλ μ΄μ λ, res.render() ν¨μκ° μ€νλ νμ Video.find()μ μ½λ°± ν¨μκ° μ’ λ£λλλ‘ νκΈ° μν¨μ΄λ€.returnμ΄ μ€μν κ² μλλΌ
μλ΅νκΈ° μν΄ μ€νλλ ν¨μ
κ° μ€μν κ²μ΄λ€.
μλ΅ν νμ λ λ€μ μλ΅ν μ μλ€.
μλ₯Ό λ€μ΄, res.render() μ΄νμ res.end() λ±μ ν μ μλ€.
μλμ κ°μ΄ μλ²κ° μλ΅νλ μ½λλ₯Ό νλ λ μΆκ°ν΄λ³΄μ.export const home = (req, res) => { Video.find({}, (error, videos) => { return res.render("home", { pageTitle: "Home", videos }); }); return res.end(); };
μ΄μ²λΌ μμ±ν ν localhost:4000μΌλ‘ λ€μ΄κ°λ©΄, ν°μ λΉ νμ΄μ§κ° λ¬λ€.
μμμ videoλ₯Ό μ°ΎκΈ°κΉμ§(λ°μ΄ν°λ₯Ό μ μ‘λ°κΈ°κΉμ§) μκ°μ΄ 걸리λ―λ‘ λ€μ μ½λμΈ res.end()κ° Video.find()μ μ½λ°± ν¨μλ³΄λ€ λ¨Όμ μ€νλλ€.
λΉλμ€ μ°ΎκΈ°κ° λλ ν μ½λ°± ν¨μλ₯Ό μ€ννλ €κ³ νλ©΄ μ΄λ―Έ νλ² μλ΅μ νμΌλ―λ‘ λ€μ μλ‘μ΄ μλ΅μΈ res.render()μ μ€νν μ μκ² λλ κ²μ΄λ€.
μ¬μ©μλ videoλ₯Ό upload ν λ videoμ title, description, hashtagsλ₯Ό μμ±ν΄μΌ νλ€. (meta λ°μ΄ν°λ μλμΌλ‘ μ£Όμ΄μ§λ€.)
from νκ·Έλ₯Ό μμ νλ€.
//- upload.pug
form(method="POST")
input(name="title", placeholder="Title", required)
input(name="description", placeholder="Description", required) //- μΆκ° β
input(name="hashtags", placeholder="Hashtags, separated by comma", required) //- μΆκ° β
input(value="save", type="submit")
document(JavaScript Object)λ₯Ό λ§λ€μ΄μΌ νλ€.
// 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;
// videoController.js (μμ )
import Video from "../models/Video";
export const postUpload = (req, res) => {
const { title, description, hashtags } = req.body;
const video = new Video({
title,
description,
createdAt: Date.now(),
hashtags: hashtags.split(",").map(word => `#${word}`);
meta: {
views: 0,
rating: 0,
},
});
console.log(video); // μμ±ν video μΈμ€ν΄μ€λ₯Ό console μ°½μ μΆλ ₯
return res.redirect("/");
}
videoλ₯Ό λ§λ€μλ€ ! (μμ§ λ°μ΄ν°λ₯Ό μ μ₯νμ§λ μμ μν)
console μ°½μλ λ€μκ³Ό κ°μ κ²°κ³Όκ° μΆλ ₯λλ€.
{
title: 'video 1',
description: 'first video',
createdAt: 2021-11-26T09:24:15.248Z,
hashtags: [ '#wow', '#amazing', '#boom' ],
mata: { views: 0, rating: 0 },
_id: new ObjectId("61a0a7bf1c1dbbe8315b6cdf")
}
β» idλ mongooseκ° video documentμ μλμΌλ‘ λΆμ¬ν΄μ€ κ³ μ μλ³ λ²νΈμ΄λ€.
videoλ₯Ό λ°μ΄ν°λ² μ΄μ€μ μ μ₯ν΄μΌ νλ€.
// videoController.js
import Video from "../models/Video";
export const postUpload = async (req, res) => { // μμ β
const { title, description, hashtags } = req.body;
const video = new Video({
title,
description,
createdAt: Date.now(),
hashtags: hashtags.split(",").map(word => `#${word}`);
meta: {
views: 0,
rating: 0,
},
});
await video.save(); // μΆκ° β
return res.redirect("/");
}
λ°μ΄ν°λ₯Ό λ°μ΄ν°λ² μ΄μ€μ μ μ‘νλ λ°λ μκ°μ΄ 걸리기 λλ¬Έμ async & awaitμ μ¨μ€μΌ νλ€.
μ΄μ λ°λΌ μλ°μ€ν¬λ¦½νΈλ video.save()μ μν΄ λ°μ΄ν°κ° λ°μ΄ν°λ² μ΄μ€μ μ μ₯λκΈ°λ₯Ό κΈ°λ€λ¦°λ€.
cf. video.save()λ λ°μ΄ν°λ² μ΄μ€μ μ μ₯λ video documentλ₯Ό return νλ€.
μ΄μ localhost:4000μΌλ‘ λ€μ΄κ°μ upload videoλ₯Ό ν΄λ¦ν΄ formμ μμ±ν ν save λ²νΌμ λλ₯΄λ©΄
(λ°μ΄ν°λ² μ΄μ€μ video λ°μ΄ν°κ° μ μ₯λ ν) homeμΌλ‘ redirect λλλ°
(home 컨νΈλ‘€λ¬μ μν΄) μ΄λ homeμλ λ°μ΄ν°λ² μ΄μ€μ μ μ₯λ video λ°μ΄ν°κ° νμλλ€.
λ°μ΄ν°λ² μ΄μ€μ video λ°μ΄ν°κ° μ μ₯λμλ€! ( μ¦, videoκ° upload λμλ€! )
π‘ MongoDB shellμμ λ°μ΄ν°λ² μ΄μ€ νμΈνκΈ°
show dbs β wetube λ°μ΄ν°λ² μ΄μ€κ° μμ±λ κ²μ νμΈν μ μλ€. admin 0.000GB config 0.000GB local 0.000GB wetube 0.000GB use wetube β wetube λ°μ΄ν°λ² μ΄μ€λ‘ μ΄λνλ€ switched to db wetube show collections β documents λ¬Άμμ 보μ¬μ€λ€. videos β μ§κΈμ document μ’ λ₯κ° videoλ°μ μλ€.
helpλ₯Ό μ λ ₯νλ©΄ μ¬λ¬ λͺ λ Ήμ΄λ€μ΄ μλ΄λμ΄ λ°μ΄ν°λ² μ΄μ€ μμ μ΄ν΄λ³Ό μ μλ€.
ex)
db.mycoll.find()
β ν΄λΉ collection μμ objectλ€μ νμΈν μ μλ€.db.videos.find() { "_id" : ObjectId("61a0b09e66a8f06d2a0ad89b"), "title" : "video 1", "description" : "first video", "createdAt" : ISODate("2021-11-26T10:02:06.719Z"), "hashtags" : [ "#wow", "#first", "#video" ], "mata" : { "views" : 0, "rating" : 0 }, "__v" : 0 }
π‘ ννΈ, λ°μ΄ν°λ₯Ό λ§λ€κ³ μ μ₯νλ λ°
Video.create({ })
λ₯Ό μ¬μ©ν μ μλ€.// videoController.js import Video from "../models/Video"; export const postUpload = async (req, res) => { const { title, description, hashtags } = req.body; await Video.create({ // μμ β title, description, createdAt: Date.now(), hashtags: hashtags.split(",").map(word => `#${word}`); meta: { views: 0, rating: 0, }, }); return res.redirect("/"); }
Mongooseλ 미리 μλ €μ€ λ°μ΄ν° νμ
μ λ°νμΌλ‘ λ°μ΄ν° νμ
μ μ ν¨μ± κ²μ¬
λ₯Ό λμμ€λ€.
μ€ν€λ§μ μ μλ λ°μ΄ν° νμ κ³Ό μ€μ λ°μ΄ν°μ λ°μ΄ν° νμ μ΄ λ€λ₯΄λ©΄, μλ¬κ° λ°μνλ€.
ex. postUpload 컨νΈλ‘€λ¬μμ createdAtμ μμ± κ°μΌλ‘ λ¬Έμμ΄μ μ μ΄μ£Όλ©΄, console μ°½μ ν΄λΉ μλ¬κ° μ°νλ©΄μ λΈλΌμ°μ κ° '무ν λ‘λ©'μ νλ€.
β awaitμΌλ‘ μΈν΄ λ°μ΄ν° μμ± λ° μ μ₯μ΄ λλ νμμΌ res.redirect() μ¦, μλ΅μ ν μ μλλ° λ°μ΄ν° μμ± μμ²΄κ° λμ§ μκΈ° λλ¬Έμ μλ°μ€ν¬λ¦½νΈλ λ€μ λ¨κ³λ‘ λμ΄κ°μ§ μμΌλ―λ‘ λΈλΌμ°μ κ° λ¬΄ν λ‘λ©
νκ² λλ κ²μ΄λ€.
μ€ν€λ§μ μ μλ λ°μ΄ν°μ κ΅¬μ± μμκ° μ€μ λ°μ΄ν° κ΅¬μ± μ λλ½λλ©΄, μλ¬κ° λ°μνλ€.
κ·Έλ¬λ, μ§κΈμ κ²½μ°μλ postUpload 컨νΈλ‘€λ¬μμ μλ‘μ΄ videoλ₯Ό μμ±ν λ createdAtμ κ°μ΄ λλ½λμλλΌλ μλ¬λ λ°μνμ§ μλλ€. μ΄λ videoSchemaμμ createdAtμ΄ νμ μμ(required)κ° μλκΈ° λλ¬Έμ΄λ€.
λ°λΌμ, Video.js νμΌμμ videoSchemaλ₯Ό μμ ν΄ createAtμ νμ μμλ‘ μ€μ ν΄μ£Όμ΄μΌ νλ€.
// Video.js
import mongoose from "mongoose";
const videoSchema = new mongoose.Schema({
title: String,
description: String,
createdAt: { type: Date, required: true }, // μμ β
hashtags: [{ type: String }],
mata: {
views: Number,
rating: Number,
},
});
const Video = mongoose.model("Video", videoSchema);
export default Video;
μ΄μ postUpload 컨νΈλ‘€λ¬μμ μλ‘μ΄ videoλ₯Ό μμ±ν λ createdAtμ κ°μ΄ λλ½λλ©΄, console μ°½μ ν΄λΉ μλ¬κ° μ°νλ©΄μ μμ λΈλΌμ°μ κ° λ¬΄ν λ‘λ©
μ νκ² λλ€.
try catch ꡬ문
μ μΆκ°νμ¬ μλ¬ λ°μ μ μ¬μ©μμκ² μ§μ μλ¬ λ΄μ©μ 보μ¬μ£Όκ³ μ νλ€.//- upload.pug μμ
block content
if errorMessage
span=errorMessage
// videoController.js μμ
try {
await Video.create({
title,
description,
createdAt: Date.now(),
hashtags: hashtags.split(",").map(word => `#${word}`);
meta: {
views: 0,
rating: 0,
},
});
return res.redirect("/");
} catch(error) { // μλ¬ λ°κ²¬ μ
return res.render("upload", { pageTitle: "Upload Video", errorMessage: error._message });
// upload νλ νμ΄μ§λ₯Ό 보μ¬μ£Όλ©΄μ μ¬μ©μμκ² μλ¬ λ©μμ§λ₯Ό ν¨κ» 보μ¬μ€λ€.
// error._messageλ μ½μ μ°½μ λ¬ errorλ₯Ό μ°Έκ³ ν΄ μμ±ν κ²
}
default κ°μ μ€μ ν μ μλ€.
// Video.js
const videoSchema = new mongoose.Schema({
title: String,
description: String,
createAt: { type: Date, required: true, default: Date.now }, // μμ β
// μ΄λ Date.now() λΌκ³ μμ±νλ©΄ ν¨μλ₯Ό λΉμ₯ μ€νμν¨λ€. Date.now λΌκ³ μμ±ν΄μΌ νλ€.
hashtags: [{ type: String }],
mata: { // μμ β
views: { type: Number, required: true, default: 0 },
rating: { type: Number, required: true, default: 0 },
},
});
μ΄μ μ΄λ κ² μ€μ ν΄λμΌλ©΄ video λ°μ΄ν°λ₯Ό λ§λ€ λ createdAtκ³Ό meta λ°μ΄ν°λ₯Ό κ΅³μ΄ μ μ΄μ£Όμ§ μμλ μλ¬κ° λ¨μ§ μλλ€!
μ΅μ’
Video.js νμΌ & videoController.js νμΌμ postUpload 컨νΈλ‘€λ¬// Video.js
import mongoose from "mongoose";
const videoSchema = new mongoose.Schema({
title: { type: String, required: true },
description: { type: String, required: true },
createdAt: { type: Date, required: true, default: Date.now },
hashtags: [{ type: String }],
mata: {
views: { type: Number, required: true, default: 0 },
rating: { type: Number, required: true, default: 0 },
},
});
const Video = mongoose.model("Video", videoSchema);
export default Video;
// videoController.js
export const postUpload = async (req, res) => {
const { title, description, hashtags } = req.body;
try {
await Video.create({
title,
description,
hashtags: hashtags.split(",").map((word) => `#${word}`),
});
} catch (error) {
return res.render("upload", {
pageTitle: "Upload Video",
errorMessage: error._message,
});
}
return res.redirect("/");
};
Mongoose - SchemaTypes μ°Έκ³
// Video.js
const videoSchema = new mongoose.Schema({
title: { type: String, required: true, trim: true, maxLength: 80 },
description: { type: String, required: true, trim: true, minLength: 20 },
createdAt: { type: Date, required: true, default: Date.now },
hashtags: [{ type: String, trim: true }],
mata: {
views: { type: Number, required: true, default: 0 },
rating: { type: Number, required: true, default: 0 },
},
});
trim
ν μ€νΈ μ μμ spaceλ₯Ό μμ ν΄μ€λ€.
maxLength, minLength
μ΅λ, μ΅μ κΈμ μλ formμ input μμ±μΌλ‘λ μ€μ ν μ μμ§λ§, μ΄λ νμ΄μ§μ HTML μ½λλ₯Ό ν΅ν΄ μ½κ² λ³κ²½μ΄ κ°λ₯νλ―λ‘ μμ€ν μ 보νΈλ₯Ό μν΄ λ°μ΄ν°λ² μ΄μ€μλ λκ°μ΄ μ€μ ν΄μ€μΌ νλ€.
video mixinμ μμ νλ€.
//- video.pug
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
p=video.description
small=video.createdAt
hr
Cannot GET ~~~
μ΄ λ¬λ€.μλ¬ μμΈ
upload λ videoμ titleμ ν΄λ¦ν΄ λ€μ΄κ° νμ΄μ§μ urlμ μ΄ν΄λ³΄λ©΄
videos/ λ€μ μ€λ id νλΌλ―Έν°μ μ«μμ λ¬Έμκ° μμ¬μλ κ²μ νμΈν μ μλ€.
κ·Έλ¬λ videoRouter.js νμΌμ λ°λ₯΄λ©΄ id νλΌλ―Έν°κ° μ«μμΌ κ²½μ°μλ§ watch 컨νΈλ‘€λ¬κ° μ€νλλ―λ‘ νμ΄μ§λ₯Ό κ°μ Έμ¬ μ μμ΄ μλ¬κ° λ¬ κ²μ΄λ€.
μλ¬ ν΄κ²° λ°©λ² 1
id νλΌλ―Έν°μ μ«μλ§ μ¬ μ μλλ‘ μΆκ°ν΄μ€ μ κ·ννμ(\\d+
)μ μμ νλ€.
λ€λ§, μ΄λ κ² μμ νλ©΄ /uploadμμ uploadλ id νλΌλ―Έν°λ‘ μΈμλλ μλ¬κ° λ°μνκΈ° λλ¬Έμ /upload νμ΄μ§λ₯Ό μ»λ μ½λλ₯Ό /:id νμ΄μ§λ₯Ό μ»λ μ½λλ³΄λ€ μμͺ½μ μμ±ν΄μΌ νλ€.
// VideoRouter.js
videoRouter.route("/upload").get(getUpload).post(postUpload);
videoRouter.get("/:id", watch);
videoRouter.route("/:id/edit").get(getEdit).post(postEdit);
μλ¬ ν΄κ²° λ°©λ² 2
MongoDB documentationμμ idμ λν΄ μ‘°μ¬ν΄ μ΄λ₯Ό μ°Έκ³ νμ¬ μ κ·ννμμ λ§λ λ€.
MongoDBκ° μ 곡νλ id
λ 24λ°μ΄νΈ 16μ§μ λ¬Έμμ΄
λ‘ μ΄λ£¨μ΄μ Έ μλ€.
16μ§μ λ¬Έμμ΄μ΄λ 0λΆν° 9κΉμ§μ 10κ°μ μ«μμ AλΆν° FκΉμ§μ 6κ°μ λ¬Έμλ‘ μ΄λ£¨μ΄μ§ λ¬Έμμ΄μ λ§νλ€.
μ΄λ₯Ό μ κ·ννμμ μ΄μ©ν΄ λνλ΄λ©΄ [0-9a-f]{24}
μ΄ λλ€.
μ΄λ 0λΆν° 9 κ·Έλ¦¬κ³ aλΆν° fκΉμ§μ 24μμ§λ¦¬ λ¬Έμμ΄μ μλ―Ένλ€.
μ΄λ₯Ό id νλΌλ―Έν° λ€μ μΆκ°ν΄μΌ νλ€.
// videoRouter.js
videoRouter.get("/:id([0-9a-f]{24})", watch);
videoRouter.route("/:id([0-9a-f]{24})/edit").get(getEdit).post(postEdit);
videoRouter.route("/upload").get(getUpload).post(postUpload);
κ·Έλ¬λ μ΄λ κ² μμ±νλ©΄ Homeμμ videoμ titleμ ν΄λ¦νμ λ, watch 컨νΈλ‘€λ¬κ° μλμ νμ§λ§, video.pug νμΌμ video λ³μκ° undefined
λΌμ μλ¬κ° λ°μνλ€.
μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ videoController.js νμΌμ watch 컨νΈλ‘€λ¬μμ λ°μ΄ν°λ² μ΄μ€λ‘λΆν° video λ°μ΄ν°λ₯Ό λ°μμ μ΄λ₯Ό watch.pug νμΌλ‘ λ λλ§ ν΄μΌ νλ€.
Model.findOne()
μ
λ ₯ν λͺ¨λ 쑰건μ λ§μ‘±νλ λ°μ΄ν°λ₯Ό μ°Ύμμ€λ€.
Model.findById()
ν΄λΉ idλ₯Ό κ°μ§λ λ°μ΄ν°λ₯Ό μ°Ύμμ€λ€.
// videoController.js
export const watch = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
// const video = await Video.findById(id).exec();
// .exec()μ λ€μ λ§λΆμ΄λ©΄, mongoose λ΄λΆμ μΌλ‘ promiseλ₯Ό return νλ€
// μ¬κΈ°μλ async & awaitμ μ¬μ©νμΌλ―λ‘ μ μ¨μ€λ λλ€.
return res.render("watch", { pageTitle: video.title, video });
};
μ΄κ±Έλ‘ userλ Homeμμ λͺ¨λ videoλ€μ λ³Ό μ μκ² λμκ³ , κ°κ°μ videoμ μμΈ νμ΄μ§μλ μ μν μ μκ² λμλ€.
μ¬μ©μκ° μ‘΄μ¬νμ§ μλ
24λ°μ΄νΈ 16μ§μ λ¬Έμμ΄ idλ₯Ό μ
λ ₯ν΄ video νμ΄μ§λ₯Ό λ°©λ¬Έ
νλ©΄ μλ¬κ° λ°μνλ©΄μ λΈλΌμ°μ λ 무ν λ‘λ©νκ² λλ€.
μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ μμ κ°μ μλ¬ λ°μ μ 404 νμ΄μ§λ₯Ό λ λλ§νλλ‘ νλ€.
views ν΄λ μμ 404.pug νμΌμ λ§λ ν base.pug νμΌμ extends νλ€.
// videoController.js
export const watch = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
if (video) {
return res.render("watch", { pageTitle: video.title, video });
}
return res.render("404", { pageTitle: "Video not found" }); // μλ¬
};
+base.pug νμΌμ HomeμΌλ‘ κ°λ λ§ν¬λ₯Ό μΆκ°νλ€.