
๐ฏ ๋ผ์ฐํฐ์ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ณ ,
crypto๋ด์ฅ ๋ชจ๋์ ํตํด ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํฉ๋๋ค.
http-status-code ํจํค์ง๋ฅผ ์ค์นํ์ฌ ์ํ ์ฝ๋๋ฅผ ๋ณ์๋ช
์ผ๋ก ์ฐธ์กฐํ ์ ์๋๋ก ํฉ๋๋ค.
npm install http-status-code
controller๋ฅผ ํตํด์ ๋ผ์ฐํฐ๋ ๊ฒฝ๋ก ์ค์ ๋ง ๋ด๋นํ๊ณ , ์์ฒญ ์ฒ๋ฆฌ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ํตํด ์ํํ๋๋ก ๋ถ๋ฆฌํฉ๋๋ค.
๐ BOOK-SHOP
โโโ ๐ controller
โ โโโ ๐ BookController.js
โ โโโ ๐ CartController.js
โ โโโ ๐ LikeController.js
โ โโโ ๐ OrderController.js
โ โโโ ๐ UserController.js
โโโ ๐ node_modules
โโโ ๐ routes
โ โโโ ๐ books.js
โ โโโ ๐ carts.js
โ โโโ ๐ likes.js
โ โโโ ๐ orders.js
โ โโโ ๐ users.js
โโโ ๐ .env
โโโ ๐ app.js
โโโ ๐ package-lock.json
โโโ ๐ package.json
๐ค ์ ์ปจํธ๋กค๋ฌ๋ PascalCase๋ก ์์ฑํ ๊น?
์ปจํธ๋กค๋ฌ ํ์ผ์ ํด๋์ค์ฒ๋ผ ํ๋์ ๊ฐ์ฒด์ฒ๋ผ ํน์ ์ญํ ์ ์ํํ๋ ๋ชจ๋๋ก์์ ์ญํ ์ ๊ตฌ๋ถํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ ์ฑ ๊ด๋ฆฌ๋ฅผ ์ํด์ ์ฃผ๋ก PascalCase๋ก ์์ฑํ๊ฒ ๋ฉ๋๋ค.
Table users {
id integer [primary key]
email varchar
password varchar
salt varchar
}
Salt(์ํธ)
๊ฐ์ ๋น๋ฐ๋ฒํธ๋ผ๋ ๋งค๋ฒ ๋ค๋ฅธ ํด์๊ฐ์ ๋ง๋ค๊ธฐ ์ํด ์ถ๊ฐํ๋ ๋๋ค ๋ฌธ์์ด์ ๋๋ค.
๐ ์์ ํ ๋ถ๋ถ์ ์ฃผํฉ์ ๊ธ์จ๋ก ๋ํ๋
๋๋ค.

Method : POST
URL : /users/join
HTTP Status Code : 201 Created
Request Body
{
"email": "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์ด๋ฉ์ผ",
"password" : "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋น๋ฐ๋ฒํธ"
}
Method : POST
URL : /users/login
HTTP Status Code : 200 Ok
Request Body
{
"email": "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์ด๋ฉ์ผ",
"password" : "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋น๋ฐ๋ฒํธ"
}
JWT TokenMethod : POST
URL :/users/rest
HTTP Status Code : 200 Ok
Request Body
{
"email": "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์ด๋ฉ์ผ"
}
{
"email": "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์ด๋ฉ์ผ"
}
Method : PUT
URL : /users/rest
HTTP Status Code : 200 Ok
Request Body
{
"email": "์ด์ ํ์ด์ง์์ ์
๋ ฅํ ์ด๋ฉ์ผ",
"password" : "์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋น๋ฐ๋ฒํธ"
}
const conn = require('../mariadb');
const { StatusCodes } = require('http-status-codes');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const dotenv = require('dotenv');
dotenv.config();
const join = (req, res) => {
const { email, password } = req.body;
// ์ํธํ๋ ๋น๋ฐ๋ฒํธ์ salt ๊ฐ์ ๊ฐ์ด DB ์ ์ฅ
const salt = crypto.randomBytes(10).toString('base64');
const hashPassword = crypto
.pbkdf2Sync(password, salt, 10000, 10, 'sha512')
.toString('base64');
const sql = 'INSERT INTO users (email, password, salt) VALUES(?, ?, ?)';
const values = [email, hashPassword, salt];
conn.query(sql, values, (err, result) => {
if (err) {
return res.status(StatusCodes.BAD_REQUEST).end();
}
res.status(StatusCodes.CREATED).json(result);
});
};
const login = (req, res) => {
const { email, password } = req.body;
const sql = 'SELECT * FROM users WHERE email = ?';
conn.query(sql, email, (err, result) => {
if (err) {
return res.status(StatusCodes.BAD_REQUEST).end();
}
const loginUser = result[0];
const hashPassword = crypto
.pbkdf2Sync(password, loginUser.salt, 10000, 10, 'sha512')
.toString('base64');
if (loginUser && loginUser.password === hashPassword) {
const token = jwt.sign(
{
email: loginUser.email,
},
process.env.PRIVATE_KEY,
{
expiresIn: '5m',
issuer: 'eunmi',
}
);
res.cookie('token', token, {
httpOnly: true,
});
console.log(token);
return res.status(StatusCodes.OK).json(result);
} else {
return res.status(StatusCodes.UNAUTHORIZED).end();
}
});
};
ํ์๊ฐ์
, ๋ก๊ทธ์ธ ๋ก์ง์์๋ PBKDF2(Password-Based Key Derivation Function 2) ๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํ์์ต๋๋ค.
salt ์์ฑconst salt = crypto.randomBytes(10).toString('base64');
crypto.randomBytes(10) : 10๋ฐ์ดํธ ํฌ๊ธฐ์ ๋๋ค ๊ฐ์ ์์ฑํฉ๋๋ค.
toString('base64') : Base64 ์ธ์ฝ๋ฉํ์ฌ ๋ฌธ์์ด๋ก ๋ณํํฉ๋๋ค. ( ์ด์ง ๋ฐ์ดํฐ๋ฅผ ์ฌ๋์ด ์ฝ์ ์ ์๋ ๋ฌธ์์ด๋ก ๋ณํํ๋ ๊ณผ์ )
const hashPassword = crypto
.pbkdf2Sync(password, salt, 10000, 10, 'sha512')
.toString('base64');
pbkdf2Sync(์๋ณธ ๋น๋ฐ๋ฒํธ, salt, ๋ฐ๋ณต ํ์, ์ถ๋ ฅ ๊ธธ์ด, ํด์ ์๊ณ ๋ฆฌ์ฆ)
password : ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์๋ณธ ๋น๋ฐ๋ฒํธ
salt : ์์์ ์์ฑํ ๋๋ค ์ํธ
10000 : ํด์ฑ์ ๋ฐ๋ณตํ๋ ํ์ (๋ฐ๋ณต ํ์๊ฐ ๋์์๋ก ํดํน ์๋์ ๋ํ ์ ํญ๋ ฅ์ด ์ฆ๊ฐ)
10 : ์ต์ข
ํด์๊ฐ์ ๊ธธ์ด (๋ฐ์ดํธ ๋จ์)
sha512 : SHA-512 ํด์ ์๊ณ ๋ฆฌ์ฆ
๐ค ์ DB์
hashPassword์salt๋ฅผ ๊ฐ์ด ์ ์ฅํด์ผํ ๊น?๊ฐ์ด ์ ์ฅํด์ผ ๋ก๊ทธ์ธ ์์ ๊ฐ์ ์ํธ๋ฅผ ์ฌ์ฉํด์ ์ํธํ๋ ๋น๋ฐ๋ฒํธ์ ๋น๊ตํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ํธํ์ ๋ํ ๊ฐ๋ ์ ์ดํดํ๋ฉด์ ๋ณด์์ ์ค์์ฑ์ ๋ค์๊ธ ๊นจ๋ฌ์์ต๋๋ค.