Node.js 랜덤채팅 프로젝트(4) - 마이페이지 구현

2un_seong·2021년 10월 25일
1
post-thumbnail

Step0

지난 포스팅에서 JWT를 이용한 로그인을 구현해보았다.
사실 제대로 JWT를 구현하려면 Refresh Token까지 구현을 해야하지만 귀찮으니 패스.. 다음에 해보겠다..

이번에는 Authorization과 회원정보를 수정할 수 있는 mypage를 구현해보겠다.

mypage는 유저 정보를 읽는 [get], 정보를 수정하는 [put], 유저를 삭제하는 [delete]로 구성했다.


Step1 - Authorization

우선 Authorization을 구현해보자.

middleware/validate.js 생성

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;
  • validateRegister - /api/auth/signup에서 들어온 값이 유효한 지 간단하게 확인하는 함수
  • isLoggedin - 현재 client에서 유효한 토큰을 가지고 있는지 검사하는 함수

위의 함수들을 기존의 코드에 추가해보자.

routes/auth.js 수정

∙∙∙
const validate = require('../middleware/validate');
∙∙∙
router.post('/signup', validate.validateRegister, async (req, res) => {
∙∙∙

이제 /signup [post]로 요청이 들어올 때 마다 validateRegister을 먼저 거친 뒤 함수가 실행되게 된다.


Step2 - mypage 구현

mypage를 위한 라우터를 따로 만들어주자.

app.js 수정

∙∙∙
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를 설정할 수 있게 했다.

routes/mypage.js

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에 유효한 토큰이 있는지 확인 후 함수 실행.

  • /api/mypage[get] - User의 email, nickname, introduce을 return
  • /api/mypage[put] - req로 들어온 nickname, introduce를 User모델에 update
  • /api/mypage[delete] - req로 들어온 password를 비교 후 User모델을 삭제하고 cookie 제거 (이후 redirect는 client에서 진행하는 방식)

추가로 지난 포스팅에서 logout을 구현하지 않아서 간단하게 설정해 주었다.

routes/auth.js 수정

∙∙∙
router.get("/logout", validate.isLoggedin, (req, res) => {
    return res.status(200).clearCookie('user').json({result: true, msg: 'Logout Successful!'});
})

간단하게 쿠키만 제거해주면 부분이라 get요청으로 받았다.
로그아웃 이후 redirect는 client에서 해주면 된다.


Step3 - 테스트

테스트 전에 손쉬운 디버깅을 위해 nodemon을 다운받자.

npm i nodemon -g

package.json 수정

∙∙∙
"main": "app.js",
  "scripts": {
	"start": "nodemon app.js",
  },
  ∙∙∙

이제 npm start를 통해 서버를 실행시킬 수 있다.

지금까진 테스트용으로 postman을 사용했었는데 어떤 고수분이 댓글로 Thunder Client를 추천해주셔서 사용해보았다.

상당히 가볍다. 그리고 편하다.
앞으로는 계속 tc를 사용할 듯하다.


  • /api/auth/logout[get]
    잘 작동하는 것을 볼 수 있다.


  • mypage[get]
    22


  • mypage[put] 이후 [get]으로 확인
    33


  • mypage[delete] 후 login

    로그인 시 User를 찾을 수 없다는 메세지를 볼 수 있다.


  • Step1에서 구현했던 validate 또한 잘 작동한다.



이렇게 mypage 기능을 만들어 보았다.

-- 여담 --

계속해서 공부를 하다보면 내가 이전에 포스팅했던 코드들중 어설프고 잘못된 점들을 깨닫고는 수정하곤 한다. 아마 지금 쓴 포스팅도 나중에 봤을 땐 어디 보여주기 민망한 코드일 것이다.

하지만 이 코드들은 지금의 나로써 작성할 수 있는 가장 클린한 코드이고 이러한 시행착오를 거치지 않고서는 깔끔하고 군더더기 없는 코드를 짤 수 없기에 부끄러움을 무릅쓰고 현재의 내 실력을 기록한다.

profile
목적을 위해 세운 목표를 향해

1개의 댓글

comment-user-thumbnail
2022년 1월 12일

멋있어요~~

답글 달기