
오늘은 저번 시간에 이어서 회원 관련해
회원가입, 로그인, 비밀번호 초기화 요청, 비밀번호 초기화 기능을 구현했다.
앞서 코드가 너무 길어지는 것을 방지해
프로젝트 구조에 대해 알아보았다.
라우터는 사용자의 요청(req)이 들어오면 적절한 경로를 찾아주는 역할을 한다.
지금은 간략하게 작성했지만 아래와 같은 구조를 가진다.
app.js // 메인 라우터 역할
/routes
├── users.js // 하위 라우터
├── books.js // 하위 라우터
...
라우터에서 로직을 모두 처리하면 코드가 복잡해지고, 유지보수가 어려워진다.
따라서 컨트롤러(Controller) 를 활용하여 역할을 분리할 필요가 있다.
컨트롤러 는 프로젝트의 매니저 역할을 한다.
직접 일을 처리하지 않고, 적절한 담당자(서비스 혹은 모델)에게 일을 시킨다.
처리된 결과를 받아서 사용자에게 응답(res)으로 전달한다.
즉, 라우터는 URL을 찾아오고, 컨트롤러는 요청을 처리하는 방식이다.
이렇게 역할을 나누면 코드가 간결해지고, 유지보수와 디버깅이 훨씬 쉬워진다.
✅ 정리하면 라우터는 길을 찾고, 컨트롤러는 일을 분배한다.
그래서 UserController.js 를 추가했다.
const join = (req, res) => {
let {email, password} = req.body;
let sql = `INSERT INTO users (email, password, salt) VALUES (?, ?, ?)`
// 비밀번호 암호화
const salt = ctypto.randomBytes(64).toString('base64');
const hashPassword = ctypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('base64');
let values = [email, hashPassword, salt];
conn.query(sql, values, (err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
};
return res.status(StatusCodes.CREATED).json(results);
});
};
const login = (req, res) => {
const {email , password} = req.body;
let sql = `SELECT * FROM users WHERE email =?`;
conn.query(sql, email,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
};
const loginUser = results[0];
const hashPassword =ctypto.pbkdf2Sync(password, loginUser.salt, 10000, 64, 'sha512').toString('base64');
if(loginUser && hashPassword == loginUser.password){
// token 만들기
const token = jwt.sign({
email : loginUser.email,
}, process.env.PRIVATE_KEY, {
expiresIn : '1h',
issuer : "Donggeon"
});
res.cookie('token', token, {
httpOnly : true
});
console.log(token);
return res.status(StatusCodes.OK).json(results);
}else{
return res.status(StatusCodes.UNAUTHORIZED).end();
}
}
);
};
const passwordResetRequest = (req, res) => {
const {email} = req.body;
let sql = `SELECT * FROM users WHERE email =?`
conn.query(sql, email,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
};
const user = results[0];
if(user){
return res.status(StatusCodes.OK).json({
email : email,
salt : user.salt
}); // 비밀번호 초기화 허락
}else{
return res.status(StatusCodes.UNAUTHORIZED).end();
}
}
);
};
const passwordReset = (req , res) => {
const { email, password } = req.body;
let sql = `UPDATE users SET password = ?, salt = ? WHERE email = ?`;
const salt = ctypto.randomBytes(64).toString('base64');
const hashPassword = ctypto.pbkdf2Sync(password, salt, 10000, 10, 'sha512').toString('base64');
let values = [hashPassword, salt , email];
conn.query(sql, values,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
};
if (results.affectedRows === 0) {
return res.status(StatusCodes.BAD_REQUEST).end();
} else {
return res.status(StatusCodes.OK).json(results);
}
}
);
};
강사님과 함께 코드를 짜보면서
'어 DB에 비밀번호가 그대로 저장되는데 이러면 안될 것 같은데?'
라고 생각을 하자마자 바로 강사님이 ctypto 를 사용하셔서 비밀번호를 암호화 하셨다.
간략하게 crypto는 Node.js에서 제공하는 내장 모듈로, 암호화 관련 기능을 수행할 수 있다.
강사님이 멈추고 직접 코드를 작성하라고 한 부분에서
코드님과 비슷하게 작성해서 지금까지 공부한 보람이 있어 뿌듯했다.
이제 다른 API들도 작성을 할텐데 두렵지 않고
먼저 혼자서 작성해봐야겠다.