[TIL] 211203

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

TIL

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

πŸ“ 였늘 ν•œ 것

  1. github 둜그인 - 둜그인 κ·œμΉ™ μ„€μ •

  2. λ‘œκ·Έμ•„μ›ƒ

  3. join / login / github login μ²˜μŒλΆ€ν„° λ‹€μ‹œ κ΅¬ν˜„ν•΄λ³΄κΈ°


πŸ“š 배운 것

user authentication

1. github 둜그인 κ΅¬ν˜„ν•˜κΈ°

1) 둜그인 κ·œμΉ™ μ„€μ •

userκ°€ β‘  passwordλ₯Ό κ°€μ§€κ±°λ‚˜ β‘‘ github의 email이 primary & verified 된 것이라면 λ‘œκ·ΈμΈν•  수 μžˆλ„λ‘ ν•˜λ €κ³  ν•œλ‹€.

①은 μ•žμ—μ„œ κ΅¬ν˜„μ„ μ™„λ£Œν–ˆκ³ , β‘‘λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€.

(1) github email이 DB에 μžˆλ‹€λ©΄ login μ‹œν‚¨λ‹€

ν˜„μž¬ githubλ‘œλΆ€ν„° 받은 user의 emailλ“€ κ°€μš΄λ° primary & verified 된 email을 찾아놓은 μƒνƒœμ΄λ‹€.
이제 이 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μžˆλ‹€λ©΄, κ·Έ userκ°€ 전에 github둜 λ‘œκ·ΈμΈν–ˆλ“  password둜 계정을 μƒμ„±ν–ˆλ“  상관없이 λ‘œκ·ΈμΈμ‹œμΌœμ€„ 것이닀.

export const finishGithubLogin = (req, res) = {
  // finalUrl을 λ§Œλ“¦
  // finalUrlλ‘œλΆ€ν„° 데이터λ₯Ό κ°€μ Έμ˜΄ (finalUrl에 POST requestλ₯Ό 보냄)
  // κ·Έ 데이터λ₯Ό json ν˜•μ‹μœΌλ‘œ λ°”κΏˆ
  
  // access token을 μ΄μš©ν•΄ github API에 μ ‘κ·Όν•΄ user에 λŒ€ν•œ 정보λ₯Ό κ°€μ Έμ˜΄
  if ("access_token" in tokenRequest) {
    const { access_token } = tokenRequest;
    const apiUrl = "https://api.github.com";
    // public 데이터
    const userData = await (
      await fetch(`${apiUrl}/user`, {
        headers: {
          Authorization: `token ${access_token}`,
        },
      })
    ).json();
    // private 데이터 (δΈ­ email)
    const emailData = await (
      await fetch(`${apiUrl}/user/emails`, {
        headers: {
          Authorization: `token ${access_token}`,
        },
      })
    ).json();
    // primary & verified email κ°€μ Έμ˜€κΈ°
    const emailObj = emailData.find(email => email.primary === true && email.verified === true);
    if (!emailObj) {
      return res.redirect("/login");
    }
    // 찾은 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μžˆλ‹€λ©΄, κ·Έ userλ₯Ό login μ‹œν‚¨λ‹€
    const existingUser = await User.findOne({ email: emailObj.email });
    if (existingUser) {
      req.session.loggedIn = true;
      req.session.user = existingUser;
      return res.redirect("/");
    } else {
      // πŸ”₯ 찾은 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μ—†λ‹€λ©΄, κ·Έ user의 계정을 μƒμ„±ν•œλ‹€ (join)
    }
  } else {
    return res.redirect("/login");
  }
};

이제 (ν˜„μž¬ μ—°κ²°ν•œ github κ³„μ •μ˜ emailκ³Ό 같은 email둜 Join ν•˜μ—¬ κ·Έ email이 DB에 μ €μž₯λ˜μ–΄ μžˆμ–΄μ•Ό 함) login νŽ˜μ΄μ§€μ—μ„œ 'Continue with Github β†’'λ₯Ό ν΄λ¦­ν•˜λ©΄, 둜그인이 λ˜μ–΄ home으둜 redirect λ˜λ©΄μ„œ Log outκ³Ό profile 메뉴가 λœ¬λ‹€.

(2) github email이 DB에 μ—†λ‹€λ©΄ κ·Έ user의 계정을 μƒμ„±ν•œλ‹€ (join)

μœ„ μ½”λ“œμ—μ„œ πŸ”₯ 뢀뢄을 κ΅¬ν˜„ν•˜λ €κ³  ν•œλ‹€.
일단 Users DBμ—μ„œ Join λ˜μ–΄ μžˆλŠ” user 정보λ₯Ό μ‚­μ œν•œλ‹€.

db.users.remove({})

User.js νŒŒμΌμ—μ„œ userSchema에 socialOnly 값을 μΆ”κ°€ν•œλ‹€.
github λ‘œκ·ΈμΈμ„ ν†΅ν•œ join인지 μ•„λ‹Œμ§€ μ•ŒκΈ° μœ„ν•΄ μ‚¬μš©λœλ‹€.

λ˜ν•œ, password에 λΆ€μ—¬ν•΄μ€€ required: trueλ₯Ό μ‚­μ œν•œλ‹€.
github λ‘œκ·ΈμΈμ„ 톡해 join ν•˜λ©΄ passwordλŠ” μ±„μšΈ 수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.
(단, join.pug νŒŒμΌμ—μ„œ password input은 κ·ΈλŒ€λ‘œ requiredμ—¬μ•Ό ν•œλ‹€. userSchema와 비ꡐ할 것.)

// User.js
const userSchema = new mongoose.Schema({
  // μƒλž΅
  password: String,
  socialOnly: { type: Boolean, default: false },
});

finishGithubLogin 컨트둀러λ₯Ό μˆ˜μ •ν•œλ‹€.
githubλ‘œλΆ€ν„° κ°€μ Έμ˜¨ email이 DB에 없을 λ•Œ μ•žμ—μ„œ κ°€μ Έμ˜¨ userData와 eamilObjλ₯Ό μ΄μš©ν•΄ 계정을 생성(Join)ν•˜λ„λ‘ ν•œλ‹€.

export const finishGithubLogin = (req, res) = {
  // finalUrl을 λ§Œλ“¦
  // finalUrlλ‘œλΆ€ν„° 데이터λ₯Ό κ°€μ Έμ˜΄ (finalUrl에 POST requestλ₯Ό 보냄)
  // κ·Έ 데이터λ₯Ό json ν˜•μ‹μœΌλ‘œ λ°”κΏˆ
  
  // access token을 μ΄μš©ν•΄ github API에 μ ‘κ·Όν•΄ user에 λŒ€ν•œ 정보λ₯Ό κ°€μ Έμ˜΄
    // public 데이터
    // private 데이터 (δΈ­ email)
    // primary & verified email κ°€μ Έμ˜€κΈ°

    // 찾은 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μžˆλ‹€λ©΄, κ·Έ userλ₯Ό login μ‹œν‚¨λ‹€
    const existingUser = await User.findOne({ email: emailObj.email });
    if (existingUser) {
      req.session.loggedIn = true;
      req.session.user = existingUser;
      return res.redirect("/");
    } else {
      // πŸ”₯ 찾은 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μ—†λ‹€λ©΄, κ·Έ user의 계정을 μƒμ„±ν•œλ‹€ (join)
      const user = await User.create({
        name: userData.name,
        email: emailObj.email,
        username: userData.login,
        password: "", // githubμ—μ„œ κ°€μ Έμ˜¨ λ°μ΄ν„°λ‘œλΆ€ν„° password 값을 μ±„μšΈ 수 μ—†λ‹€
        socialOnly: true, // λŒ€μ‹  githubλ₯Ό ν†΅ν•œ 계정 μƒμ„±μ΄λž€ 것을 μ•Œλ¦¬κΈ° μœ„ν•΄ socialOnly 값을 true둜 λ°”κΎΌλ‹€
        location: userData.location,
      });
    }
  } else {
    return res.redirect("/login");
  }
};

이제 (ν˜„μž¬ μ—°κ²°ν•œ github κ³„μ •μ˜ emailκ³Ό 같은 email이 DB에 μ €μž₯λ˜μ–΄ μžˆμ§€ μ•Šλ”λΌλ„) login νŽ˜μ΄μ§€μ—μ„œ 'Continue with Github β†’'λ₯Ό ν΄λ¦­ν•˜λ©΄, 둜그인이 λ˜μ–΄ home으둜 redirect λ˜λ©΄μ„œ Log outκ³Ό profile 메뉴가 λœ¬λ‹€.

ν•œνŽΈ, sicialOnly 값이 true인 userλŠ” login form을 톡해 λ‘œκ·ΈμΈν•  수 없도둝 postLogin 컨트둀러λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

export const postLogin = async (req, res) => {
  const { username, password } = req.body;
  const user = await User.findOne({ username, socialOnly: false }); // μˆ˜μ • ❗
// μ€‘λž΅
};

2) μ΅œμ’… finishGithubLogin 컨트둀러 ❗❗❗

  • existingUser λΆ€λΆ„μ—μ„œ μ€‘λ³΅λœ μ½”λ“œλ₯Ό μˆ˜μ •ν–ˆλ‹€. (existingUser β†’ user)

  • userSchema에 avatarUrl을 μΆ”κ°€ν•œ ν›„ finishGithubLogin μ»¨νŠΈλ‘€λŸ¬μ—μ„œ github email을 μ΄μš©ν•΄ 계정(user)을 μƒˆλ‘­κ²Œ μƒμ„±ν•˜λŠ” λΆ€λΆ„μ˜ μ½”λ“œλ₯Ό μˆ˜μ •ν–ˆλ‹€.
    (password둜 계정을 μƒμ„±ν•œ 경우 avatarλ₯Ό 가지지 μ•ŠλŠ”λ‹€. λ‹€λ§Œ, λ‚˜μ€‘μ— ν”„λ‘œν•„μ—μ„œ μΆ”κ°€ κ°€λŠ₯ν•˜λ‹€.)

export const finishGithubLogin = async (req, res) => {
  // 1. finalUrl을 λ§Œλ“¦
  const baseUrl = "https://github.com/login/oauth/access_token";
  const config = {
    client_id: process.env.GH_CLIENT,
    client_secret: process.env.GH_SECRET,
    code: req.query.code,
  };
  const params = new URLSearchParams(config).toString();
  const finalUrl = `${baseUrl}?${params}`;
  // 2. finalUrlλ‘œλΆ€ν„° 데이터λ₯Ό κ°€μ Έμ˜΄ (finalUrl에 POST requestλ₯Ό 보냄) (code β†’ access token)
  // 그리고 κ·Έ 데이터λ₯Ό json ν˜•μ‹μœΌλ‘œ λ°”κΏˆ
  const tokenRequest = await (
    await fetch(finalUrl, {
      method: "POST",
      headers: {
        Accept: "application/json",
      },
    })
  ).json();
  // 3-1. access token을 μ΄μš©ν•΄ github API에 μ ‘κ·Όν•΄ user에 λŒ€ν•œ 정보λ₯Ό κ°€μ Έμ˜΄
  if ("access_token" in tokenRequest) {
    const { access_token } = tokenRequest;
    const apiUrl = "https://api.github.com";
    // 3-1-1. public 데이터
    const userData = await (
      await fetch(`${apiUrl}/user`, {
        headers: {
          Authorization: `token ${access_token}`,
        },
      })
    ).json();
    // 3-1-2. private 데이터 (δΈ­ email)
    const emailData = await (
      await fetch(`${apiUrl}/user/emails`, {
        headers: {
          Authorization: `token ${access_token}`,
        },
      })
    ).json();
    // (1) primary & verified email κ°€μ Έμ˜€κΈ°
    const emailObj = emailData.find(
      (email) => email.primary === true && email.verified === true
    );
    if (!emailObj) {
      return res.redirect("/login");
    }
    // (2) 찾은 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μžˆλ‹€λ©΄, κ·Έ userλ₯Ό login μ‹œν‚¨λ‹€
    // (3) 찾은 emailκ³Ό 같은 email을 가진 userκ°€ DB에 μ—†λ‹€λ©΄, κ·Έ user의 계정을 μƒμ„±ν•œλ‹€ (join)
    let user = await User.findOne({ email: emailObj.email });
    if (!user) {
      user = await User.create({
        avatarUrl: userData.avatar_url,
        name: userData.name,
        email: emailObj.email,
        username: userData.login,
        password: "", // githubμ—μ„œ κ°€μ Έμ˜¨ λ°μ΄ν„°λ‘œλΆ€ν„° passwordλŠ” μ±„μšΈ 수 μ—†λ‹€
        socialOnly: true, // λŒ€μ‹ , socialOnly 값을 true둜 바꿔쀬닀
        location: userData.location,
      });
    }
    req.session.loggedIn = true;
    req.session.user = user;
    return res.redirect("/");
  } else {
    // 3-2. access_token이 tokenRequest μ•ˆμ— μ—†λ‹€λ©΄ userλ₯Ό login νŽ˜μ΄μ§€λ‘œ 보낸닀.
    return res.redirect("/login");
  }
};

❗❗❗ 이제, github emailκ³Ό 같은 email을 가진 userκ°€ DB에 μžˆλ‹€λ©΄, κ·Έ userκ°€ 전에 github둜 λ‘œκ·ΈμΈν–ˆλ“  password둜 계정을 μƒμ„±ν–ˆλ“  상관없이, λ‘œκ·ΈμΈμ‹œμΌœμ€„ 것이닀.

github둜 둜그인
이미 wetubeμ—μ„œ githubλ₯Ό 톡해 계정을 λ§Œλ“  μƒνƒœμ—μ„œ cookieλ₯Ό μ§€μ›Œ λ‘œκ·Έμ•„μ›ƒ ν•œλ‹€.

'Continue with Github β†’'λ₯Ό ν΄λ¦­ν•˜λ©΄ wetube에 둜그인이 λ˜λŠ” 것을 확인할 수 μžˆλ‹€.
μ΄λ•Œ socialOnly 값은 true 이닀.

password둜 계정 생성
μ΄λ²ˆμ—λŠ” cookieλ₯Ό μ§€μ›Œ λ‘œκ·Έμ•„μ›ƒ ν•˜κ³ , Users DBμ—μ„œ githubλ₯Ό 톡해 μƒμ„±ν•œ 계정(user)을 μ‚­μ œν•œ ν›„, Join νŽ˜μ΄μ§€μ—μ„œ password와 ν•¨κ»˜ μ•žμ„œ wetubeλ₯Ό μŠΉμΈν•œ github emailκ³Ό λ™μΌν•œ email을 μž…λ ₯ν•΄ 계정을 μƒμ„±ν•œλ‹€. (join)

(wetubeλ₯Ό μŠΉμΈν•œ κ³„μ •μœΌλ‘œ gtihub μžμ²΄μ— 둜그인이 λ˜μ–΄ μžˆλŠ” μƒνƒœμ—μ„œ) 'Continue with Github β†’'λ₯Ό ν΄λ¦­ν•˜λ©΄ (μ•žμ„œ User DBμ—μ„œλŠ” githubλ₯Ό 톡해 μƒμ„±ν•œ 계정을 μ‚­μ œν–ˆμ„μ§€λΌλ„, join νŽ˜μ΄μ§€μ—μ„œ 계정을 λ§Œλ“€ λ•Œ μž…λ ₯ν•œ email이 ν˜„μž¬ 둜그인 λ˜μ–΄ μžˆλŠ” github emailκ³Ό μΌμΉ˜ν•˜λ―€λ‘œ) wetube에 둜그인이 λ˜λŠ” 것을 확인할 수 μžˆλ‹€. πŸ”₯
∡ const user = await User.findOne({ email: emailObj.email });
μ΄λ•Œ socialOnly 값은 false이닀.

❗❗❗ ν•œνŽΈ, github emailκ³Ό 같은 email을 가진 userκ°€ DB에 μ—†λ‹€λ©΄, μ•žμ—μ„œ κ°€μ Έμ˜¨ userData와 eamilObjλ₯Ό μ΄μš©ν•΄ 계정을 생성(Join)ν•œ ν›„, λ‘œκ·ΈμΈμ‹œμΌœμ€„ 것이닀.

β€» μ†Œμ…œ 둜그인 κ΅¬ν˜„ μ‹œ νŠΈμœ„ν„°, μΉ΄μΉ΄μ˜€ν†‘, μΈμŠ€νƒ€κ·Έλž¨ 등은 client_idκ°€ app_id λ“±μœΌλ‘œ λ°”λ€Œμ–΄ ν‘œν˜„λ  μˆ˜λŠ” μžˆμ§€λ§Œ μ „μ²΄μ μœΌλ‘œ github와 거의 λΉ„μŠ·ν•œ 과정을 κ±°μΉœλ‹€.
λ‹€λ§Œ, κ΅¬κΈ€μ΄λ‚˜ 페이슀뢁 등은 μ’€ 더 μ ˆμ°¨κ°€ λ³΅μž‘ν•  수 μžˆλ‹€.


2. λ‘œκ·Έμ•„μ›ƒ

1) req.session.destroy()

// userController.js
export const logout = (req, res) => {
  req.session.destroy();
  return res.redirect("/");
};

Log out 메뉴λ₯Ό ν΄λ¦­ν•˜λ©΄, λ‘œκ·Έμ•„μ›ƒλœλ‹€.
메뉴가 Log out & Profileμ—μ„œ Join & Login으둜 바뀐닀.


3. join / login / github login 볡슡

β€» νŠΉμ • λ²„μ „μ˜ 컀밋 κ°€μ Έμ˜€κΈ°

$ git clone [λ ˆνŒŒμ§€ν† λ¦¬ url]
$ git reset --hard [μ›ν•˜λŠ” 버전 μ»€λ°‹μ˜ ν•΄μ‹œμ½”λ“œ]

git clone을 μ΄μš©ν•΄ user authentication 파트 μ²˜μŒλΆ€ν„° λ‹€μ‹œ κ΅¬ν˜„ν•΄λ³΄κΈ°
ν—·κ°ˆλ Έκ±°λ‚˜ λ‹€μ‹œ 보고 싢은 λΆ€λΆ„λ“€λ§Œ 정리함

1) νŒ¨ν‚€μ§€ 정리

(1) bcrypt

(join) λΉ„λ°€λ²ˆν˜Έ ν•΄μ‹±
(login) μž…λ ₯ λΉ„λ°€λ²ˆν˜Έμ™€ DB λΉ„λ°€λ²ˆν˜Έλ₯Ό 비ꡐ

(2) express-session

expressμ—μ„œ session을 μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•œλ‹€.

λΈŒλΌμš°μ €κ°€ μ„œλ²„μ— μš”μ²­μ„ 보내면, μ„œλ²„λŠ” λΈŒλΌμš°μ €μ—κ²Œ session idλ₯Ό μ£Όκ³ , session store에 μ„Έμ…˜ id와 ν•¨κ»˜ μ„Έμ…˜ objectλ₯Ό μ €μž₯ν•΄μ€€λ‹€.

λΈŒλΌμš°μ €λŠ” λ‹€μŒλΆ€ν„° μ„œλ²„μ— requestλ₯Ό 보낼 λ•Œ κ·Έ session idλ₯Ό ν•¨κ»˜ 보낸닀.

같은 μ›Ή μ‚¬μ΄νŠΈμ˜ 같은 user라도 μ„œλ‘œ λ‹€λ₯Έ λΈŒλΌμš°μ €μ—λŠ” μ„œλ‘œ λ‹€λ₯Έ session idκ°€ λΆ€μ—¬λ˜λ―€λ‘œ μ„œλ²„λŠ” 이λ₯Ό 톡해 λΈŒλΌμš°μ €(user)λ₯Ό ꡬ뢄할 수 있고, session store에 user에 λŒ€ν•œ 정보λ₯Ό μ—…λ°μ΄νŠΈν•  수 μžˆλ‹€.

(3) connect-mongo

session 데이터λ₯Ό mongoDB에 μ €μž₯ν•˜λ„λ‘ 함

// server.js
import MongoStore from "connect-mongo";

app.use(session({
    secret: process.env.COOKIE_SECRET,
    resave: false,
    saveUninitialized: false,
    store: MongoStore.create({ mongoUrl: process.env.DB_URL }),
  })
);

(4) dotenv

process.envλ₯Ό 톡해 .env νŒŒμΌμ— μ •μ˜ν•΄λ†“μ€ 것듀에 μ ‘κ·Όν•˜κ³  μ‚¬μš©ν•  수 μžˆλ„λ‘ 함

(5) node-fetch

node.jsμ—μ„œ fetch APIλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ μ„€μΉ˜ν•΄μ•Ό ν•œλ‹€.
버전 3λΆ€ν„°λŠ” μ—λŸ¬κ°€ 뜰 수 μžˆλ‹€.
ν•΄κ²° 방법은 μžˆμ§€λ§Œ 곡식 λ¬Έμ„œμ—μ„  CSM(CommonJS)λ₯Ό μ“΄λ‹€λ©΄ 버전 2λ₯Ό ꢌμž₯ν•˜κ³  μžˆλ‹€. (뒀에 정리)

2) email 쀑볡 검사 / email 일치 유무

postJoin μ»¨νŠΈλ‘€λŸ¬μ—μ„œ userκ°€ μž…λ ₯ν•œ email이 DB에 μžˆλŠ” emailκ³Ό μ€‘λ³΅λ˜λ©΄, μ—λŸ¬ λ©”μ‹œμ§€λ₯Ό λ„μš°λ„λ‘ 함

postLogin μ»¨νŠΈλ‘€λŸ¬μ—μ„œ github emailκ³Ό λ™μΌν•œ email이 DB에 있으면, κ·Έ email을 λΉ„λ‘―ν•΄ githubλ‘œλΆ€ν„° κ°€μ Έμ˜¨ user 정보λ₯Ό λ°”νƒ•μœΌλ‘œ 계정을 μƒμ„±ν•˜λ„λ‘ 함

β†’ 두 κ²½μš°κ°€ 잠깐 ν—·κ°ˆλ Έλ‹€. λ°°μΉ˜λ˜μ§€ μ•ŠλŠ”λ‹€!

3) model.find() VS model.exists()

db.users.remove({}) ν•œ 후에 join ν•˜λŠ”λ° 자꾸 'ν•΄λ‹Ή username/email은 이미 μ‚¬μš© μ€‘μž…λ‹ˆλ‹€.'라고 λ‚˜μ™”λ‹€.
μ—λŸ¬κ°€ λ°œμƒν•œ 원인은 User.exists()λ₯Ό User.find()라고 잘λͺ» μ μ—ˆκΈ° λ•Œλ¬Έμ΄μ—ˆλ‹€.

model.find()λŠ” 쑰건을 λ§Œμ‘±ν•˜λŠ” 데이터λ₯Ό 담은 배열을 return ν•œλ‹€.
즉, 쑰건을 λ§Œμ‘±ν•˜λŠ” 데이터가 μ—†λ‹€λ©΄ κ·Έ 값은 [ ](빈 λ°°μ—΄)이 λœλ‹€.
그런데, 빈 배열은 true이닀! ( 빈 λ¬Έμžμ—΄μ€ falseμ΄μ§€λ§Œ, 빈 λ°°μ—΄ & 빈 κ°μ²΄λŠ” true이닀. )

ν•œνŽΈ, model.exists()λŠ” 쑰건을 λ§Œμ‘±ν•˜λŠ” λ°μ΄ν„°μ˜ 유무λ₯Ό Boolean κ°’μœΌλ‘œ return ν•œλ‹€.
즉, 쑰건을 λ§Œμ‘±ν•˜λŠ” 데이터가 μ—†λ‹€λ©΄ κ·Έ 값은 falseκ°€ λœλ‹€.

μ—λŸ¬ μ½”λ“œ

export const postJoin = async (req, res) => {
  const { name, email, username, password, password2, location } = req.body;
  const exists = await User.find({ $or: [{ username }, { email }] });
  console.log(exists); // [] β†’ true
  if (exists) {
    return res.status(400).render("join", {
      pageTitle: "Join",
      errorMessage: "ν•΄λ‹Ή username λ˜λŠ” email은 이미 μ‚¬μš© μ€‘μž…λ‹ˆλ‹€.",
    });
  }
  // μƒλž΅
}

μˆ˜μ • 및 ν•΄κ²°

export const postJoin = async (req, res) => {
  const { name, email, username, password, password2, location } = req.body;
  const exists = await User.exists({ $or: [{ username }, { email }] });
  console.log(exists); // false
  if (exists) {
    return res.status(400).render("join", {
      pageTitle: "Join",
      errorMessage: "ν•΄λ‹Ή username λ˜λŠ” email은 이미 μ‚¬μš© μ€‘μž…λ‹ˆλ‹€.",
    });
  }
  // μƒλž΅
}

4) unique: true

userSchemaλ₯Ό μ •μ˜ν•  λ•Œ usernameκ³Ό email에 'unique: true'λ₯Ό μ μ–΄μ€˜μ•Ό ν•œλ‹€.

5) try ~ catch

model.create() μ‚¬μš© μ‹œ try ~ catch ꡬ문을 μ‚¬μš©ν•΄ μ—λŸ¬κ°€ λ°œμƒν•  λ•Œλ₯Ό λŒ€λΉ„ν•΄μ•Ό ν•œλ‹€.

6) localsMiddleware μ‹€ν–‰ μˆœμ„œ

μ½”λ“œ μƒμœΌλ‘œλŠ” postLogin μ»¨νŠΈλ‘€λŸ¬μ—μ„œ req.session에 값을 μ€€ 후에 middlewares.js 파일 μ•ˆμ˜ localMiddlewareκ°€ μ‹€ν–‰λ˜μ–΄μ•Ό ν•˜κ³ , μ‹€μ œλ‘œλ„ κ·Έλ ‡κ²Œ μ‹€ν–‰λ˜μ–΄ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•Šκ³  μžˆλ‹€.

그런데 μ›λž˜ next()κ°€ μžˆλŠ” middlewareλŠ” controller보닀 전에 μ‹€ν–‰λ˜μ–΄μ•Ό ν•˜λŠ” 걸둜 μ•Œκ³  μžˆλŠ”λ° 이게 μ–΄λ–»κ²Œ 된 건지 λͺ¨λ₯΄κ² λ‹€.

// userController.js
export const postLogin = async (req, res) => {
  const { username, password } = req.body;
  const user = await User.findOne({ username });
  if (!user) {
    return res.status(400).render("login", {
      pageTitle: "Login",
      errorMessage: "ν•΄λ‹Ή username을 가진 계정이 μ—†μŠ΅λ‹ˆλ‹€.",
    });
  }
  const ok = <await bcrypt.compare(password, user.password);
  if (!ok) {
    return res.status(400).render("login", {
      pageTitle: "Login",
      errorMessage: "λΉ„λ°€λ²ˆν˜Έκ°€ ν‹€λ¦½λ‹ˆλ‹€.",
    });
  }
  req.session.loggedIn = true;
  req.session.user = user;
  return res.redirect("/");
};
// middlewares.js
export const localsMiddleware = (req, res, next) => {
  res.locals.loggedIn = req.session.loggedIn;
  res.locals.loggedInUser = req.session.user;
  res.locals.siteName = "Wetube";
  next();
};
// server.js
app.use(localsMiddleware);
app.use("/", rootRouter);

이유λ₯Ό μžμ„Ένžˆ μ„€λͺ…ν•˜μžλ©΄, μ‹€ν–‰ μˆœμ„œλŠ” μ•„λž˜μ™€ κ°™λ‹€.
login λ²„νŠΌμ„ λˆ„λ₯΄λ©΄, expressκ°€ server.js νŒŒμΌμ—μ„œ home routeλ₯Ό μ°Ύμ•„(∡ /login) postLogin μ»¨νŠΈλ‘€λŸ¬κ°€ μ‹€ν–‰λ˜κΈ° 전에, κ·Έ μœ„μ— 적힌 localsMiddlewareκ°€ λ¨Όμ € μ‹€ν–‰λœλ‹€.
λ‹€μŒμœΌλ‘œ postLogin μ»¨νŠΈλ‘€λŸ¬κ°€ μ­‰ μ‹€ν–‰λ˜λ‹€κ°€ λ§ˆμ§€λ§‰ λΆ€λΆ„μ—μ„œ res.session에 값을 μ€€ ν›„ res.redirect("/")에 μ˜ν•΄ expressλŠ” λ‹€μ‹œ home routeλ₯Ό μ°ΎλŠ”λ‹€.(∡ /)
μ΄λ²ˆμ—λ„ home μ»¨νŠΈλ‘€λŸ¬κ°€ μ‹€ν–‰λ˜κΈ° 전에, κ·Έ μœ„μ— 적힌 localsMiddlewareκ°€ λ¨Όμ € μ‹€ν–‰λœλ‹€.

λ”°λΌμ„œ, 결과적으둜 login λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ localsMiddleware β†’ postLogin 컨트둀러 β†’ localsMiddleware β†’ home 컨트둀러 μˆœμ„œλŒ€λ‘œ 싀행이 됨을 μ•Œ 수 μžˆλ‹€.

7) require() of ES Module not supported μ—λŸ¬

node-fetchλ₯Ό import ν•˜μž λ°œμƒν•œ μ—λŸ¬μ΄λ‹€.
node-fetchκ°€ 버전 3λΆ€ν„°λŠ” ESM-only Module이라고 ν•œλ‹€.
곡식 λ¬Έμ„œμ—μ„œλŠ” CSM(CommonJS)λ₯Ό μ“΄λ‹€λ©΄ 버전 2둜 μ“Έ 것을 ꢌμž₯ν•˜κ³  μžˆλ‹€.

node uninstall node-fetch
npm install node-fetch@2.6.1

8) λ³€μˆ˜ μ‚¬μš© 정리

#{λ³€μˆ˜} / =λ³€μˆ˜
pug νŒŒμΌμ—μ„œ μžλ°”μŠ€ν¬λ¦½νŠΈ λ³€μˆ˜λ₯Ό μ“Έ 수 있음

env
.env νŒŒμΌμ— μ •μ˜ν•΄λ†“μ€ 값듀을 process.envλ₯Ό μ΄μš©ν•΄ μ‚¬μš© κ°€λŠ₯

res.locals
pug와 expressκ°€ res.locals 값을 곡유
μ „μ—­ λ³€μˆ˜μ²˜λŸΌ λͺ¨λ“  pug νŒŒμΌμ—μ„œ μ‚¬μš© κ°€λŠ₯


✨ 내일 ν•  것

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

0개의 λŒ“κΈ€