로그인 기능 구현
app.js
const express = require("express");
const mongoose = require("mongoose");
mongoose.set('strictQuery', true);
const jwt = require('jsonwebtoken');
mongoose.connect("mongodb://127.0.0.1:27017/shopping-demo", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
const app = express();
const router = express.Router();
// 회원가입 기능
const User = require('./models/user')
router.post('/users', async(req,res) => {
const {nickname, email, password, confirmPassword} = req.body;
// 1. 패스워드와 패스워드확인 값이 일치하는가
// 2. 이메일과 닉네임 중복인가
// 3. db에 삽입
if (password !== confirmPassword) {
res.status(400).json({errorMessage:"비밀번호 확인이 일치하지 않습니다."});
return; // 위에 패스워드가 일치하지 않으면 밑에 코드를 더 실행하면 안되니까 여기서 리턴해준다
}
const existUser = await User.findOne({
$or: [{email: email},{nickname:nickname}] // $or << 이안에 있는 값중에 하나라도 일치했을 때 보여준다.
}); // 그리고 값중에 하나라도 일치한다는 뜻은 이메일이나 닉네임 중 중복이 있다는 뜻이기에 더 진행해선 안된다.
if (existUser) {
res.status(400).json({errorMessage:"이메일이나 닉네임이 중복됩니다."})
return; // 중복되면 회원가입이 되면 안되기에 여기서 리턴하여 코드를 종료시킨다.
}
// 여기까지 위의 두 if문을 거치지 않았다면 회원가입이 정상적으로 실행되어야 한다.
const user = new User({nickname,email,password});
await user.save();
res.status(201).json({}); // 스테이터스 201은 무언가를 만들었다는 creat 생성 코드! 기억하면 좋음
});
// 로그인 기능
router.post('/auth', async(req,res) => {
const {email,password} = req.body;
const user = await User.findOne({email}); // 특정 사용자를 이메일을 바탕으로 먼저 찾을 것 .
// 1. 로그인이 가능하지 않을 경우를 먼저 구현하는데 우선 사용자가 존재하지 않거나
// 2. 입력받은 패스워드와 사용자의 패스워드가 다를 때 에러 메세지를 발생 시켜야 한다.
if (!user || password !== user.password) {
res.status(400).json({errorMessage:"로그인 정보가 일치하지 않습니다."})
return;
}
// 이제는 위의 if문을 거치지 않는다면 사용자 정보가 일치한다는 뜻이니 로그인이 되어야한다.
const token = jwt.sign({userId:user.userId},'juho-secretkey') // 여기서 user.userId의 userId는 /models/user.js에서 8번줄인 _id값이란 뜻이다.
// 즉, user.userId는 user._id와 같다는 뜻!
res.status(200).json({
"token":token,
})
})
const authMiddleware = require('./middlewares/auth-middlewares');
router.get("/users/me", authMiddleware, async(req,res) => {
res.json({user:res.locals.user}); // 내정보 조회api 기능
})
app.use("/api", express.urlencoded({ extended: false }), router); // urlencoded는 미들웨어, 즉 /api로 들어온것은 urlencoded 미들웨어를 거친다음 라우터로 가라 라는 뜻
app.use(express.static("assets")); // 정적 파일을 서빙해준다.
app.listen(8080, () => {
console.log("서버가 요청을 받을 준비가 됐어요");
});
./middlewares/auth-middlewares.js
// 모델
// jwt
const User = require("../models/user");
const jwt = require("jsonwebtoken");
module.exports = async (req, res, next) => {
const {authorization} = req.headers;
const [authType, authToken] = authorization.split(" "); // Bearer와 실제 값이 띄어쓰기로 구분되어있기에 스플릿을 사용하여 나눠준다.
// authType 에는 Bearer가 들어오고
// authToken 에는 실제 jwt값이 들어옴
console.log([authType,authToken]);
if(authType !== "Bearer" || !authToken){
res.status(400).json({
errorMessage:"로그인 후 사용이 가능한 API입니다아아아아!!"
});
return;
}
try{ // try catch를 쓰는 이유가 시크릿키가 다르면 에러를 내뿜으면서 서버가 아예 닫혀버림
// 복호화 및 검증
const {userId} = jwt.verify(authToken,'juho-secretkey'); // 로그인할 때 쓰는 시크릿키로 검증
console.log(userId);
const user = await User.findById(userId);
res.locals.user = user;
console.log(user);
next(); // 다음 미들웨어로 넘긴다.
}catch(error){
console.log(error);
res.status(400).json({
errorMessage:"로그인 후 사용이 가능한 API입니드아아앙!"
})
return;
}
}
./models/user.js
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
email: String,
nickname: String,
password: String,
});
UserSchema.virtual("userId").get(function () { // 가상으로 읽는 userId 파라미터가 추가된다
return this._id.toHexString();
});
UserSchema.set("toJSON", {
virtuals: true,
});
module.exports = mongoose.model("User", UserSchema);