로그인 로직은 아직 껄끄럽다.
스르륵 읽히는게 아니라 꾸역꾸역 눈에 집어넣어야 들어오는 느낌.
많이 짜보고 읽고 해서 백지상태에서도 흐름을 휙휙 만들어낼 수 있게 해야지.
로그인의 기본 로직은 이렇다.
이제 위의 로직이 실제 코드로는 어떻게 구현되는지 살펴보자.
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.post("/api/users/login", (req, res) => {
// 1. 요청받은 이메일을 DB에서 탐색
User.findOne({ email: req.body.email }, (err, user) => {
if (!user) {
return res.json({
loginSuccess: false,
message: "not exist email",
});
}
// 2. 이메일이 DB에 있다면, 비밀번호가 맞는 비밀번호인지 확인
// comparePassword 와 generateToken 메소드는 다음 목차 확인
user.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({
loginSuccess: false,
message: "not matched password",
});
// 3. 비밀번호까지 맞다면, 토큰을 생성
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
// 토큰을 저장한다. 어디에? 쿠키에...
res
.cookie("x_auth", user.token)
.status(200)
.json({ loginSuccess: true, userId: user._id });
});
});
});
});
위 로직에서 쓰였던 comparePassword 메소드와 generateToken 메소드는 이렇게 선언되어 있다.
// 나중에 comparePassword(plainPassword, cb) { ... } 이렇게 갖다쓰면
// cb의 두 번째 인자에 true or false 값이 담겨서 { ... } 안에서 쓸 수 있음
userSchema.methods.comparePassword = function (plainPassword, cb) {
// painPassword 를 암호화한 다음 db의 password와 비교
// bcrypt.compare(기본 패스워드, 해싱된 패스워드, function(err, inMatch) { ... }
bcrypt.compare(plainPassword, this.password, function (err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
// --------------------------------------------------------------------------
// generateToken(err, user) { ... } 이렇게 갖다쓰면
// user 에 토큰 딱 담겨서 { ... } 안에서 쓸 수 있게 됨
userSchema.methods.generateToken = function (cb) {
var user = this;
// jwt.sign(payload, secret_key) 에서 payload는 string 타입이어야 하는데,
// mongoDB에서 자동생성되는 _id 는 string 타입이 아니므로 toHexString() 사용
var token = jwt.sign(user._id.toHexString(), "secretToken");
user.token = token;
// 생성된 token을 해당 유저의 token칼럼에 담고 db에 save
user.save(function (err, user) {
if (err) return db(err);
cb(null, user);
});
};