[TIL] 211128

Lee SyongΒ·2021λ…„ 11μ›” 28일
0

TIL

λͺ©λ‘ 보기
102/204
post-thumbnail

πŸ“ 였늘 ν•œ 것

  1. 볡슡 - node.js / express / routers / templates / MongoDB와 Mongoose / CRUD

πŸ“š 배운 것

user 파트 λ“€μ–΄κ°€κΈ° 전에 μ—¬νƒœκΉŒμ§€ 배운 λ‚΄μš©(node.js ~ video 파트) λ‹€μ‹œ λ³΅μŠ΅ν•¨
ν—·κ°ˆλ¦¬κ±°λ‚˜ λ‹€μ‹œ 봀으면 쒋을 것 같은 뢀뢄은 μƒμ„Έν•˜κ²Œ, λ‚˜λ¨Έμ§€ 뢀뢄은 전체 흐름을 읡히기 μœ„ν•΄ κ°„λž΅ν•˜κ²Œ μ •λ¦¬ν•˜λŠ” μ‹μœΌλ‘œ μž‘μ„±ν•¨

1. node.js

  • git init / npm init

  • ν„°λ―Έλ„μ—μ„œ package.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) 미듀웨어(middleware)

(1) next()

request와 response μ‚¬μ΄μ—μ„œ requestλ₯Ό μ§€μ†μ‹œν‚€λŠ” ν•¨μˆ˜μ΄λ‹€.
expressκ°€ requestλ₯Ό ν™•μΈν•˜λ©΄ μˆœμ„œλŒ€λ‘œ handler ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ”λ°
본문에 next()κ°€ 있으면 κ·Έ ν•¨μˆ˜λŠ” handler이자 middleware이닀.

const middleware = (req, res, next) => {
  console.log("이건 미듀웨어닀");
  next();
};

const handleHome  = (req, res, next) => {
  return res.end();
};

app.get("route", middleare, handleHome);

(2) app.use()

global middlewareλ₯Ό λ§Œλ“€μ–΄μ€€λ‹€.
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. λΌμš°ν„°(routers)

routerλŠ” url을 κ·Έλ£Ήν™” ν•œλ‹€.

1) μ–΄λ–€ μ’…λ₯˜μ˜ 데이터λ₯Ό μ‚¬μš©ν•  것인가

이λ₯Ό κΈ°μ€€μœΌλ‘œ 도메인을 생각해본닀.

globalRouter /으둜 μ‹œμž‘
videoRouter /videos둜 μ‹œμž‘
userRouter /users둜 μ‹œμž‘

2) routers & conrollters 생성 및 μ‚¬μš©

일단은 server.js νŒŒμΌμ— λ§Œλ“€μ—ˆλ‹€.
(μ•„λž˜ route듀은 일뢀뢄일 뿐, μ „λΆ€κ°€ μ•„λ‹˜)

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) λͺ¨λ“ˆν™”

routers 폴더와 controllers 폴더λ₯Ό λ§Œλ“€μ–΄ 각 νŒŒμΌμ— λΌμš°ν„°μ™€ 컨트둀러λ₯Ό λ‚˜λˆ μ€€λ‹€.
λΌμš°ν„°μ™€ 컨트둀러λ₯Ό export (default) & import ν•œλ‹€.
+express도 각 νŒŒμΌλ§ˆλ‹€ import ν•΄μ€˜μ•Ό ν•œλ‹€.

4) url νŒŒλΌλ―Έν„°

url νŒŒλΌλ―Έν„°λ₯Ό μ΄μš©ν•΄ url에 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

req.params: { νŒŒλΌλ―Έν„° 이름: "νŒŒλΌλ―Έν„° κ°’" }

req.paramsλŠ” url νŒŒλΌλ―Έν„° 이름과 κ·Έ 값을 담은 object이닀.

// 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

ν•œνŽΈ, video.pug 파일의 video mixins에 μž‘μ„±ν•œ μ½”λ“œμ— λ”°λ₯΄λ©΄,
Home에 λ‚˜μ—΄λœ 각 videoλ“€μ˜ title을 ν΄λ¦­ν•˜λ©΄, 각 video의 video.idκ°€ ν¬ν•¨λœ url둜 μ΄λ™ν•˜κ²Œ λœλ‹€.

μ΄λ•Œ video.pug νŒŒμΌμ—μ„œ id νŒŒλΌλ―Έν„°(:id) μžλ¦¬μ— video.idκ°€ μœ„μΉ˜ν•΄ 있기 λ•Œλ¬Έμ—
id νŒŒλΌλ―Έν„°(:id)λŠ” 곧 video.id κ°€ λœλ‹€.

즉, req.params.id === id νŒŒλΌλ―Έν„°μ˜ κ°’ === video.id 이 λœλ‹€.

πŸ”₯ mongoose - Schema δΈ­ option: id & Mongoose - λͺ¨λ₯΄λŠ” 것 μ±„μ›Œλ†“κΈ° μ°Έκ³ 

그런데 λ‚˜λŠ” videoSchema에도 video documentλ₯Ό λ§Œλ“€ λ•Œλ„ id ν•„λ“œλ₯Ό λ§Œλ“  적이 μ—†λ‹€.
ν•˜μ§€λ§Œ κ°€μ§œ λ°μ΄ν„°λ² μ΄μŠ€λ‘œ ν…ŒμŠ€νŠΈν•  λ•Œ μ‚¬μš©ν–ˆλ˜ 이 μ½”λ“œλ₯Ό κ·ΈλŒ€λ‘œ μ‚¬μš©ν•΄λ„ μ—λŸ¬λŠ” λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.
이게 μ–΄λ–»κ²Œ 된 걸까?

β†’ MongoDBλŠ” 기본적으둜 각각의 video에 고유 식별 번호둜써 _idλΌλŠ” ν•„λ“œμ™€ 값을 μΆ”κ°€ν•΄μ€€λ‹€.
이 _id ν•„λ“œμ˜ 값은 .id 의 ν˜•μ‹μœΌλ‘œ λΆˆλŸ¬μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.


4. ν…œν”Œλ¦Ώ(templates)

1) pug

이제 url에 듀어갔을 λ•Œ μ›ν•˜λŠ” νŽ˜μ΄μ§€λ₯Ό 보여주도둝 컨트둀러λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.
pugλŠ” μ΄λŸ¬ν•œ λ·°λ₯Ό λ§Œλ“œλŠ” 것을 λ•λŠ” ν…œν”Œλ¦Ώμ΄λ‹€.

(1) view engine μ„€μ •

  • pugλ₯Ό view engine으둜 μ„€μ •ν•œλ‹€.
    이제 expressλŠ” HTML을 λ¦¬ν„΄ν•˜κΈ° μœ„ν•΄ pugλ₯Ό μ‚¬μš©ν•œλ‹€.
// server.js
app.set("view engine", "pug");

(2) view 폴더 경둜 μ„€μ •

  • src 폴더 μ•ˆμ— views 폴더λ₯Ό λ§Œλ“  ν›„, expressκ°€ pug νŒŒμΌμ„ 탐색할 views ν΄λ”μ˜ μœ„μΉ˜λ₯Ό μ„€μ •ν•œλ‹€. (기본값이 cwd이기 λ•Œλ¬Έμ— λ°”κΏ”μ€˜μ•Ό μ—λŸ¬κ°€ μ•ˆ λœ¬λ‹€.)
// server.js
app.set("views", process.cwd() + "/src/views");

(3) pug 파일 μž‘μ„±

  • =λ³€μˆ˜ / #{λ³€μˆ˜}

  • include(partials) / extends / block

  • 쑰건문(conditionals)

  • 반볡문(iteration)

ul
  each video in videos
    li=video

(4) 믹슀인(mixins)

λ‹€λ₯Έ 데이터λ₯Ό 가진 같은 ν˜•νƒœμ˜ HTML을 return ν•œλ‹€.
include와 ν•¨κ»˜ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.


5. MongoDB와 Mongoose

1) κ°€μ§œ λ°μ΄ν„°λ² μ΄μŠ€

watch video / edit video / upload video λ₯Ό λ‹€λ£Έ

(1) μ‚Όν•­ 쑰건 μ—°μ‚°μž

pug νŒŒμΌμ—μ„œ μžλ°”μŠ€ν¬λ¦½νŠΈ μ‚Όν•­ 쑰건 μ—°μ‚°μž μ‚¬μš©ν•˜κΈ°

block content
  h3 #{video.views} #{video.views === 1 ? "view" : "views"}

(2) req.body

req.body: { input의 name: "input의 value" }

req.bodyλ₯Ό μ΄μš©ν•΄ form(method="POST")μ—μ„œ μ „μ†‘ν•œ 값을 받을 수 μžˆλ‹€.

expressκ°€ form의 bodyλ₯Ό μ΄ν•΄ν•œ ν›„ μœ„μ™€ 같은 μžλ°”μŠ€ν¬λ¦½νŠΈ object둜 바꿔주도둝 ν•˜κΈ° μœ„ν•΄μ„œλŠ” middlewareλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

β€» 주의! λͺ¨λ“  middlewareλŠ” router 이전에 μž‘μ„±λ˜μ–΄μ•Ό ν•œλ‹€.
이 경우λ₯Ό 예둜 λ“€μžλ©΄, router 이전에 ν•΄λ‹Ή middlewareκ°€ μœ„μΉ˜ν•΄μ•Όλ§Œ requestκ°€ videoRouter에 λ„μ°©ν–ˆμ„ λ•Œ 이미 req.bodyκ°€ μ€€λΉ„λ˜μ–΄ μžˆμ„ 수 있기 λ•Œλ¬Έμ΄λ‹€.

// server.js
app.use(express.urlencoded({ extended: true }));

2) MongoDB & Mongoose μ„€μΉ˜ 및 μ—°κ²°

[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 νŒŒμΌμ„ import ν•œλ‹€.
db.js νŒŒμΌμ—μ„œ μ–΄λ–€ 것도 export 해주지 μ•Šμ•˜μ§€λ§Œ, db.js νŒŒμΌμ„ import ν•˜λŠ” κ²ƒλ§ŒμœΌλ‘œ μ„œλ²„λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ— μ—°κ²°λœλ‹€.

3) λͺ¨λΈ(Model)

(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;

model 폴더λ₯Ό λ§Œλ“  ν›„ κ·Έ μ•ˆμ— Video.js νŒŒμΌμ„ λ§Œλ“ λ‹€.
Video.js νŒŒμΌμ— videoSchema와 Video λͺ¨λΈμ„ λ§Œλ“  ν›„ 이λ₯Ό export default ν•œλ‹€.
Video λͺ¨λΈμ„ λͺ¨λ‘κ°€ μ‚¬μš©ν•  수 μžˆλ„λ‘ server.js νŒŒμΌμ— import ν•œλ‹€.

// server.js
import "./db";
import "./models/Video";

μ„œλ²„μ™€ λ°μ΄ν„°λ² μ΄μŠ€κ°€ μ—°κ²°λœ ν›„, 연결에 μ„±κ³΅ν–ˆλ‹€λ©΄ λ°μ΄ν„°λ² μ΄μŠ€λŠ” Video λͺ¨λΈμ„ μΈμ§€ν•˜κ²Œ λœλ‹€.
server.js νŒŒμΌμ—μ„œ λ‹Ήμž₯ Video λͺ¨λΈμ„ μ‚¬μš©ν•˜μ§€λŠ” μ•Šμ„ κ²ƒμ΄λ―€λ‘œ λ³€μˆ˜μ— λ°›μ•„μ˜€μ§€λŠ” μ•Šμ•˜μ§€λ§Œ, μ΄λ ‡κ²Œ μž‘μ„±ν•΄μ€˜μ•Ό 미리 Video λͺ¨λΈμ„ complie ν•¨μœΌλ‘œμ¨ μš°λ¦¬κ°€ 원할 λ•Œ 뢈러올 수 μžˆλ‹€.

(2) server.js와 init.js 파일둜 λ‚˜λˆ„κΈ°

server.js νŒŒμΌμ€ express λ“± μ„œλ²„ ꡬ성과 κ΄€λ ¨λœ κ²ƒλ“€λ§Œμ„ 닀루고,
init.js νŒŒμΌμ€ λ°μ΄ν„°λ² μ΄μŠ€μ™€ κ΄€λ ¨λœ 것듀을 import ν•œ ν›„ 이상이 μ—†μœΌλ©΄ μ„œλ²„λ₯Ό μ—¬λŠ” 역할을 ν•œλ‹€.

β€» 이제 app을 μ‹€ν–‰ν•˜λŠ” νŒŒμΌμ€ init.js νŒŒμΌμ΄λ―€λ‘œ package.json νŒŒμΌμ—μ„œ dev scriptλ₯Ό src/init.js 둜 μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

4) home video

(1) promise: async & await

이λ₯Ό μ‚¬μš©ν•˜λ©΄ μžλ°”μŠ€ν¬λ¦½νŠΈκ°€ λ°μ΄ν„°λ² μ΄μŠ€λ‘œλΆ€ν„° 데이터λ₯Ό λ°›μ•„μ˜¬ λ™μ•ˆ κ·Έ μ½”λ“œμ—μ„œ λ©ˆμΆ°μ„œ κΈ°λ‹€λ €μ€€λ‹€.
μž‘μ„±ν•œ μˆœμ„œλŒ€λ‘œ μ½”λ“œκ°€ μ‹€ν–‰λ˜μ–΄ 콜백 ν•¨μˆ˜λ³΄λ‹€ 훨씬 읽기가 νŽΈν•˜λ‹€.

// 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) videoSchema μˆ˜μ •

schema options
required / default / trim / maxLength / minLength

ν•΄μ‹œνƒœκ·Έ ν¬λ§·ν•˜λŠ” 방법 (이건 μ›λž˜ edit video νŒŒνŠΈμ— λ‚˜μ˜€λŠ”λ° κ·Έλƒ₯ μ—¬κΈ°μ„œ 같이 μˆ˜μ •ν•¨)
선택 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 μˆ˜μ •

videoλ₯Ό μƒˆλ‘­κ²Œ λ§Œλ“œλŠ” 게 μ•„λ‹ˆλΌ μˆ˜μ •ν•˜λŠ” κ²ƒμ΄λ―€λ‘œ 기쑴의 값이 input에 λ“€μ–΄κ°€ μžˆλ„λ‘ value 속성을 μΆ”κ°€ν•œλ‹€.

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에 route μΆ”κ°€

보톡 데이터λ₯Ό μ‚­μ œν•  λ•ŒλŠ” post requestλŠ” μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€.
url을 λ°©λ¬Έν•˜λ©΄ λ°”λ‘œ videoκ°€ μ‚­μ œλ˜λ„λ‘ ν•œλ‹€.

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) search 컨트둀러

videos 배열을 빈 λ°°μ—΄λ‘œ λ§Œλ“ λ‹€.
keywordκ°€ μž…λ ₯λ˜μ—ˆμœΌλ©΄ 이λ₯Ό μ΄μš©ν•΄ 쑰건과 μΌμΉ˜ν•˜λŠ” videoλ₯Ό μ°Ύμ•„ videos 배열을 μ—…λ°μ΄νŠΈν•œλ‹€.
쑰건에 λ§žλŠ” videoκ°€ 있으면 search νŽ˜μ΄μ§€μ— 이λ₯Ό 보여주고, μ—†μœΌλ©΄ search νŽ˜μ΄μ§€λ§Œ 보여쀀닀.

β€» search νŽ˜μ΄μ§€μ— 처음 왔을 λ•Œ req.queryλŠ” undefined 값을 가진닀. 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 });
};

✨ 내일 ν•  것

  1. user 파트 μ‹œμž‘!
profile
λŠ₯λ™μ μœΌλ‘œ μ‚΄μž, ν–‰λ³΅ν•˜κ²ŒπŸ˜

0개의 λŒ“κΈ€