[TIL] 211205

Lee SyongΒ·2021λ…„ 12μ›” 5일
0

TIL

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

πŸ“ 였늘 ν•œ 것

  1. λΉ„λ””μ˜€ 파일 μ—…λ‘œλ“œ - multer middleware

  2. μ‚¬μš©μž ν”„λ‘œν•„ - populate() ( video owner / user videos )


πŸ“š 배운 것

user profile

1. video μ—…λ‘œλ“œ

1) input(type="file")

//- upload.pug

extends base

block content
  if errorMessage
    span=errorMessage
form(method="POST", enctype="multipart/form-data")
  label(for="video") Video File
  input(name="video", type="file", accept="videos/*", id="video")
//- μ΄ν•˜ μƒλž΅

2) multer middleware

전에 λ§Œλ“€μ—ˆλ˜ uploadFiels middlewareλ₯Ό 각각 파일 크기 μ œν•œ μ˜΅μ…˜μ„ μΆ”κ°€ν•˜μ—¬ uploadAvatar와 uploadVideo middleware둜 λ‚˜λˆ„μ–΄ λ§Œλ“€μ—ˆλ‹€.

// middlewares.js
export const uploadAvatar = multer({ dest: "uploads/avatars/"}, { limits: { filesize: 5000000 }}); // 5MB μ œν•œ

export const uploadVideo = multer({ dest: "uploads/videos/"}, { limits: { filesize: 10000000 }}); // 10MB μ œν•œ

3) videoRouter

// videoRouter.js
import { uploadVideo } from "../middlewares";

videoRouter.route("/upload").all(protectorMiddleware).get(getUpload).post(uploadVideo.single("video"), postUpload);

4) videoSchema, postUpload 컨트둀러

Video.js νŒŒμΌμ—μ„œ videoSchema에 fileUrl을 μΆ”κ°€ν–ˆλ‹€.

// Video.js
const videoSchema = new mongoose.Schema({
  title: { type: String, required: true, trim: true, maxLength: 80 },
  fileUrl: { type: String, required: true },
  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 },
  },
});

videoController.js νŒŒμΌμ—μ„œ postUpload 컨트둀러λ₯Ό μˆ˜μ •ν–ˆλ‹€.

// videoController.js
export const postUpload = async (req, res) => {
  const {
    body: { title, description, hashtags },
    file, // μΆ”κ°€ ❗
  } = req;
  try {
    await Video.create({
      title,
      fileUrl: file.path, // μΆ”κ°€ ❗
      description,
      hashtags,
    });
  } catch(error) {
    return res.render("upload", { pageTitle: "Upload Video", errorMessage: error._message });
  }
  return res.redirect("/");
};

μ΄λŠ” ES6 문법을 μ΄μš©ν•΄ λ‹€μŒκ³Ό 같이 λ°”κΏ”λ³Ό μˆ˜λ„ μžˆλ‹€.

export const postUpload = async (req, res) => {
  const { path: fileUrl } = req.file; // μˆ˜μ • ❗
  const { title, description, hashtags } = req.body; // μˆ˜μ • ❗
  try {
    await Video.create({
      title,
      fileUrl, // μˆ˜μ • ❗
      description,
      hashtags,
    });
  } catch(error) {
    return res.render("upload", { pageTitle: "Upload Video", errorMessage: error._message });
  }
  return res.redirect("/");
};

5) video mixins, watch.pug μˆ˜μ •

video.pug와 watch.pug νŒŒμΌμ— video νƒœκ·Έλ₯Ό μΆ”κ°€ν–ˆλ‹€.

//- video.pug

mixin video(video)
  div 
    h4
      a(href=`/videos/${video.id}`)=video.title
    video(src="/" + video.fileUrl, width="800", controls)
    p=video.description
    ul
      each hashtag in video.hashtags
        li=hashtag
    small=video.createdAt
    hr
//- watch.pug

extends base

block content
  video(src="/" + video.fileUrl, width="800", controls)
  div
    p=video.description
    small=video.createdAt
  a(href=`${video.id}/edit`) Edit Video →
  br
  a(href=`${video.id}/delete`) Delete Video →

이제 video νŒŒμΌμ„ μ—…λ‘œλ“œ ν•˜λ©΄ videoκ°€ λœ¨λŠ” 것을 확인할 수 μžˆλ‹€.


2. μ‚¬μš©μž ν”„λ‘œν•„

μ‚¬μš©μžκ°€ λ‹€λ₯Έ μ‚¬μš©μžμ˜ ν”„λ‘œν•„μ„ ν΄λ¦­ν•˜λ©΄ ν•΄λ‹Ή μ‚¬μš©μžμ˜ 이름, 아바타, μ—…λ‘œλ“œν•œ μ˜μƒ 등을 λ³Ό 수 μžˆλ„λ‘ λ§Œλ“€ 것이닀.

μ˜μƒ 밑에 μ˜μƒμ„ 올린 μ‚¬μš©μžμ˜ 이름을 ν‘œμ‹œν•˜κ³ , μ˜μƒμ„ 올린 μ‚¬μš©μžκ°€ μ•„λ‹ˆλΌλ©΄ κ·Έ λ°‘μ˜ edit / delete λ§ν¬λŠ” λ³Ό 수 없도둝 ν•  것이닀.

userSchema에 videoListλ₯Ό μΆ”κ°€ν•˜κ³ , videoScheam에 ownerλ₯Ό μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.

1) my profile 링크 μΆ”κ°€

base.pug νŒŒμΌμ— my profile 메뉴λ₯Ό μΆ”κ°€ν–ˆλ‹€.
이 url은 λ‘œκ·ΈμΈν•œ user의 정보λ₯Ό 기반으둜 κ·Έ _idλ₯Ό 가져와 λ§Œλ“€μ—ˆμ§€λ§Œ, λˆ„κ΅¬λ‚˜ μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•΄λ‹Ή νŽ˜μ΄μ§€λŠ” λͺ¨λ‘μ—κ²Œ κ³΅κ°œν•  것이닀.

//- base.pug

li
  a(href=`/users/${loggedInUser._id}`) My Profile

2) userRouter

// userRouter.js
userRouter.get("/:id([0-9a-f]{24})", see);

3) see 컨트둀러

λˆ„κ΅¬λ‚˜ λ‹€λ₯Έ user의 profile을 λ³Ό 수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•΄ req.session.userμ—μ„œ _idλ₯Ό 가지고 μ˜€λŠ” 게 μ•„λ‹ˆλΌ req.paramsμ—μ„œ idλ₯Ό 가지고 와야 ν•œλ‹€.

// userController.js
export const see = async (req, res) => {
  const { id } = req.params;
  const user = await User.findById(id);
  if (!user) {
    return res.render("404", { pageTitle: "User not found" });
  }
  reutrn res.render("profile", { pageTitle: user.name, user });
};

4) profile.pug 생성

//- profile.pug

extends base

5) video와 userλ₯Ό μ—°κ²°

video owner / videoList

userμ—λŠ” ν•΄λ‹Ή userκ°€ μ—…λ‘œλ“œν•œ λͺ¨λ“  video의 idλ₯Ό μ €μž₯ν•œλ‹€.
videoμ—λŠ” ν•΄λ‹Ή video을 μ—…λ‘œλ“œν•œ user의 idλ₯Ό μ €μž₯ν•œλ‹€.

(1) videoSchema에 ownerλ₯Ό μΆ”κ°€

video에 ν•΄λ‹Ή videoλ₯Ό μ—…λ‘œλ“œν•œ user의 idλ₯Ό μ €μž₯ν•˜λ €κ³  ν•œλ‹€.
이λ₯Ό μœ„ν•΄ Video.js νŒŒμΌμ—μ„œ videoSchema에 ownerλ₯Ό μΆ”κ°€ν–ˆλ‹€.

type은 ObjectIdκ°€ μ•„λ‹ˆλΌ mongoose.Schema.Types.ObjectId라고 써야 ν•œλ‹€.
refλŠ” mongooseμ—κ²Œ owner에 User model의 idλ₯Ό μ €μž₯ν•˜κ² λ‹€κ³  μ•Œλ €μ£ΌκΈ° μœ„ν•΄ μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.

// Video.js
const videoSchema = new mongoose.Schema({
  // μƒλž΅
  owner: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
});

(2) videoController.jsμ—μ„œ postUpload 컨트둀러 μˆ˜μ •

video.owner에 ν˜„μž¬ 둜그인 ν•œ user의 _idλ₯Ό λ„£μ–΄μ€€λ‹€ (video와 user μ—°κ²°)

// videoController.js
export const postUpload = (req, res) = {
  const {
    session: {
      user: { _id }, // μΆ”κ°€ ❗
    },
    body: { title, description, hashtags },
    file,
  } = req;
  try {
    await Video.create({
      fileUrl: file.path,
      title,
      description,
      hashtags: Video.formatHashtags(hashtags),
      owner: _id, // μΆ”κ°€ ❗
    });
  } catch (error) {
    return res.status(400).render("upload", {
      pageTitle: "Upload Video",
      errorMessage: error._message,
    });
  }
  return res.redirect("/");
};

이제 videoλ₯Ό upload ν•œ ν›„ DBλ₯Ό 확인해보면 ownerκ°€ μΆ”κ°€λœ 것을 확인할 수 μžˆλ‹€.
κ·Έ 값은 videoλ₯Ό upload ν•œ user의 _id 값이닀.
video와 userκ°€ μ—°κ²°λ˜μ—ˆλ‹€!

> db.users.find()
{
  "_id" : ObjectId("61ac2ca850178cd5ec48b727"),
  "email" : "syong@naver.com",
  "avatarUrl" : "https://avatars.githubusercontent.com/u/89576038?v=4",
  "username" : "LeeSyong",
  "password" : "$2b$05$Ph6WZ/R1cdrM4wcZ0NUBAucc7BNXEEDornqN6sGz703aUvKUtctrO",
  "socialOnly" : true,
  "name" : "LeeSyong",
  "location" : null,
  "__v" : 0
}

> db.videos.find()
{
  "_id" : ObjectId("61ac2e2365004e56d46c9966"),
  "title" : "겨울",
  "fileUrl" : "uploads/vidoes/375d678307db8686e3eaa0e92b227416",
  "description" : "크리슀마슀 λΆ„μœ„κΈ°μ˜ λ”°λœ»ν•œ μ˜μƒμž…λ‹ˆλ‹€",
  "hashtags" : [ "#winter", "#video", "#christmas" ],
  "mata" : { "views" : 0, "rating" : 0 },
  "owner" : ObjectId("61ac2ca850178cd5ec48b727"),
  "createdAt" : ISODate("2021-12-05T03:12:35.713Z"),
  "__v" : 0
}

(3) edit / delete 링크 숨기기

String(video.owner) === loggedInUser._id라면, ν•΄λ‹Ή videoλ₯Ό upload ν•œ user와 ν˜„μž¬ λ‘œκ·ΈμΈν•œ userκ°€ 같은 μ‚¬λžŒμ΄λž€ λœ»μ΄λ―€λ‘œ edit / delete 링크λ₯Ό λ³Ό 수 μžˆλ„λ‘ ν•œλ‹€.

μ΄λ•Œ video.owner은 ObjectId인 데 λ°˜ν•΄, loggedInUser._idλŠ” Stringμ΄λ―€λ‘œ video.owner에 String()을 μ”Œμ›Œμ•Ό ν•œλ‹€.

//- watch.pug

if String(video.owner) === loggedInUser._id
  a(href=`${video.id}/edit`) Edit Video →
  br
  a(href=`${video.id}/delete`) Delete Video →

(4) video owner ν‘œμ‹œν•˜κΈ°

λ‹€μŒμœΌλ‘œ videoλ₯Ό 보러 듀어갔을 λ•Œ video을 upload ν•œ user의 이름을 ν‘œμ‹œν•˜κΈ° μœ„ν•΄ watch μ»¨νŠΈλ‘€λŸ¬μ™€ watch.pug νŒŒμΌμ„ λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•˜μ˜€λ‹€.

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

extends base

block content
  video(src="/" + video.fileUrl, width="800", controls)
  div
    p=video.description
    small=video.createdAt
  div
    small Uploaded by 
    a(href=`/users/${owner._id}`) #{owner.name}
  if String(video.owner) === loggedInUser._id
    a(href=`${video.id}/edit`) Edit Video →
    br
    a(href=`${video.id}/delete`) Delete Video →

κ·ΈλŸ¬λ‚˜, videoSchema의 ref: "User"에 μ˜ν•΄ video.ownerκ°€ 곧 ν•΄λ‹Ή videoλ₯Ό 올린 userλΌλŠ” 것을 μ•„λŠ”λ°, User DBμ—μ„œ λ‹€μ‹œ 검색을 ν•΄μ£ΌλŠ” 것은 λ„ˆλ¬΄ λΉ„νš¨μœ¨μ μ΄λ‹€.
이 점을 κ°œμ„ ν•˜κΈ° μœ„ν•΄ 두 번째 DB 검색 κ²°κ³ΌλŠ” μ§€μš°κ³ , λŒ€μ‹  populate("owner")λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

// videoController.js
export const watch = (req, res) => {
  const { id } = req.params; 
  const video = await Video.findById(id).populate("owner"); // μˆ˜μ • ❗
  if (!video) {
    return res.render("404", { pageTitle: "Video not found" });
  }
  return res.render("watch", { pageTitle: video.title, video });
};

populate()λ₯Ό μ‚¬μš©ν•˜λ©΄, video.owner에 videoλ₯Ό 올린 user의 _idκ°€ μ•„λ‹ˆλΌ ν•΄λ‹Ή user objectκ°€ ν†΅μ§Έλ‘œ λ“€μ–΄μ˜€κ²Œ λœλ‹€.
mongooseλŠ” video.owner에 듀어와야 ν•  값이 ObjectId인데 κ·Έ 값이 Userλ‘œλΆ€ν„° μ˜¨λ‹€λŠ” 것을 μ•Œκ³  μžˆμœΌλ―€λ‘œ video.ownerλ₯Ό ν•΄λ‹Ή user object둜 λ°”κΏ”μ£ΌλŠ” 것이닀.

console.log(video)λ₯Ό 톡해 μ‚΄νŽ΄λ³΄λ©΄ λ‹€μŒκ³Ό κ°™λ‹€

{
  mata: { views: 0, rating: 0 },
  _id: new ObjectId("61ac2e2365004e56d46c9966"),
  title: '겨울',
  fileUrl: 'uploads/vidoes/375d678307db8686e3eaa0e92b227416',
  description: '크리슀마슀 λΆ„μœ„κΈ°μ˜ λ”°λœ»ν•œ μ˜μƒμž…λ‹ˆλ‹€',
  hashtags: [ '#winter', '#video', '#christmas' ],
  owner: {
    _id: new ObjectId("61ac2ca850178cd5ec48b727"),
    email: 'syong@naver.com',
    avatarUrl: 'https://avatars.githubusercontent.com/u/89576038?v=4',
    username: 'LeeSyong',
    password: '$2b$05$Ph6WZ/R1cdrM4wcZ0NUBAucc7BNXEEDornqN6sGz703aUvKUtctrO',
    socialOnly: true,
    name: 'LeeSyong',
    location: null,
    __v: 0
  },
  createdAt: 2021-12-05T03:12:35.713Z,
  __v: 0
}

(5) user videos 보여주기

userController.js νŒŒμΌμ—μ„œ see 컨트둀러(profile νŽ˜μ΄μ§€λ₯Ό λ³΄μ—¬μ€Œ)λ₯Ό μˆ˜μ •ν•œλ‹€.
req.paramsλ‘œλΆ€ν„° idλ₯Ό μ–»μ–΄μ˜¨ ν›„ 이λ₯Ό 톡해 DBμ—μ„œ ν•΄λ‹Ή profile의 주인인 userλ₯Ό μ°ΎλŠ”λ‹€.
DBμ—μ„œ video.ownerκ°€ id(req.params) ν˜Ήμ€ user._id와 같은 video듀을 μ°ΎλŠ”λ‹€.
찾은 videosλ₯Ό profile ν…œν”Œλ¦Ώμ— λ„˜κ²¨μ€€ ν›„ profile ν…œν”Œλ¦Ώμ—μ„œ mixins/videoλ₯Ό include ν•˜λ©΄ profile νŽ˜μ΄μ§€μ—μ„œ ν•΄λ‹Ή userκ°€ μ—…λ‘œλ“œν•œ video듀을 λ³Ό 수 μžˆλ‹€.

export const see = async (req, res) => {
  const { id } = req.params;
  const user = await User.findById(id);
  const videos = await Video.find({ owner: user._id }); // ν˜Ήμ€ { owner: id } 라고 써도 됨
  if (!user) {
    return res.status(404).render("404", { pageTitle: "User not found" });
  }
  return res.render("profile", { pageTitle: user.name, user, videos });
};
//- profile.pug

extends base
include mixins/video

block content
  each video in videos
    +video(video)
  else
    li No Videos

ν•œνŽΈ, μ—¬κΈ°μ„œλ„ μœ„μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ populate("videos")λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

user에 ν•΄λ‹Ή userκ°€ μ—…λ‘œλ“œν•œ videoλ₯Ό μ—…λ‘œλ“œν•  λ•Œλ§ˆλ‹€ κ·Έ video의 idλ₯Ό μ €μž₯ν•˜λ €κ³  ν•œλ‹€.
이λ₯Ό μœ„ν•΄ User.js νŒŒμΌμ—μ„œ userSchema에 videosλ₯Ό μΆ”κ°€ν–ˆλ‹€.
videosλŠ” ObjectIdλ₯Ό κ°’μœΌλ‘œ κ°€μ§€λŠ” 배열이닀.

// User.js
const userSchema = new mongoose.Schema({
  // μƒλž΅
  videos: [{ type: mongoose.Schema.Types.ObjectId, ref: "Video" }],
});

videoλ₯Ό μ—…λ‘œλ“œ ν•  λ•Œ ν•΄λ‹Ή videoλ₯Ό μ—…λ‘œλ“œ ν•œ user의 videos에 κ·Έ video의 _idκ°€ μΆ”κ°€λ˜λ„λ‘, videoController.js νŒŒμΌμ—μ„œ postUpload 컨트둀러λ₯Ό μˆ˜μ •ν•œλ‹€.

// videoController.js
export const postUpload = async (req, res) => {
  const {
    session: {
      user: { _id },
    },
    body: { title, description, hashtags },
    file,
  } = req;
  try {
    const newVideo = await Video.create({
      file: file.path,
      title,
      description,
      hashtags: Video.formatHashtags(hashtags),
      owner: _id,
    });
    const user = await User.findById(_id);
    user.videos.push(newVideo._id);
    user.save();
  } catch(error) {
    return res.status(400).render("upload", { pageTitle: "Upload Video", errorMessage: error._message });
  }
  return res.redirect("/");
};

populate()λ₯Ό μ΄μš©ν•˜λ©΄ userλ₯Ό κ°€μ Έμ˜¬ λ•Œ user.videos의 _id 값듀을 κ·Έ _id 값을 가진 video object둜 λ°”κΏ”μ€€λ‹€.

// userController.js
export const see = async (req, res) => {
  const { id } = req.params;
  const user = await User.findById(id).populate("videos");
  // console.log(user);
  if (!user) {
    return res.status(404).render("404", { pageTitle: "User not found" });
  }
  return res.render("profile", { pageTitle: user.name, user });
};

console.log(user)λ₯Ό 톡해 μ‚΄νŽ΄λ³΄λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

{
  _id: new ObjectId("61ac7c5f048c24da7e9df72b"),
  email: 'syong@naver.com',
  avatarUrl: 'https://avatars.githubusercontent.com/u/89576038?v=4',
  username: 'LeeSyong',
  password: '$2b$05$DINZAMOVle5hwNO87rnVZekEJmF/vIMEvap8G7qBzi7va/Dulw86y',
  socialOnly: true,
  name: 'LeeSyong',
  location: null,
  videos: [
    {
      mata: [Object],
      _id: new ObjectId("61ac7c81048c24da7e9df72e"),
      title: '겨울',
      fileUrl: 'uploads/vidoes/49acd210e1f4c16ae6891addf62409f0',
      description: '겨울겨울겨울겨울겨울겨울겨울겨울겨울겨울',
      hashtags: [Array],
      owner: new ObjectId("61ac7c5f048c24da7e9df72b"),
      createdAt: 2021-12-05T08:46:57.617Z,
      __v: 0
    },
    {
      mata: [Object],
      _id: new ObjectId("61ac8160bd22b2db227bbc44"),
      title: 'μ“°λ‹΄μ“°λ‹΄',
      fileUrl: 'uploads/vidoes/33aa31574d8b086666e5fafc519fdb91',
      description: 'μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄μ“°λ‹΄',
      hashtags: [Array],
      owner: new ObjectId("61ac7c5f048c24da7e9df72b"),
      createdAt: 2021-12-05T09:07:44.008Z,
      __v: 0
    }
  ],
  __v: 3
}

6) 버그 μˆ˜μ •

(1) μ˜μƒμ„ 올릴 λ•Œλ§ˆλ‹€ νŒ¨μŠ€μ›Œλ“œκ°€ ν•΄μ‹±λ˜λŠ” 문제 ν•΄κ²°

videoλ₯Ό upload ν•  λ•Œ ν•΄λ‹Ή videoλ₯Ό upload ν•˜λŠ” user λ°μ΄ν„°μ˜ videos에 κ·Έ video의 _idλ₯Ό μ „ν•΄μ£Όλ©΄μ„œ user.save()λ₯Ό ν•˜λŠ”λ° μ΄λ•Œ User.js 파일의 pre save middleware에 μ˜ν•΄ passwordκ°€ hashing λœλ‹€.

그런데 μ΄λ ‡κ²Œ ν•˜λ©΄ 계정을 생성할 λ•Œ λ§Œλ“€μ—ˆλ˜ password의 ν•΄μ‹± 값이 또 ν•œλ²ˆ ν•΄μ‹±λ¨μœΌλ‘œμ¨ ν•΄λ‹Ή userλŠ” λ‘œκ·Έμ•„μ›ƒμ„ ν•˜λŠ” μˆœκ°„λΆ€ν„°λŠ” 이전과 같은 password둜 λ‘œκ·ΈμΈν•  수 μ—†κ²Œ λœλ‹€.

문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ isModified()λ₯Ό μ΄μš©ν•΄ passwordκ°€ μˆ˜μ •λ  λ•Œλ§Œ hashing 과정을 κ±°μΉ˜λ„λ‘ ν•΄μ•Ό ν•œλ‹€.

// User.js
userSchema.pre("save", async function() {
  // μ €μž₯ν•˜λ €λŠ” user의 passwordκ°€ μˆ˜μ •λœ κ²½μš°μ—λ§Œ μ €μž₯ 전에 hashing을 κ±°μΉ˜λ„λ‘ 함 ❗
  if (this.isModified("password")) {
    this.password = await bcrypt.hash(this.password, 5);
  }
});

thisλž€ μ €μž₯ν•˜λ €λŠ” user documentλ₯Ό λ§ν•œλ‹€.
this.isModified("password")λŠ” user object의 password propertyκ°€ μˆ˜μ •λ˜μ—ˆλ‹€λ©΄ trueλ₯Ό return ν•œλ‹€.

(2) ownerκ°€ μ•„λ‹Œ 둜그인 user도 video μˆ˜μ •/μ‚­μ œκ°€ κ°€λŠ₯ν•œ 문제 ν•΄κ²°

ν˜„μž¬λŠ” 둜그인만 λ˜μ–΄ 있으면 ν•΄λ‹Ή video의 ownerκ°€ μ•„λ‹ˆμ–΄λ„ μ£Όμ†Œ 창에 url을 직접 μž…λ ₯ν•˜μ—¬ edit video와 delete video νŽ˜μ΄μ§€μ— 접근이 κ°€λŠ₯ν•˜λ‹€.
λ‘œκ·ΈμΈν•œ user라 ν•˜λ”λΌλ„ video의 ownerκ°€ μ•„λ‹ˆλΌλ©΄ ν•΄λ‹Ή νŽ˜μ΄μ§€μ— μ ‘κ·Όν•  수 없도둝 ν•΄μ•Ό ν•œλ‹€.

λ¨Όμ €, videoController.js νŒŒμΌμ—μ„œ getEdit와 postEdit 컨트둀러λ₯Ό λͺ¨λ‘ μˆ˜μ •ν•œλ‹€. (postEdit 컨트둀러 λ˜ν•œ λ°±μ—”λ“œλ₯Ό λ³΄ν˜Έν•˜κΈ° μœ„ν•΄ λ˜‘κ°™μ΄ μˆ˜μ •ν•΄μ€˜μ•Ό ν•œλ‹€. μˆ˜μ •ν•œ postEdit μ»¨νŠΈλ‘€λŸ¬λŠ” 적지 μ•Šμ•˜λ‹€.)

β€» μ΄λ•Œ ObjectId(객체)와 req.session.user._id(λ¬Έμžμ—΄)λŠ” 데이터 νƒ€μž…μ΄ λ‹€λ₯΄λ‹€λŠ” 것에 μ£Όμ˜ν•˜μ—¬ String()을 μ΄μš©ν•΄μ•Ό ν•œλ‹€.

// videoController.js
export const getEdit = async (req, res) => {
  const {
    session: {
      user: { _id }, // μΆ”κ°€ ❗
    },
  } = req;
  const { id } = req.params;
  const video = await Video.findById(id);
  // videoκ°€ μžˆλŠ”μ§€ 확인
  if (!video) {
    return res.status(404).render("404", { pageTitle: "Video not found" });
  }
  // video의 owner인지 확인 ❗
  if (String(video.owner) !== _id) {
    return.status(403).redirect("/");
  } // status(403)은 forbidden을 λœ»ν•¨
  return res.render("edit", { pageTitle: `Edit ${video.title}`, video });
};

λ§ˆμ°¬κ°€μ§€λ‘œ, videoController.js νŒŒμΌμ—μ„œ deleteVideo 컨트둀러λ₯Ό μˆ˜μ •ν•œλ‹€.

// videoController.js
export const deleteVideo = async (req, res) => {
  const {
    session: {
      user: { _id }, // μΆ”κ°€ ❗
    },
  },
  const { id } = req.params;
  const video = await Video.findById(id);
  // videoκ°€ μžˆλŠ”μ§€ 확인
  if (!video) {
    return res.status(404).render("404", { pageTitle: "Video not found" });
  }
  // video의 owner인지 확인 ❗
  if (video.owner !== _id) {
    return res.status(403).redirect("/");
  }
  // video μ‚­μ œ
  await Video.findByIdAndDelete(id);
  return res.redirect("/");
};

μ •λ¦¬ν•˜λ©΄,

  1. ν•΄λ‹Ή videoκ°€ μ‘΄μž¬ν•˜λŠ”μ§€ ν™•μΈν•˜κ³ 
  2. μž‘μ—…μ„ μ§„ν–‰ν•˜λ €λŠ” userκ°€ video owner인지 ν™•μΈν•œ 후에
  3. edit video ν˜Ήμ€ delete video ν•΄μ•Ό ν•œλ‹€.

✨ 내일 ν•  것

  1. user profile μ²˜μŒλΆ€ν„° λ‹€μ‹œ κ΅¬ν˜„ν•΄λ³΄κΈ°

  2. κ°•μ˜ 계속 λ“£κΈ°

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

0개의 λŒ“κΈ€