오늘은 사이드 프로젝트를 진행하기 위해 유데미에서 관련 강의를 들었다. 이번주 그리고 다음주에 볼 부분이 인증과 보안에 관한 부분인데, 확실히 어렵다. 그래서 오늘 공부한 내용을 기록해두고 이어서 공부할 부분들과 잘 연결할 수 있도록 노력해야겠다.
const express = require("express");
const bcrypt = require("bcryptjs");
const db = require("../data/database");
const router = express.Router();
router.post("/signup", async function (req, res) {
const userData = req.body;
const enteredEmail = userData.email;
const enteredConfirmEmail = userData["confirm-email"];
const enteredPassword = userData.password;
if (
!enteredEmail ||
!enteredConfirmEmail ||
!enteredPassword ||
enteredPassword < 6 ||
enteredEmail !== enteredConfirmEmail ||
!enteredEmail.includes("@")
) {
console.log("Incorrect data");
return res.redirect("/signup");
}
const existingUser = await db
.getDb()
.collection("users")
.findOne({ email: enteredEmail });
if (existingUser) {
console.log('User already exists');
return res.redirect('/signup');
}
const hashedPassword = await bcrypt.hash(enteredPassword, 12);
const user = {
email: enteredEmail,
password: hashedPassword,
};
await db.getDb().collection("users").insertOne(user);
res.redirect("/login");
});
이건 DB를 연결하는 부분입니다.
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;
let database;
async function connectToDatabase() {
const client = await MongoClient.connect(
'mongodb://localhost:27017'
);
database = client.db('auth-demo');
}
function getDb() {
if (!database) {
throw { message: 'You must connect first!' };
}
return database;
}
module.exports = {
connectToDatabase: connectToDatabase,
getDb: getDb,
};
router.post("/signup", async function (req, res) {
const userData = req.body;
const enteredEmail = userData.email;
const enteredConfirmEmail = userData["confirm-email"];
const enteredPassword = userData.password;
먼저 이 함수에는 DB와 관련한 작업이 포함될 예정이기 때문에 async function으로 선언하여 비동기 작업을 컨트롤할 수 있도록 준비한다.
클라이언트에서 회원가입에 대한 post 요청이 발생하면 회원가입 화면에서 사용자가 입력한 ID
나 Password
, email
같은 정보를 req
에 담아온다. 그러면 각각의 변수들을 선언하여 거기다가 유저가 입력한 정보를 담는다.
if (
!enteredEmail ||
!enteredConfirmEmail ||
!enteredPassword ||
enteredPassword < 6 ||
enteredEmail !== enteredConfirmEmail ||
!enteredEmail.includes("@")
) {
console.log("Incorrect data");
return res.redirect("/signup");
}
유저가 보낸 정보를 정리했으면, 유효성 검사를 해야한다. 여기서의 유효성 검사는 공백이나 입력 불가능한 특수문자가 있는 지 확인하고 이메일처럼 특정 규칙에 따라 작성되어야 하는 문자열이 제대로 작성되었는지 확인하는 과정이다.
만약 항목들중에서 입력값이 없어 undefined 상태로 넘어오거나, 이메일이 @ 없이 작성되었거나 한다면 오류 메세지를 출력하고 초기 화면으로 redirect한다.
const existingUser = await db
.getDb()
.collection("users")
.findOne({ email: enteredEmail });
if (existingUser) {
console.log('User already exists');
return res.redirect('/signup');
}
이것도 크게 보면 유효성 검사일 수 있는데 사실 아직 정확한 용어 정리가 되어있지 않아서 일단은 이렇게 적는다!
앞에서 이메일이나 비밀번호가 규칙을 벗어나지 않았음을 확인한 뒤에는, 가입하고자 하는 이메일이나 계정이 중복되지는 않는지 확인해야 한다. 즉, 동일한 계정으로 가입한 사람이 있는지 확인하는 과정이다.
existingUser
라는 변수에 db.getDb().collection("users").findOne({ email: enteredEmail });
를 실행하여 DB에서 유저가 입력한 email을 쿼리에 넣어서 동일한 계정으로 가입한 유저가 있는지 검색한다. 여기서는 mongoDB를 사용하고 있어서 DB에서 데이터를 받는 코드는 DB에 따라 상이할 수 있다.
만약 불러온 데이터가 undefined
가 아니라면, 그러니까 값이 존재한다면 그건 중복되는 데이터가 있다는 의미이므로 역시 초기화면으로 redirect한다.
여기서의 초기화면은 맨 처음의 랜딩 페이지가 아니라 그냥 회원가입창을 말하는 것 같다.
const hashedPassword = await bcrypt.hash(enteredPassword, 12);
const user = {
email: enteredEmail,
password: hashedPassword,
};
await db.getDb().collection("users").insertOne(user);
res.redirect("/login");
});
이 강의에서는 bcrypt
라는 암호화 라이브러리를 사용한 것 같다. bycrypt는 입력한 문자열을 일련의 알고리즘을 통해서 알아볼 수 없는 문자열로 암호화해준다. 다만 한 번 암호화한 문자열은 다시 기존의 문자열로 되돌릴 수 없다.
내가 secret123
이라는 문자열을 bycript.hash("secret123", 12);
를 통해 암호화하면 jk23489sfsa3f2fn23fj2...
처럼 암호화된 문자열이 리턴되는데, 누군가 다시 secret123
이라는 비밀번호를 입력했을 때 이게 암호화된 비밀번호와 같은지 아닌지 비교하는 것은 가능하지만, 암호화된 문자열을 다시 원래의 문자열인secret123
으로 바꾸는 것은 불가능하다. 다행스럽게도 로그인 시 입력되는 비밀번호와 DB에 저장되어있는 암호화된 문자열이 같은 지 확인하는 것은bycript.compare
이라는 메서드를 통해서 서로 일치하는지 확인할 수있기 때문에 그 과정은 간단하게 진행할 수 있다.
근데 옛날에 게임이나 그런거 할때 비밀번호 까먹고 비밀번호 찾기 하면, 내 원래 비밀번호를 알려주는 게 아니라 맨날 새로 비밀번호를 만들라고 했었는데 이제서야 이해가 된다. 비밀번호를 안 알려주는게 아니라 걔네도 모르는거였다. 하핳.
이렇게 암호화된 비밀번호
와 email
정보는 user
객체에 담겨서 DB에 저장되고, 회원가입에 성공한 유저는 login page로 redirect된다.
내일은 가능하면 session을 통해서 인증 정보를 확인하는 방법을 공부해보자.