npm i bcrypt
단방향의 특징을 가진 해시 함수를 이용하여, 입력받은 값을 해시를 통해 암호화를 하여 저장된 값과 비교해서 검증를 도와주는 라이브러리이다.
const bcrypt = require("bcrypt") or import bcrypt from "bcrypt"
// 비밀번호 암호화
// bcrypt( 해시하려는 값, 해시를 반복하는 횟수, 입력값을 해시한 후 실행할 함수);
const hashedPW = bcrypt.hash(pw, 10, (err, hash) => {
try{
//해시화된 비밀번호(hash)를 DB에 저장하거나 다른 처리
}
catch(err){
// 오류처리
}
// 비밀번호 확인
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch){
// 오류처리
}
JWT은 JSON 객체에 인증에 필요한 정보들을 담고 비밀키로 만든 토큰으로, 인터넷 표준 인증 방식이며, 인증과 권한허가 방식들로 사용이 된다.

JWT토큰을 구현하기 위해 jsonwebtoken라이브러리를 사용했다.
npm i jsonwebtoken
// jwt.sign( 페이로드, 비밀키, [옵션, 콜백])
// 1시간 동안 유효하고, 사용자 이름을 담고 있는 토큰
const token = jwt.sign({name: "Kim"}, "secret Key", { expiresln: "1h"});
//jwt.verify(토큰, 비밀키, [옵션])
jwt.verify(token, "secret_key", function (err, decoded) {
console.log(decoded);
});
// {
// userId: '12345',
// email: 'kagrin97@gmail.com',
// iat: 1674784695, 시작 시간 (issued at)
// exp: 1674788295 만료 시간 (Expiration)
// }
cookie-parse 모듈은 요청된 쿠키를 쉽게 사용할 수 있도록 도와주는 모듈 이다. express(req, res) 객체에 cookie를 사용할 수 있도록 기능을 부여한다.
app.use(cookieParser());
console.log(req.cookies);
cookieParser()를 통해 해석된 쿠기는req.cookies를 통해 읽을 수 있다.
// res.cookie(키, 값, 옵션)
res.cookie("name","nile", {
httpOnly: true,
})
쿠기 옵션
res.clearCookie(키, 값, 옵션)
loginRouter 설정const express = require("express");
const dbConnect = require("./config/dbConnect");
const bodyParser = require("body-parser");
const methodoverride = require("method-override");
const app = express();
// ejs view engine 세팅
app.set("view engine", "ejs");
app.set("views", "./views");
app.use(express.static("./public"));
app.use(methodoverride("_method"));
dbConnect();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use("/", require("./routes/loginRouter"));
app.listen(3000, () => {
console.log("서버 실행 중");
});
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
module.exports = mongoose.model("User", UserSchema);
<%- include("./include/_header")%>
<main id="site-main">
<div class="home-container">
<h3>로그인</h3>
<p>로그인이 필요한 서비스입니다.</p>
<form action="/" class="login" method="POST">
<label for="username"><b>Username</b></label>
<input
type="text"
placeholder="사용자 아이디"
name="username"
id="username"
/>
<label for="password"><b>Password</b></label>
<input
type="password"
placeholder="비밀번호"
name="password"
id="password"
/>
<button type="submit">로그인</button>
</form>
</div>
</main>
<%- include("./include/_footer")%>
<%- include("./include/_header")%>
<!-- Main -->
<main id="site-main">
<h3>사용자 등록</h3>
<form action="/register" class="register" method="POST">
<label for="username"><b>아이디</b></label>
<input type="text" placeholder="아이디" name="username" id="username" />
<label for="password"><b>비밀번호</b></label>
<input
type="password"
placeholder="비밀번호"
name="password"
id="password"
/>
<label for="password2"><b>비밀번호 확인</b></label>
<input
type="password"
placeholder="비밀번호 확인"
name="password2"
id="password2"
/>
<input type="submit" value="등록" class="register-btn" />
</form>
</main>
<!-- /Main -->
<%- include("./include/_footer")%>
const express = require("express");
const router = express.Router();
const {
getLogin,
loginUser,
getRegister,
registerUser,
} = require("../controller/loginController");
router.route("/").get(getLogin).post(loginUser);
router.route("/register").get(getRegister).post(registerUser);
module.exports = router;
const asyncHandler = require("express-async-handler");
const User = require("../models/userModel"); // 유저 정보 스키마
const bcrypt = require("bcrypt"); // 비밀번호 암호화 해시
require("dotenv").config(); // env 환경 변수 읽어오기
const jwtSecret = `${process.env.JWT_SECRET}`; // jwt Secret code
const jwt = require("jsonwebtoken"); // jwt 모듈 설정
// GET login page
const getLogin = (req, res) => {
res.render("home");
};
// POST Login user
const loginUser = asyncHandler(async (req, res) => {
const { username, password } = req.body;
// 요청의 username과 일치하는 username을 DB에서 찾아 가져온다.
const user = await User.findOne({ username });
// 일치하는 유저가 없을 경우 오류 처리
if (!user) {
return res.json({ message: "일치하는 사용자가 없습니다." });
}
// 회원 가입시 설정한 비밀번호의 해시값과 입력한 해시 값 대조
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.json({ message: "비밀번호가 맞지 않습니다." });
}
// 맞을 경우 유저의 토큰을 쿠키에 담아 유저에 보낸 후, contacts화면으로 리다이렉트
const token = jwt.sign({ id: user._id }, jwtSecret);
res.cookie("token", token, { httpOnly: true });
res.redirect("/contacts");
});
// 회원 가입
// Resiter Page Get
const getRegister = (req, res) => {
res.render("register");
};
// Resiter user POST /register
const registerUser = asyncHandler(async (req, res) => {
const { username, password, password2 } = req.body;
// 입력한 비밀번호 확인이 맞을 경우
if (password === password2) {
// 입력 받은 비밀 번호를 10번 해싱
const hashedPassword = await bcrypt.hash(password, 10);
// username과 해시한 비밀번호를 DB에 저장
const user = await User.create({ username, password: hashedPassword });
// 회원 가입 완료
res.json({ message: "Resiter successful", user });
}
// 입력한 비밀번호 확인이 틀릴 경우 오류 처리
else {
res.send("fail");
}
});
module.exports = { getLogin, loginUser, getRegister, registerUser };
- 실제로 일단 만들어 보자는 마음으로 이 수업을 듣고 있지만, JWT, bcrypt 등 구글링을 해보면 아직 많은 내용들이 있어, 차차 하나씩 더 찾아보아야겠다.
- 아직까지 이유를 찾지 못한 jwt Secret code를
템플릿 리터럴을 이용하지 않으면 아래와 같은 오류가 생기는 점을 발견하였다.
오류 : secretOrPrivateKey