지난 포스팅에서 JWT를 이용한 로그인을 구현해보았다.
사실 제대로 JWT를 구현하려면 Refresh Token까지 구현을 해야하지만 귀찮으니 패스.. 다음에 해보겠다..
이번에는 Authorization과 회원정보를 수정할 수 있는 mypage를 구현해보겠다.
mypage는 유저 정보를 읽는 [get], 정보를 수정하는 [put], 유저를 삭제하는 [delete]로 구성했다.
우선 Authorization을 구현해보자.
const jwt = require('jsonwebtoken');
var validate = {};
validate.validateRegister = function (req, res, next){
if (!(req.body.email && req.body.gender && (req.body.gender === 'F' || req.body.gender === 'M') &&
req.body.nickname && req.body.password && req.body.password.length >= 8)) {
return res.status(400).send({
msg: 'Something wrong!'
});
}
next();
}
validate.isLoggedin = function(req,res,next){
var token = req.cookies.user;
if (!token) { // 토큰이 없을 때
return res.status(403).json({
msg: "No token provided!"
});
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) { // 토큰이 유효하지 않을 때
return res.status(401).json({
msg: "Token is invalid!"
});
}
req.decoded = decoded;
// 토큰이 유효하다면 토큰에 저장된 값을 req.decoded에 저장해 사용할 수 있게 한다.
next();
});
};
module.exports = validate;
위의 함수들을 기존의 코드에 추가해보자.
∙∙∙
const validate = require('../middleware/validate');
∙∙∙
router.post('/signup', validate.validateRegister, async (req, res) => {
∙∙∙
이제 /signup [post]로 요청이 들어올 때 마다 validateRegister을 먼저 거친 뒤 함수가 실행되게 된다.
mypage를 위한 라우터를 따로 만들어주자.
∙∙∙
const authRouter = require('./routes/auth');
const mypageRouter = require('./routes/mypage');
∙∙∙
// API
app.use('/api/auth', authRouter);
app.use('/api/mypage', mypageRouter);
mypage에서 get을 할 경우 email, nickname, introduce를 설정할 수 있게 했다.
const express = require('express');
const router = express.Router();
// 인증
const bcrypt = require('bcryptjs');
const validate = require('../middleware/validate');
// DB
const pool = require('../middleware/pool');
router.get('/', validate.isLoggedin, async (req, res) => {
try {
const User = await pool.query("SELECT * FROM Users WHERE email = ?", req.decoded.email);
return res.status(200).json({
email: User[0][0].email,
nickname: User[0][0].nickname,
introduce: User[0][0].introduce
});
} catch (e) {
throw e;
}
});
router.put('/', validate.isLoggedin, async (req, res) => {
let con1 = await pool.getConnection(async (conn) => conn);
try {
con1.beginTransaction();
await con1.query('UPDATE Users SET nickname = ?, introduce = ? WHERE email = ?',
[req.body.nickname, req.body.introduce, req.decoded.email]);
con1.commit();
res.status(200).json({ result: true, msg: "User update successful!"});
} catch (e) {
con1.rollback();
throw e;
} finally {
con1.release();
}
})
router.delete('/', validate.isLoggedin, async (req, res) => {
let con1 = await pool.getConnection(async conn => conn)
try {
con1.beginTransaction()
const User = await con1.query('SELECT * FROM Users WHERE email = ?', [req.decoded.email]);
bcrypt.compare(req.body.password, User[0][0].password, async (err, result) => {
if (!result){
res.status(400).json({result: false, msg: "Password is incorrect!"});
} else {
await con1.query('DELETE FROM Users WHERE email = ?', [req.decoded.email]);
con1.commit()
res.status(200).clearCookie('user').json({ msg: 'User delete complete! '});
}
})
} catch (e) {
con1.rollback()
throw e;
} finally {
con1.release()
}
})
module.exports = router;
각 라우터마다 validate.isLoggedin를 사용해 client에 유효한 토큰이 있는지 확인 후 함수 실행.
추가로 지난 포스팅에서 logout을 구현하지 않아서 간단하게 설정해 주었다.
∙∙∙
router.get("/logout", validate.isLoggedin, (req, res) => {
return res.status(200).clearCookie('user').json({result: true, msg: 'Logout Successful!'});
})
간단하게 쿠키만 제거해주면 부분이라 get요청으로 받았다.
로그아웃 이후 redirect는 client에서 해주면 된다.
테스트 전에 손쉬운 디버깅을 위해 nodemon을 다운받자.
npm i nodemon -g
∙∙∙
"main": "app.js",
"scripts": {
"start": "nodemon app.js",
},
∙∙∙
이제 npm start를 통해 서버를 실행시킬 수 있다.
지금까진 테스트용으로 postman을 사용했었는데 어떤 고수분이 댓글로 Thunder Client를 추천해주셔서 사용해보았다.
상당히 가볍다. 그리고 편하다.
앞으로는 계속 tc를 사용할 듯하다.
이렇게 mypage 기능을 만들어 보았다.
-- 여담 --
계속해서 공부를 하다보면 내가 이전에 포스팅했던 코드들중 어설프고 잘못된 점들을 깨닫고는 수정하곤 한다. 아마 지금 쓴 포스팅도 나중에 봤을 땐 어디 보여주기 민망한 코드일 것이다.
하지만 이 코드들은 지금의 나로써 작성할 수 있는 가장 클린한 코드이고 이러한 시행착오를 거치지 않고서는 깔끔하고 군더더기 없는 코드를 짤 수 없기에 부끄러움을 무릅쓰고 현재의 내 실력을 기록한다.
멋있어요~~