[TIL] 211204

dev·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
dev log

0ę°œė˜ 댓ęļ€