데브코스 8주차 day1 - http-status-code 모듈, 폴더구조, 암호화, user 컨트롤러 구현
구현한 부분 (with 암호화)
- 회원가입
- 로그인
- 비밀번호 수정 요청(id확인)
- 비밀번호 수정
- 회원가입 / 로그인 / 비밀번호 수정 요청(id확인) / 비밀번호 수정
node.js 패키지 구조
- app.js
메인 라우터 역할
const express = require("express");
const app = express();
const dotenv = require("dotenv");
dotenv.config();
app.listen(process.env.PORT);
const userRouter = require("./routes/users");
const booksRouter = require("./routes/books");
const likesRouter = require("./routes/likes");
const cartsRouter = require("./routes/carts");
const ordersRouter = require("./routes/orders");
app.use("/users", userRouter);
app.use("/books", booksRouter);
app.use("/likes", likesRouter);
app.use("/carts", cartsRouter);
app.use("/orders", ordersRouter);
- routes
/users.js : user에 관련된 하위 라우터 역할
/books.js : book과 관련된 하위 라우터 역할
...
//users.js
const express = require("express");
const router = express.Router();
const conn = require("../mariadb");
const {
join,
login,
passwordReset,
PasswordResetrequest,
} = require("../controller/userController");
router.use(express.json());
//회원가입
router.post("/join", join);
//로그인
router.post("/login", login);
//비밀번호 초기화 요청
router.post("/reset", PasswordResetrequest);
//비밀번호 초기화
router.put("/reset", passwordReset);
module.exports = router;
routes안 파일에서는 경로만을 지정해주고 콜백함수는 controller에 저장되어있는 모듈을 불러와서 사용한다.
- controller
- 프로젝트에서 매니저 역할을 하는 파일
- 사용자의 요청(req)가 길(url)을 찾아오면 콜백함수(=controller)가 받아줌.
- 즉, 해당 url이 들어왔을 때 실행될 내용을 저장하고 있다.
- module.exports로 내보내고 꺼내와 사용.
-
더보기
const conn = require("../mariadb"); const { StatusCodes } = require("http-status-codes"); const crypto = require("crypto"); //내장 암호화 모듈 //jwt 모듈 const jwt = require("jsonwebtoken"); const join = (req, res) => { const { email, name, password } = req.body; //비밀번호 암호화 const salt = crypto.randomBytes(64).toString("base64"); const hashPassword = crypto .pbkdf2Sync(password, salt, 10000, 10, "sha512") .toString("base64"); let sql = "INSERT INTO users (email,name,password,salt) VALUES (?,?,?,?)"; let values = [email, name, hashPassword, salt]; //암호화된 비밀번호와 salt값을 같이 db에 저장 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 = ?"; let values = [email]; conn.query(sql, values, (err, results) => { if (err) { console.log(err); return res.status(StatusCodes.BAD_REQUEST).end(); } const loginUser = results[0]; //암호화 const salt = loginUser.salt; const hashPassword = crypto .pbkdf2Sync(password, salt, 10000, 10, "sha512") .toString("base64"); //해시 password 비교 if (loginUser && loginUser.password == hashPassword) { //토큰 발급 const token = jwt.sign( { email: loginUser.email, name: loginUser.name, }, process.env.PRIVATE_KEY, { expiresIn: "15m", issuer: "sungeun", } ); console.log(token); res.cookie("token", token, { httpOnly: true, }); return res .status(StatusCodes.OK) .json({ message: `${loginUser.name}님 환영합니다.` }); } else { return res.status(StatusCodes.UNAUTHORIZED).json({ message: "id 및 pw를 확인해주세요.", }); } }); }; 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 }); } 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 =?"; //새로운 salt와 비밀번호 const salt = crypto.randomBytes(64).toString("base64"); const hashPassword = crypto .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) { return res.status(StatusCodes.OK).end(); } else { return res.status(StatusCodes.BAD_REQUEST).json(results); } }); }; module.exports = { join, login, PasswordResetrequest, passwordReset };
암호화 구현 - 비밀번호 암호화
비밀번호 암호화는 단방향 암호화(hash)를 사용하는 내부모듈 crypto를 사용했으며 내용은 다음 포스트에 작성하였다.
https://bsu0404.tistory.com/98
암호화 (+crypto)
bsu0404.tistory.com
http-status-codes
https://www.npmjs.com/package/http-status-codes
http-status-codes
Constants enumerating the HTTP status codes. Based on the Java Apache HttpStatus API.. Latest version: 2.3.0, last published: 4 months ago. Start using http-status-codes in your project by running `npm i http-status-codes`. There are 2430 other projects in
www.npmjs.com
사용법:
const { StatusCodes } = require("http-status-codes");
예시 잘못된 요청)
return res.status(StatusCodes.BAD_REQUEST).end();
예시 성공)
return res.status(StatusCodes.OK).end();
예시 찾을 수 없는 경우)
return res.status(StatusCodes.NOT_FOUND).end();
앞으로 주의할 점
return값, 즉 res가 무한 로딩으로 돌아오지 않을 경우
1. router.use(express.json());에서 json뒤에 () 확인
2. res.json()을 쓰지 않은 경우 end()라도 사용했는지 확인