[TIL] 211204

Lee Syong·2021년 12ė›” 4ėž
0

TIL

ëŠĐ록 ëģīęļ°
108/204
post-thumbnail

📝 ė˜Ī늘 한 ęēƒ

  1. edit profile / change password

  2. 파ėž ė—…ëĄœë“œ - multer / req.file / express.static()

  3. git restore --staged ė—ëŸŽ í•īęē°


📚 ë°°ėšī ęēƒ

user profile

1. edit profile

1) userRouter, edit-profile.pug, getEdit ėŧĻíŠļëĄĪ럮

ė—ŽíƒœęđŒė§€ ë°°ėšī ęēƒ ëģĩėŠĩ
githubė—ë§Œ ė˜ŽëĶž

2) loggedInUser is undefined ė—ëŸŽ

ė—ëŸŽ ë‚īėšĐ

로ę·ļėļ 하ė§€ ė•Šė€ user가 ė§ė ‘ ėĢžė†Œ ė°―ė— localhost:4000/users/editëĨž ėž…ë Ĩí•ī ë“Īė–īė˜Īë Īęģ  할 때 화ëĐīė—ëŠ” 'undefinedė˜ name ė†ė„ąė„ ė―ė„ ėˆ˜ ė—†ë‹Ī'는 ė—ëŸŽę°€ 뜮ë‹Ī.

ė—ëŸŽ ė›ėļ

로ę·ļėļė„ 하ė§€ ė•ŠėœžëĐī req.session.user가 undefined ėƒíƒœëžė„œ res.locals.loggedInUser도 undefined가 된ë‹Ī.
따띾ė„œ, edit-profile.pug 파ėžė—ė„œ undefined(loggedInUser)ė˜ name ė†ė„ąė„ ė―ė„ ėˆ˜ ė—†ë‹ĪëĐ° ė—ëŸŽę°€ ëœĻęēŒ 되는 ęēƒėīë‹Ī.

ė—ëŸŽ í•īęē°

로ę·ļėļ 하ė§€ ė•Šė€ user도 edit-profile 페ėīė§€ė— ė ‘ę·ží•  ėˆ˜ ėžˆë„록 localsMiddlewareëĨž ėˆ˜ė •í–ˆë‹Ī.
user가 로ę·ļėļ 하ė§€ ė•Šė•„ req.session.user가 undefinedėļ ęē―ėš°ė—ëŠ” res.locals.loggedInUser가 {}(undefined ė•„님)ė˜ 값ė„ 가ė§€ë„록 í•ĻėœžëĄœėĻ 로ę·ļėļ 하ė§€ ė•Šė€ userëĨž 폎í•Ļí•ī 누ęĩŽë“ ė§€ edit-profile 페ėīė§€ëĨž ëģž ėˆ˜ ėžˆë„록 했ë‹Ī.

// middlewares.js
export const localsMiddleware = (req, res) => {
  res.locals.siteName = "Wetube";
  res.locals.loggedIn = Boolean(req.session.loggedIn);
  res.locals.loggedInUser = req.session.user || {}; // ėˆ˜ė • ❗
  next();
};

3) routeëĨž ëģīí˜ļ하는 middleware

â€ŧ req.session.loggedIn 대ė‹ ė— res.locals.loggedInė„ ė‚ŽėšĐ할 ėˆ˜ë„ ėžˆë‹Ī. (req / res ėĢžė˜!)

(1) protectorMiddleware

로ę·ļėļ 하ė§€ ė•Šė€ user는 homeė—ė„œ edit profile ëДë‰īëĨž ëģž ėˆ˜ ė—†ėœžëŊ€ëĄœ ëДë‰īëĨž íīëĶ­í•ī edit profile 페ėīė§€ė— ë“Īė–īė˜Ž ėˆ˜ëŠ” ė—†ë‹Ī.
ę·ļ럮나, 로ę·ļėļ 하ė§€ ė•Šė€ user도 ė§ė ‘ ėĢžė†Œ ė°―ė— localhost:4000/users/editė„ ėž…ë Ĩí•ī ë“Īė–īė˜Īë Īęģ  할 ėˆ˜ ėžˆë‹Ī.

ėœ„ė—ė„œ ėī런 ęē―ėš°ė—ë„ (user가 로ę·ļėļ 하ė§€ ė•Šė€ ęē―ėš°ė—ë„) loggedInUserė˜ 값ėī undefined가 되ė§€ ė•Šë„록 ė„Īė •í–ˆë‹Ī.

ë‹Ī만, ė‹Īė œëĄœ 로ę·ļėļ 하ė§€ ė•Šė€ user는 edit profile 페ėīė§€ė— ë“Īė–īė™€ė„œëŠ” ė•ˆë˜ęļ° 때ëŽļė— í•īë‹đ 페ėīė§€ëĄœ ė ‘ę·ž ė‹œ login 페ėīė§€ëĄœ redirect ė‹œėžœė•ž 한ë‹Ī.
ėīëĨž ėœ„í•ī protectorMiddlewareëĨž 만ë“Īė–īė•ž 한ë‹Ī.

// middlewares.js
export const protectorMiddleware = (req, res, next) => {
  if (req.session.loggedIn) {
    next();
  } else {
    return res.redirect("/");
  }
};

edit profile 페ėīė§€ëŋ ė•„니띞 logout, upload video, edit video, delete video 페ėīė§€ė—ė„œ 또한, controller가 ė‹Ī행되ęļ° ė „ė— protectorMiddleware가 ė‹Ī행되도록 하ė—Ž, 페ėīė§€ė— ė ‘ę·žė„ ė‹œë„하는 user가 로ę·ļėļ된 userėļė§€ ė•„ë‹Œė§€ė— 따띾 각각 ë‹ĪëĨļ 페ėīė§€ëĨž ëģīė—ŽėĢžë„록 í•īė•ž 한ë‹Ī.

// userRouter.js
import { protectorMiddleware } from "../middlewares";

userRouter.get("logout", protectorMiddleware, logout);
userRouter.route("/edit").all(protectorMiddleware).get(getEdit).post(postEdit);
// videoRouter.js
import { protectorMiddleware } from "../middlewares";

videoRouter.route("/:id([0-9a-f]{24})/edit").all(protectorMiddleware).get(getEdit).post(postEdit);
videoRouter.route("/upload").all(protectorMiddleware).get(getUpload).post(postUpload);
videoRouter.route("/:id([0-9a-f]{24})/delete").all(protectorMiddleware).get(deleteVideo);

â€ŧ upload video ëДë‰ī는 로ę·ļėļ 한 user만 ëģž ėˆ˜ ėžˆë„록 base.pug 파ėžė„ ėˆ˜ė •í–ˆë‹Ī.

(2) publicOnlyMiddleware

한íŽļ, 반대로 로ę·ļėļ 되ė–ī ėžˆė§€ ė•Šė€ user만 ė ‘ę·ží•  ėˆ˜ ėžˆëŠ” 페ėīė§€ë„ ėžˆë‹Ī.
ėīëĨž ėœ„í•ī publicOnlyMiddlewareëĨž 만ë“Īė–īė•ž 한ë‹Ī.

// middlewares.js
export const publicOnlyMiddleware = (req, res, next) => {
  if (!req.session.loggedIn) {
    next();
  } else {
    return res.redirect("/");
  }
};

github 로ę·ļėļęģž ęī€ë Ļ된 route(github/start, github/finish)ė™€ join, login 페ėīė§€ė—ëŠ” 로ę·ļėļ 하ė§€ ė•Šė€ user만 ė ‘ę·ží•  ėˆ˜ ėžˆë„록 í•īė•ž 한ë‹Ī.

// userRouter.js
import { publicOnlyMiddleware } from "../middlewares";

userRouter.get("/github/start", publicOnlyMiddleware, startGithubLogin);
userRouter.get("/github/finish", publicOnlyMiddleware, finishGithubLogin);
// rootRouter.js
import { publicOnlyMiddleware } from "../middlewares";

rootRouter.route("/join").all(publicOnlyMiddleware).get(getJoin).post(postJoin);
rootRouter.route("/login").all(publicOnlyMiddleware).get(getLogin).post(postLogin);

4) postEdit ėŧĻíŠļëĄĪ럮

(1) ES6 객ėēī ëŽļëē•

ëĻžė € req.session.useręģž req.bodyė—ė„œ user ė •ëģīëĨž 가ė ļė™€ė•ž 한ë‹Ī.
ėī때 ES6 객ėēī ëŽļëē•ė„ ė‚ŽėšĐí•ī ëģīë‹Ī 간ęē°í•˜ęēŒ 표현할 ėˆ˜ ėžˆë‹Ī.

const id = req.session.user._id;
const { name, email, username, location } = req.body;

ėœ„ė™€ ė•„래ė˜ ė―”드는 동ėží•˜ë‹Ī.

const {
  session: {
    user: { _id },
  },
  body: { name, email, username, location },
} = req;

(2) DB ė—…데ėīíŠļ

ðŸ’Ą model.findByIdAndUpdate()

현ėžŽ 로ę·ļėļ 되ė–ī ėžˆëŠ” user objectė˜ _id ė†ė„ą 값ęģž user가 formė— ėž…ë Ĩ한 profile ėˆ˜ė • ė‚Ží•­ë“Īė„ 받ė•„ė™€ė„œ ėīëĨž model.findByIdAndUpdate()ëĨž ėīėšĐí•ī 데ėī터ëē ėīėŠĪëĨž ė—…데ėīíŠļ 할 ėˆ˜ ėžˆë‹Ī.

export const postEdit = async (req, res) => {
  const {
    session: {
      user: { _id },
    },
    body: { name, email, username, location },
  } = req;
  await User.findByIdAndUpdate(_id, {
    name,
    email,
    username,
    location,
  });
  return res.render("edit-profile", { pageTitle: "Edit Profile" });
};

ðŸ’Ą ëŽļė œ 발ėƒ

ę·ļ런데 ėī렇ęēŒ ėž‘ė„ąí•˜ëĐī DB는 ė—…데ėīíŠļ가 되ė§€ë§Œ, res.renderė— ė˜í•ī ë‹Īė‹œ 돌ė•„ę°„ edit-profile 페ėīė§€ė—ëŠ” ė—Žė „히 ėˆ˜ė • ė „ value 값ë“Īėī ėž…ë Ĩ되ė–ī ėžˆë‹Ī.

ę·ļ ėīėœ ëŠ” DBė—ė„œ user object는 ė—…데ėīíŠļ가 되ė—ˆė§€ë§Œ, sessionė€ ė—…데ėīíŠļ 되ė§€ ė•Šė•˜ęļ° 때ëŽļėīë‹Ī.

ėžė„ļ히 ė„Ī멅하ëĐī, edit-profile 페ėīė§€ė˜ formė˜ value 값ë“Īė€ loggedInUserė˜ ė†ė„ą 값ë“Īėīë‹Ī.
ę·ļ런데 req.session.userė˜ 값ėī localsMiddlewareė— ė˜í•ī ęģ§ res.locals.loggedInUserė˜ 값ėī 된ë‹Ī.
req.session.user는 로ę·ļėļ 할 때 postLogin ėŧĻíŠļëĄĪ럮ė—ė„œ 만ë“Īė–īė§„ ęēƒėœžëĄœėĻ user objectė˜ 값ė„ 가ė§„ë‹Ī.

ę·ļ럮ëŊ€ëĄœ ëŽļė œëĨž í•īęē°í•˜ęļ° ėœ„í•īė„œëŠ” req.session.userė— ėˆ˜ė •ëœ user objectëĨž ë‹Īė‹œ 할ë‹đí•īė•ž 한ë‹Ī.
DBė— ė—…데ėīíŠļ한 ë‚īėšĐė„ sessionė—ë„ ė—…데ėīíŠļ í•ĻėœžëĄœėĻ 프론íŠļė—”ë“œė—ë„ 반ė˜ë˜ë„록 í•īė•ž 한ë‹Ī.

(3) session ė—…데ėīíŠļ

ðŸ’Ą ė§ė ‘ session ė—…데ėīíŠļ

export const postEdit = async (req ,res) => {
  const {
    session: {
      user: { _id },
    },
    body: { name, email, username, location },
  } = req;
  // DB ė—…데ėīíŠļ
  await User.findByIdAndUpdate(_id, {
    name,
    email,
    username,
    location,
  });
  // session ė—…데ėīíŠļ
  req.session.user = {
    ...req.session.user, // ęļ°ėĄīė˜ 값ë“Īė„ ė „ë‹Ží•œ 후 ėˆ˜ė • ė‚Ží•­ë“Ī만 ëŪė–īė“°ęļ°
    name,
    email,
    username,
    location,
  };
  // renderëĨž redirect로 ėˆ˜ė •í•Ļ
  return res.redirect("/users/edit");
};

마ė§€ë§‰ė— res.render("edit-profile")ė„ redirect("/user/edit")ėœžëĄœ ėˆ˜ė •í•īė•ž 한ë‹Ī.
sessionė„ ė—…데ėīíŠļ한 후 localsMiddelwareëĨž ë‹Īė‹œ ëķˆëŸŽė™€ė•ž 하ęļ° 때ëŽļėīë‹Ī.
localsMiddlewareëĨž í†ĩí•ī ė—…데ėīíŠļ한 user ė •ëģīëĨž res.locals.loggedInUserė— 할ë‹đ할 ėˆ˜ ėžˆë‹Ī.
locasMiddleware는 routeëĨž ė°ūęļ° ė „ė— ė‹Ī행되ëŊ€ëĄœ ë‹Ļėˆœížˆ pug 파ėžė„ 렌더링 할 ęēŒ ė•„니띞 redirect í•īė•ž 한ë‹Ī.

ðŸ’Ą updatedUser ėƒė„ą

ęļ°ëģļė ėœžëĄœ model.findByIdAndUpdate()ė€ update 되ęļ° ė „ė˜ 데ėī터ëĨž return 한ë‹Ī.
update 된 후ė˜ 데ėī터ëĨž return 하도록 하ë ĪëĐī new: true ė„Īė •ė„ ėķ”ę°€í•īė•ž 한ë‹Ī.

â€ŧ User.findByIdAndUpdate()ė— 3개ė˜ ėļėžëĨž ėĢžęģ  ėžˆë‹Ī.

  • ė—…데ėīíŠļ 하ë Ī는 데ėī터ė˜ id
  • ė—…데ėīíŠļ 하ë Ī는 ė •ëģī
  • ė—…데ėīíŠļ가 끝난 ėĩœė‹ ė˜ 데ėī터ëĨž return 하도록 하는 new ė˜ĩė…˜
export const postEdit = async (req ,res) => {
  const {
    session: {
      user: { _id },
    },
    body: { name, email, username, location },
  } = req;
  // DB ė—…데ėīíŠļ
  const updatedUser = await User.findByIdAndUpdate(
    _id,
    {
      name,
      email,
      username,
      location,
    },
    { new: true }, // ėķ”ę°€ ❗
  );
  // session ė—…데ėīíŠļ
  req.session.user = updatedUser;
  // localsMiddleware가 ė‹Ī행되도록 redirect
  return res.redirect("/users/edit");
};

ė˜ë„í•œ 대로 ėž˜ ėž‘동한ë‹Ī.

ę·ļ럮나 한 가ė§€ ęģ ë Īí•īė•ž 할 ė‚Ží•­ėī ë‚Ļė•„ ėžˆë‹Ī.
ėˆ˜ė •ëœ usernameęģž emailėī ėīëŊļ 누ęĩ°ę°€ ė‚ŽėšĐ ėĪ‘ėī띞ëĐī ę·ļ 값ë“Ī로 ė—…데ėīíŠļ 되ė–īė„œëŠ” ė•ˆëœë‹Ī.

(4) username, email ėĪ‘ëģĩ ęē€ė‚Ž

ė―”ë“œ ėąŒëĶ°ė§€

ðŸ’Ą ė˜Īë‹ĩ

ėžë‹Ļė€ joinė—ė„œ ė‚ŽėšĐ했던 ë°Đëē•ė„ ėĻëģīė•˜ë‹Ī.

export const postEdit = async (req, res) => {
  const {
    session: {
      user: { _id },
    },
    body: { name, email, username, location },
  } = req;
  // username, email ėĪ‘ëģĩ ęē€ė‚Ž (ėķ”ę°€ ❗)
  const exists = await User.exists({ $or: [{ username }, { email }] });
  if (exists) {
    return res.render("edit-profile", {
      pageTitle: "Edit Profile",
      errorMessage: "This username/email is already taken.",
    });
  }
  // DB ė—…데ėīíŠļ
  const updatedUser = await User.findByIdAndUpdate(
    _id,
    {
      name,
      email,
      username,
      location,
    },
    { new: true }
  );
  // session ė—…데ėīíŠļ
  req.session.user = updatedUser;
  // localsMiddleware가 ė‹Ī행되도록 redirect
  return res.redirect("/users/edit");
};

ę·ļ럮나 ėī렇ęēŒ ėž‘ė„ąí•˜ëĐī, nameėī나 location만ė„ ëģ€ęē―하ęģ  usernameėī나 emailė€ ëģ€ęē―하ė§€ ė•Šė€ ęē―ėš°ė—ë„ ė—ëŸŽ ëДė‹œė§€ę°€ ëœĻęēŒ 되ė–ī, nameėī나 location만ė„ ëģ€ęē―할 ėˆ˜ę°€ ė—†ë‹Ī.

ðŸ’Ą í•īë‹ĩ

ėœ„ ë°Đëē• 대ė‹ ė— usernameęģž emailė˜ ėˆ˜ė • ė—Žëķ€ëĨž ëĻžė € 확ėļ한 후, ėˆ˜ė •ë˜ė—ˆë‹ĪëĐī 데ėī터ëē ėīėŠĪė— ę·ļ ėˆ˜ė •ëœ 값ęģž 동ėží•œ 값ėī ėĄīėžŽí•˜ëŠ”ė§€ 확ėļ하는 ë°Đëē•ė„ ė‚ŽėšĐ하ë Īęģ  한ë‹Ī.

export const postEdit = async (req ,res) => {
  // req.session.useręģž req.bodyė—ė„œ user ė •ëģīëĨž 가ė ļė˜ī
  const {
    session: {
      user: { _id },
    },
    body: { name, email, username, location },
  } = req;
  // username, email ėĪ‘ëģĩ ęē€ė‚Ž
  if (username !== req.session.user.username) { // formė—ė„œ usernameėī ėˆ˜ė •ë˜ė—ˆë‹ĪëĐī
    const exists = await User.exists({ username }); // DBė— ėˆ˜ė •ëœ 값ęģž 동ėží•œ 값ėī ėžˆëŠ”ė§€ 확ėļ
    if (exists) { // ėĪ‘ëģĩ된 값ėī ėžˆë‹ĪëĐī ė—ëŸŽ ëДė‹œė§€ ëģīė—ŽėĢžęļ°
      return res.render("edit-profile", { pageTitle: "Edit Profile", errorMessage: "This username is already taken."});
    }
  }
  if (email !=== req.session.user.email) { // formė—ė„œ ėˆ˜ė •ë˜ė—ˆë‹ĪëĐī
    const exists = await User.exists({ username }); // DBė— ėˆ˜ė •ëœ 값ęģž 동ėží•œ 값ėī ėžˆëŠ”ė§€ 확ėļ
    if (exists) { // ėĪ‘ëģĩ된 값ėī ėžˆë‹ĪëĐī ė—ëŸŽ ëДė‹œė§€ ëģīė—ŽėĢžęļ°
      return res.render("edit-profile", { pageTitle: "Edit Profile", errorMessage: "This email is already taken."});
    }
  }
  // ėˆ˜ė •ëœ username í˜đė€ emailėī DBė— ėžˆëŠ” 값ęģž ėĪ‘ëģĩ되ė§€ ė•ŠėŒėī 확ėļ되ëĐī
  // DB ė—…데ėīíŠļ
  const updatedUser = await User.findByIdAndUpdate(
    _id,
    {
      name,
      email,
      username,
      location,
    },
    { new: true },
  );
  // session ė—…데ėīíŠļ
  req.session.user = updatedUser;
  // localsMiddleware가 ė‹Ī행되도록 redirect
  return res.redirect("/users/edit");
};

5) password ëģ€ęē―

postEdit ėŧĻíŠļëĄĪ럮ė— í•Ļęŧ˜ ėž‘ė„ąí•˜ė§€ ė•Šęģ  password ëģ€ęē―ė„ ėœ„í•œ ėŧĻíŠļëĄĪ럮ëĨž 따로 만든 ėīėœ ëŠ”
ë‹ĪëĨļ user ė •ëģī는 로ę·ļėļ 하ė§€ ė•Šė€ ëŠĻ든 userë“Īėī ëģ€ęē―할 ėˆ˜ ė—†ëŠ” 반ëĐī,
password는 githubëĨž í†ĩí•ī 로ę·ļėļ 한 userë“ĪęđŒė§€ë„ ëģ€ęē―할 ėˆ˜ ė—†ęļ° 때ëŽļėīë‹Ī.
또한, password ëģ€ęē― ė‹œ User.findByIdAndUpdate() 말ęģ  user.save()ëĨž ė‚ŽėšĐ하ęģ ėž 한ë‹Ī.
ėī럮한 ė°Ļėīė ë“Ī 때ëŽļė— password ëģ€ęē―ė„ ėœ„í•œ 페ėīė§€ëĨž 따로 만ë“Īė—ˆë‹Ī.

(1) userRouter, change-password.pug, getChangePassword ėŧĻíŠļëĄĪ럮

ė—ŽíƒœęđŒė§€ 한 ęēƒ ëģĩėŠĩ
githubė—ë§Œ ė˜ŽëĶž

githubëĨž í†ĩí•ī 로ę·ļėļ 한 user는 change password 페ėīė§€ė— ė ‘ę·ží•  ėˆ˜ ė—†ė–īė•ž 한ë‹Ī.
github로 로ę·ļėļ 한 user는 'Change Passowrd →'ëĨž ëģž ėˆ˜ ė—†ë„록 edit-profile.pug 파ėžė„ ėˆ˜ė •í•œë‹Ī.

â€ŧ password로 로ę·ļėļ한 ė‚ŽëžŒë§Œ ė ‘ę·ží•īė•ž 하는 ėŧĻíŠļëĄĪ럮가 많ė•„ė§„ë‹ĪëĐī, ėīëĨž ė•žė—ė„œ 만ë“Īė—ˆë˜ protectorMiddlewareėē˜ëŸž middlewareëĨž 만ë“Īė–ī ėē˜ëĶŽí•  ėˆ˜ë„ ėžˆë‹Ī.

// change-password.pug

if !loggedInUser.socialOnly
  hr
  a(href="change-password") Change Password →

â€ŧ ėƒëŒ€ ęē―로ëĨž ė‚ŽėšĐí•īëīĪë‹Ī. ėī는 ė ˆëŒ€ ęē―로ėļ a(href="/users/change-password")ė™€ 같ë‹Ī.

(2) postChangePassword ėŧĻíŠļëĄĪ럮

ëĻžė €, 현ėžŽ ëđ„ë°€ëēˆí˜ļ(oldPassword)가 ėžėđ˜í•˜ëŠ”ė§€ 확ėļ한 후 ėƒˆëĄœėšī ëđ„ë°€ëēˆí˜ļ(newPassword)ė™€ ėƒˆëĄœėšī ëđ„ë°€ëēˆí˜ļ 확ėļ(newPassword2)ėī ėžėđ˜í•˜ëŠ”ė§€ 확ėļ한ë‹Ī.

ëŠĻ든 ęēŒ ëŽļė œ ė—†ë‹ĪëĐī ëđ„ë°€ëēˆí˜ļëĨž ëģ€ęē―한 후 DBė— ė €ėžĨí•īė•ž 한ë‹Ī.
ëđ„ë°€ëēˆí˜ļëĨž DBė— ė €ėžĨ할 때는 password hashingė„ ėœ„í•ī pre save middlewareëĨž ęą°ėģė•ž 한ë‹Ī.
ę·ļ런데 pre save middleware는 'User.create()' í˜đė€ 'user.save()'ëĨž í†ĩí•īė„œë§Œ ė‹Ī행 가ëŠĨ하ë‹Ī. (User.findByIdAndUpdate() ė‚ŽėšĐ ė‹œė—ëŠ” pre hookėī ėž‘동되ė§€ ė•ŠëŠ”ë‹Ī.)

ėī ęē―ėš°ė—ëŠ” user.save()ëĨž ė‚ŽėšĐ하ęļ° ėœ„í•ī ëĻžė € userëĨž ė°ūė•„ė•ž 한ë‹Ī.

export const postChangePassword = async (req, res) => {
  // req.session.useręģž req.bodyė—ė„œ user ė •ëģīëĨž 가ė ļė˜ī
  const {
    session: {
      user: { _id, password },
    },
    body: { oldPassword, newPassword, newPassword2 },
  } = req;
  // old password ėžėđ˜ ė—Žëķ€
  const ok = await bcrypt.compare(oldPassword, password);
  if (!ok) {
    return res.status(400).render("change-password", {
      pageTitle: "Change Password",
      errorMessage: "The current password is incorrect.",
    });
  }
  // new password 확ėļ ėžėđ˜ ė—Žëķ€
  if (newPassword !== newPassword2) {
    return res.status(400).render("change-password", {
      pageTitle: "Change Password",
      errorMessage: "Password confirmation does not match.",
    });
  }
  // password ëģ€ęē― - DB ė—…데ėīíŠļ ❗
  const user = await User.findById(_id);
  user.password = newPassword;
  await user.save();
  // session ė—…데ėīíŠļ ❗
  req.session.user.password = user.password;
  // 로ę·ļė•„ė›ƒė‹œí‚ī
  return res.redirect("/users/logout");
};

postChangePassword ėŧĻíŠļëĄĪ럮ė—ė„œë„ postEdit ėŧĻíŠļëĄĪ럮ė—ė„œė™€ 마ė°Žę°€ė§€ëĄœ 마ė§€ë§‰ė— sessionė„ ė—…데ėīíŠļ í•īė•ž 한ë‹Ī.

ėē˜ėŒ passwordëĨž ëģ€ęē―할 때는 ëŽļė œę°€ ė—†ė§€ë§Œ, 2ëēˆė§ļ로 passwordëĨž ëģ€ęē―할 때ëķ€í„°ëŠ” ėīė „ė— passwordëĨž ëģ€ęē―할 때 sessionė„ ė—…데ėīíŠļ í•īėĢžė§€ ė•Šė•˜ë‹ĪëĐī ëŽļė œę°€ 발ėƒí•œë‹Ī.

postChangePassword ėŧĻíŠļëĄĪ럮ė˜ ė•ž ëķ€ëķ„ė—ė„œ ęļ°ėĄīė˜ passwordëĨž req.session.user.passwordė™€ ëđ„ęĩí•˜ęģ  ėžˆęļ° 때ëŽļė— 바뀐 passwordëĨž sessionė— ė—…데ėīíŠļ í•īėĢžė§€ ė•ŠėœžëĐī await bcrypt.compare(oldPassword, password)ė˜ 값ė€ false가 되는 ęēƒėīë‹Ī.

따띾ė„œ, ë‹ĪëĨļ user ė •ëģīëĨž ëģ€ęē―할 때ė™€ 마ė°Žę°€ė§€ëĄœ passwordëĨž ëģ€ęē―할 때도 dbëĨž ė—…데ėīíŠļ 한 후ė— session도 같ėī ęž­ ė—…데ėīíŠļ í•īėĪ˜ė•ž 한ë‹Ī.

+í˜đė€ ė•„래ė™€ 같ėī ėž‘ė„ąí•œë‹ĪëĐī sessionė„ ė—…데ėīíŠļ í•īėĢžė§€ ė•Šė•„도 된ë‹Ī.
ėē˜ėŒëķ€í„° DBė—ė„œ userëĨž ëķˆëŸŽė™€ passwordëĨž ëđ„ęĩí•  때도 sessionėī ė•„니띞 DBė—ė„œ ëķˆëŸŽė˜Ī는 ęēƒėīë‹Ī.

export const postChangePassword = async (req, res) => {
  // req.session.useręģž req.bodyė—ė„œ user ė •ëģīëĨž 가ė ļė˜ī
  const {
    session: {
      user: { _id }, // ėˆ˜ė • ❗
    },
    body: { oldPassword, newPassword, newPassword2 },
  } = req;
  // old password ėžėđ˜ ė—Žëķ€
  const user = await User.findById(_id); // ėœ„ėđ˜ ėˆ˜ė • ❗
  const ok = await bcrypt.compare(oldPassword, user.password); // ėˆ˜ė • ❗
  if (!ok) {
    return res.status(400).render("change-password", {
      pageTitle: "Change Password",
      errorMessage: "The current password is incorrect.",
    });
  }
  // new password 확ėļ ėžėđ˜ ė—Žëķ€
  if (newPassword !== newPassword2) {
    return res.status(400).render("change-password", {
      pageTitle: "Change Password",
      errorMessage: "Password confirmation does not match.",
    });
  }
  // password ëģ€ęē― - DB ė—…데ėīíŠļ
  user.password = newPassword;
  await user.save();
  // 로ę·ļė•„ė›ƒė‹œí‚ī
  return res.redirect("/users/logout");
};

2. 파ėž ė—…ëĄœë“œ

github로 로ę·ļėļ하ė§€ ė•Šęģ  passwordëĨž ėž…ë Ĩí•ī ęģ„ė •ė„ 만든 user도 avatarëĨž ė—…ëĄœë“œí•  ėˆ˜ ėžˆęģ 
passwordëĨž ėž…ë Ĩí•ī ęģ„ė •ė„ 만ë“Īė§€ ė•Šęģ  github로 로ę·ļėļ한 user도 avatarëĨž ėˆ˜ė •í•  ėˆ˜ ėžˆë„록 할 ęēƒėīë‹Ī.

ė–īë–ŧęēŒ ë°ąė—”ë“œė— 파ėžė„ ëģī낾 ėˆ˜ ėžˆëŠ”ę°€

1) input(type="file")

edit-profile.pug 파ėžė— labelęģž input(type="file") 태ę·ļëĨž ėķ”ę°€í•œë‹Ī.

//- edit-profile.pug

extends base

if errorMessage
  span=errorMessage
form(method="POST")
  label(for="avatar") avatar
  input(name="avatar", type="file", accept="image/*", id="avatar")
//- ėī하 ėƒëžĩ

2) multer middleware

multer란 파ėžė„ ë‹Īėšī로드 할 ėˆ˜ ėžˆë„록 도ė™€ėĢžëŠ” middlewareëĨž 말한ë‹Ī.

(1) ė„Īėđ˜ 및 ė‚ŽėšĐ

Multer ė°ļęģ 

npmė„ ėīėšĐí•ī multerëĨž ė„Īėđ˜í•œë‹Ī.

$ npm i multer 

multerëĨž ėīėšĐí•ī 파ėžė„ ė—…ëĄœë“œ 하ęļ° ėœ„í•īė„œëŠ”(파ėžė„ ë°ąė—”ë“œëĄœ ëģīë‚īęļ° ėœ„í•īė„œëŠ”) formė„ multipart formėœžëĄœ 만ë“Īė–īė•ž 한ë‹Ī.
form 태ę·ļė— enctype="multipart/form-data" ė†ė„ąė„ ėķ”ę°€í•œë‹Ī.

//- edit-profile.pug

form(method="POST", enctype="multipart/form-data")
  label(for="avatar") avatar
  input(name="avatar", type="file", accept="image/*", id="avatar")
//- ėī하 ėƒëžĩ

middlewares.js 파ėžė— user가 ëģīë‚ļ 파ėžė„ ė„œëē„ė˜ uploads íī더ė— ė €ėžĨ하는 multer middlewareëĨž 만ë“Īė—ˆë‹Ī.

multer middlewareė—ëŠ” ė˜ĩė…˜ë“Īė„ ë‹īė€ 객ėēīëĨž ë„Ģė„ ėˆ˜ ėžˆë‹Ī.
dest는 destinationėœžëĄœė„œ 파ėžėī ė €ėžĨ될 ėœ„ėđ˜ëĨž 말한ë‹Ī.
ė§€ęļˆė€ ė„œëē„ė˜ uploads íī더 ėĶ‰, 하드 드띾ėīëļŒė— ė €ėžĨ하ė§€ë§Œ 나ėĪ‘ė— 바ęŋ€ ęēƒėīë‹Ī.
프로ė íŠļ íī더ė— uploads íī더ëĨž ėƒė„ąí•œ 후 .gitignore 파ėžė— ėķ”ę°€í•œë‹Ī.

// middlewares.js
export const uploadFiles = multer({ dest: "uploads/" });

uploadFiles middleware는 avatar ė‚Žė§„ė„ upload 할 때 ė‚ŽėšĐ되ëŊ€ëĄœ postEdit ėŧĻíŠļëĄĪ럮 ė•žė—ė„œ ė‚ŽėšĐ되ė–īė•ž 한ë‹Ī.
userRouter.js 파ėžė„ ėˆ˜ė •í•œë‹Ī.

// userRouter.js
userRouter.route(/edit) // url
  .all(protectorMiddleware)
  .get(getEdit)
  .post(uploadFiles.single("avatar"), postEdit);
  // (multer middleware.multer ëДė„œë“œ("formė—ė„œ 받ė•„ė˜Ž 파ėžė˜ name"), controller)

multer middleware는 user가 í•īë‹đ url로 파ėžė„ ëģīë‚īëĐī inputė—ė„œ 파ėžė„ 받ė•„ė„œ uploads íī더ė— ė €ėžĨ한 후 ę·ļ 파ėž ė •ëģīëĨž postEdit controllerė— ė „ë‹Ží•œë‹Ī.

(2) req.file

ėīëŊļė§€ 파ėžė„ ė—…ëĄœë“œ 하ëĐī uploadFiles.single("avatar")가 ė‹Ī행되ëĐīė„œ ë‹ĪėŒęģž 같ė€ req.fileėī 만ë“Īė–īė§„ë‹Ī.
ėī ėĪ‘ avatarUrlė„ ėœ„í•ī 필ėš”í•œ ęēƒė€ pathėīë‹Ī.

{
  fieldname: 'avatar',
  originalname: 'dd.gif',
  encoding: '7bit',
  mimetype: 'image/gif',
  destination: 'uploads/',
  filename: '2987fe268e63abe892b0a1c5932f6ee9',
  path: 'uploads/2987fe268e63abe892b0a1c5932f6ee9',
  size: 5075180
}

ėīëĨž 바탕ėœžëĄœ postEdit ėŧĻíŠļëĄĪ럮ëĨž ėˆ˜ė •í•  때 ęģ ë Īí•īė•ž 할 ė‚Ží•­ėī 두 가ė§€ ėžˆë‹Ī.

  1. 만ė•― user가 profileė„ ėˆ˜ė •í•  때 avatar 파ėžė„ ė—…ëĄœë“œí•˜ė§€ ė•ŠëŠ”ë‹ĪëĐī, file ëģ€ėˆ˜ė˜ 값ėī undefined가 되ė–ī pathëĨž ė°ūė„ ėˆ˜ ė—†ė–ī ė—ëŸŽę°€ 발ėƒí•˜ëŊ€ëĄœ pathëĨž ëģ€ėˆ˜ëĄœ 만ë“Īė§€ 말ęģ  fileė„ ëģ€ėˆ˜ëĄœ 만ë“Īė–īė•ž 한ë‹Ī.

  2. ęļ°ėĄīė— avatar가 ėžˆëŠ” user가 profileė„ ėˆ˜ė •í•  때 avatar 파ėžė„ ė—…ëĄœë“œí•˜ė§€ ė•Šė•˜ė–ī도 ė›ëž˜ ėžˆë˜ avatar가 ė‚Žëžė ļė„œëŠ” ė•ˆëœë‹Ī. (ėžˆė—ˆëŠ”데 ė—†ëŠ” ęąļ로 ëŪė–īė“°ęļ° 되ëĐī ė•ˆëœë‹Ī.)

// userController.js
export const postEdit = (req, res) => {
  const {
    session: {
      user: { _id, avatarUrl }, // avatarUrlė„ ėķ”ę°€ëĄœ 가ė ļė˜Ļë‹Ī.
    },
    body: { name, email, username, location },
    file, // file: { path } 띞ęģ  ė“°ëĐī 1ëēˆ ė—ëŸŽę°€ 발ėƒí•œë‹Ī.
  } = req;
  // ėĪ‘ëžĩ
  await User.findByIdAndUpdate(
    _id,
    {
      avatarUrl: file ? file.path : avatarUrl,
      // ę·ļëƒĨ file.path띞ęģ  ė“°ëĐī 2ëēˆ ė—ëŸŽę°€ 발ėƒí•œë‹Ī.
      name,
      email,
      username,
      location,
    },
    { new: true }
  );
};

ðŸ’Ą ėĪ‘ėš”í•œ ęēƒė€ 데ėī터ëē ėīėŠĪė—ëŠ” 파ėžė„ ė €ėžĨ하ė§€ ė•ŠëŠ”ë‹Ī는 ęēƒėīë‹Ī!
데ėī터ëē ėīėŠĪė—ëŠ” 파ėžė˜ ėœ„ėđ˜ë§Œ ė €ėžĨ한ë‹Ī.

(3) avatar ėīëŊļė§€ ëģīė—ŽėĢžęļ°

// edit-profile.pug

extends base

block content
  if errorMessage
    span=errorMessage
  img(src="/" + loggedInUser.avatarUrl, width="100", heigh="100")
  form(method="POST", enctype="multipart/form-data")
  label(for="avatar") avatar
  input(name="avatar", type="file", accept="image/*", id="avatar")
//- ėī하 ėƒëžĩ

ðŸ’Ą ė ˆëŒ€ ęē―로 ė‚ŽėšĐ하ęļ°

edit profile 페ėīė§€ė— img 태ę·ļëĨž ėķ”ę°€í–ˆë‹Ī.
src ė†ė„ąė˜ 값ė€, "/"ëĨž ė“°ė§€ ė•ŠėœžëĐī ėžë™ėœžëĄœ ėƒëŒ€ ęē―로(/users/uploads/ëļ”띞ëļ”띞)로 ëģ€í™˜ë˜ëŊ€ëĄœ ė•žė— "/"ëĨž ëķ™ė—ŽėĪ˜ė•ž 한ë‹Ī.
ėœ„ ė―”ë“œė˜ ęē°ęģž src 값ė€ /uploads/ds3jdksfj3kdflsfla2222kl ė™€ 같ė€ 값ė„ 가ė§€ęēŒ 된ë‹Ī.

ðŸ’Ą express.static("ëļŒëžėš°ė €ė— ë…ļėķœė‹œí‚Ž íī더 ėīëĶ„")

ę·ļ럮나, ėœ„ė™€ 같ėī ėž‘ė„ąí•ī도 화ëĐīė—ëŠ” avatar ėīëŊļė§€ę°€ ëœĻė§€ ė•ŠëŠ”ë‹Ī.
ėī는 express가 uploads띾는 ęē―로가 ėķ”ę°€ë˜ė—ˆėŒė„ ëŠĻëĨīęģ , ëļŒëžėš°ė €ę°€ uploads íī더ė— ėžˆëŠ” 파ėžė„ ëģž ėˆ˜ ė—†ęļ° 때ëŽļėīë‹Ī.

ëŽļė œëĨž í•īęē°í•˜ęļ° ėœ„í•īė„œëŠ” sever.jsė—ė„œ 파ėžė—ė„œ /uploads ęē―로ëĨž ėķ”ę°€í•˜ęģ , ëļŒëžėš°ė €ę°€ ė–īë–Ī íī더ëĨž ëģž ėˆ˜ ėžˆė„ė§€ ė„Īė •í•īė•ž 한ë‹Ī. (static files serving)
express.static()ė„ ėīėšĐí•ī íī더 ë‚īė˜ ëŠĻ든 파ėžė„ ëļŒëžėš°ė €ė— ë…ļėķœė‹œí‚Ž ėˆ˜ ėžˆë‹Ī.

// server.js
app.use("/uploads", express.static("uploads"));

누ęĩ°ę°€ /uploads로 가ë Īęģ  하ëĐī, uploads íī더ė˜ ëŠĻ든 ë‚īėšĐė„ ëģīė—ŽėĢžë„록 한ë‹Ī.
ėīė œ edit profile 페ėīė§€ė— avatar ė‚Žė§„ėī ëœĻ는 ęēƒė„ 확ėļ할 ėˆ˜ ėžˆë‹Ī.

(4) 파ėž ė €ėžĨ ėœ„ėđ˜ëĨž ëģ€ęē―í•īė•ž í•Ļ

ė§€ęļˆė€ 파ėžė„ ė„œëē„ė˜ uploads íī더ė— ė €ėžĨ하ęģ  ėžˆë‹Ī.
ę·ļ럮나, 파ėžė„ ė„œëē„ė— ė €ėžĨí•īė„œëŠ” ė•ˆëœë‹Ī.
ėīė „ ė„œëē„ëĨž ėĒ…ëĢŒí•˜ęģ  ėƒˆëĄœėšī ė„œëē„ëĨž 만ë“Ī거나 ė„œëē„ę°€ ėĢ―ė–īëē„ëĶŽëĐī, ė„œëē„ė— ė €ėžĨ되ė–ī ėžˆë˜ 파ėžë“Īė€ ė „ëķ€ ėžƒęēŒ 되ęļ° 때ëŽļėīë‹Ī.

따띾ė„œ, 파ėžė„ ė•ˆė „하ęēŒ ė €ėžĨ하ęļ° ėœ„í•īė„œëŠ” 파ėžė„ ė„œëē„ę°€ ė•„ë‹Œ ë‹ĪëĨļ ęģģė— ė €ėžĨí•īė•ž 한ë‹Ī.
ėī는 ė‹Īė œ ė„œëē„ė— ė•ąė„ 배폮할 때 ė§„í–‰í•īëģž ęēƒėīë‹Ī.

한íŽļ, user가 ė—…ëĄœë“œëĨž í†ĩí•ī avatar ė‚Žė§„ė„ 바ęŋ€ 때마ë‹Ī ė—…ëĄœë“œ 한 ëŠĻ든 파ėžë“Īėī uploads íī더ė— ė €ėžĨ되ęģ  ėžˆë‹Ī.
ėī 또한 ë’Īė—ė„œ í•īęē°í•īëģž ęēƒėīë‹Ī.

ðŸ’Ą ė‹Īė œ 파ėžė€ Amazonė˜ 하드 드띾ėīëļŒ 등ė— ė €ėžĨ하ęģ , 데ėī터ëē ėīėŠĪė—ëŠ” 파ėžė˜ urlė„ ė €ėžĨí•īė•ž 한ë‹Ī!


3. git

1) git restore --staged

git addëĨž ėž˜ëŠŧ í•īė„œ ë‹Īė‹œ unstaged area로 ë‚īëĶŽęļ° ėœ„í•ī git restore --staged [파ėž ėīëĶ„]ė„ ėž…ë Ĩ했는데 ė•„래ė™€ 같ė€ ė—ëŸŽę°€ ë–īë‹Ī.

error: pathspec '파ėž ėīëĶ„' did not match any file(s) known to git

2) ė—ëŸŽ í•īęē°

터ëŊļ널ė—ė„œ í•īë‹đ 파ėžėī ėžˆëŠ” íī더로 가ė„œ git fetchëĨž ėž…ë Ĩ한ë‹Ī.
(ėĢžė˜! 프로ė íŠļ íī더가 ė•„니띞 'í•īë‹đ 파ėžėī ėžˆëŠ” íī더'ėīë‹Ī.)


âœĻ ë‚īėž 할 ęēƒ

  1. 강ė˜ ęģ„ė† ë“Ģęļ°
profile
ëŠĨ동ė ėœžëĄœ ė‚īėž, 행ëģĩ하ęēŒðŸ˜

0개ė˜ 댓ęļ€