회원가입
//회원가입
router.post('/join', (req, res)=> {
const {email, password} = req.body;
let sql = 'INSERT INTO users (email, password) VALUES (?, ?)';
let values = [email, password];
conn.query(sql, values,
(err, results) => {
if(err) {
console.log(err);
return res.status(400).end(); // BAD REQUEST
}
return res.status(200).json(results);
}
)
return res.json('회원가입');
});
400 : 입력이 잘못된 것으로 처리
statuscode를 하드코딩 하는 것은 좋지 않기 때문에, 다른 방법으로 처리하도록 하여야합니다.
http-status-codes 모듈
https://www.npmjs.com/package/http-status-codes
CodeConstantReason Phrase
| 200 | OK | OK |
| 201 | CREATED | Created |
| 400 | BAD_REQUEST | Bad Request |
| 401 | UNAUTHORIZED | Unauthorized 미인증 됨(비번/아이디 틀림) |
| 402 | PAYMENT_REQUIRED | Payment Required |
| 403 | FORBIDDEN | Forbidden 접근 권리 없음 |
| 404 | NOT_FOUND | Not Found |
| 500 | INTERNAL_SERVER_ERROR | Internal Server Error |
| 501 | NOT_IMPLEMENTED | Not Implemented |
| 502 | BAD_GATEWAY | Bad Gateway |
const {StatusCodes} = require('http-status-codes'); //status code 모듈
//회원가입
router.post('/join', (req, res)=> {
const {email, password} = req.body;
let sql = 'INSERT INTO users (email, password) VALUES (?, ?)';
let values = [email, password];
conn.query(sql, values,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end(); // BAD REQUEST
}
return res.status(StatusCodes.CREATED).json(results);
}
)
});

현재에는 라우터가 로직까지 다 수행하고 있는데요!
- 프로젝트 규모가 커질 수록 코드가 복잡해짐 -> 해결방법: 코드를 간결하고 가독성 높게 만들어주자.
- 가독성 X
- 트러블 슈팅 X
=> 유지보수 하기 어려워집니다.
경로를 찾은 다음 역할 -> '콜백함수'만을 빼내보겠습니다.
컨트롤러
: 프로젝트에서 매니저 역할을 하는 파일 : 관장!
- 누군가에게 일을 어떻게 시켜야할 지 알고 있습니다. = 직접 일을 하진 않을 것
=> router 통해 사용자의 요청(req)이 url을 찾아오면, 컨트롤러가 일을 시키고, 다시 받아 사용자에게 res를 돌려줍니다.
routes/users.js
const express = require('express');
const router = express.Router();
const conn = require('../mariadb'); //db 모듈
const {
join,
login,
passwordResetRequest,
passwordReset
} = 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;
길만 찾아주고 Controller와 연결해줌
controller/UserController.js
const conn = require('../mariadb'); //db 모듈
const {StatusCodes} = require('http-status-codes'); //status code 모듈
const join = (req, res)=> {
const {email, password} = req.body;
let sql = 'INSERT INTO users (email, password) VALUES (?, ?)';
let values = [email, password];
conn.query(sql, values,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end(); // BAD REQUEST
}
return res.status(StatusCodes.CREATED).json(results);
}
)
};
const login = (req, res)=>{
res.json('로그인');
};
const passwordResetRequest = (req, res)=>{
res.json('비밀번호 초기화 요청');
};
const passwordReset = (req,res) => {
res.json('비밀번호 초기화');
};
module.exports = {
join,
login,
passwordResetRequest,
passwordReset
};
로그인
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];
if(loginUser && loginUser.password == password){
const token = jwt.sign({
email : loginUser.email
}, process.env.PRIVATE_KEY, {
expiresIn : '5m',
issuer : "kayoung"
})
//토큰 쿠키에 담기
res.cookie("token", token, {
httpOnly : true
});
console.log(token);
return res.status(StatusCodes.OK).json(results);
} else {
return res.status(StatusCodes.UNAUTHORIZED).end() //401 : Unauthorized
}
})
};


비밀번호 초기화 요청 & 비밀번호 초기화
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() //401 : Unauthorized
}
})
};
//passwordResetRequest에서 res한 email을 front가 passwordReset req으로 보내줄것임
const passwordReset = (req,res) => {
const {email, password} = req.body;
let sql = 'UPDATE users SET password = ? WHERE email = ?';
let values = [password, 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);
}
}
)
};


비밀번호 암호화 - crypto 모듈
const crypto = require('crypto');
const password = "1111";
//비밀번호 암호화
const salt = crypto.randomBytes(10).toString('base64'); //매개변수로 들어오는 걸로 64길이의 랜덤바이트만들고 base64방식의 문자열로 담음
const hashPassword = crypto.pbkdf2Sync(password, salt, 1000, 10, 'sha512').toString('base64'); //암호화 알고리즘 10000 : 해시함수 반복횟수 / 64 : 길이
console.log(hashPassword);
출력 > V9Xi8ne8DVreRw== (실행할 때마다 다른 값이 출력됨)
<- password 1111이 암호화되어 출력된 것을 확인할 수 있습니다.
하지만 이는 단방향이기 때문에 암호화만 가능하고 복호화가 불가능합니다.
해결방법
1. randomBytes로 계속 바귀는 salt를 고정시켜 암호화된 코드를 고정시켜 활용하는 방법
2. salt를 데이터베이스에 저장해두고, 활용
-> 회원가입 시 비밀번호를 암호화해서 암호회된 비밀번호 & salt 값 저장
-> 로그인 시 이메일&비밀번호(날 것) => salt값 꺼내서 비밀번호 암호화 해보고 => DB 비밀번호와 비교
const conn = require('../mariadb'); //db 모듈
const {StatusCodes} = require('http-status-codes'); //status code 모듈
const jwt = require('jsonwebtoken'); //jwt 모듈
const crypto = require('crypto'); //crypto 모듈 : 암호화
const dotenv = require('dotenv'); //dotenv 모듈
dotenv.config();
const join = (req, res)=> {
const {email, password} = req.body;
let sql = 'INSERT INTO users (email, password, salt) VALUES (?, ?, ?)';
// 암호화된 비밀번호와 salt 값을 같이 DB에 저장
const salt = crypto.randomBytes(10).toString('base64');
const hashPassword = crypto.pbkdf2Sync(password, salt, 1000, 10, 'sha512').toString('base64');
console.log(hashPassword);
let values = [email, hashPassword, salt];
conn.query(sql, values,
(err, results) => {
if(err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end(); // BAD REQUEST
}
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];
// salt값 꺼내서 날 것으로 들어온 비밀번호 암호화 해보고
const hashPassword = crypto.pbkdf2Sync(password, loginUser.salt, 1000, 10, 'sha512').toString('base64');
// => DB 비밀번호랑 비교
if(loginUser && loginUser.password == hashPassword){
const token = jwt.sign({
email : loginUser.email
}, process.env.PRIVATE_KEY, {
expiresIn : '5m',
issuer : "kayoung"
})
//토큰 쿠키에 담기
res.cookie("token", token, {
httpOnly : true
});
console.log(token);
return res.status(StatusCodes.OK).json(results);
} else {
return res.status(StatusCodes.UNAUTHORIZED).end() //401 : Unauthorized
}
})
};


onst 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() //401 : Unauthorized
}
})
};
//passwordResetRequest에서 res한 email을 front가 passwordReset req으로 보내줄것임
const passwordReset = (req,res) => {
const {email, password} = req.body;
let sql = 'UPDATE users SET password=?, salt=? WHERE email=?';
// 암호화된 비밀번호와 salt 값을 같이 DB에 저장
const salt = crypto.randomBytes(10).toString('base64');
const hashPassword = crypto.pbkdf2Sync(password, salt, 1000, 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);
}
}
)
};

'Devcourse > BookShop 사이드 프로젝트' 카테고리의 다른 글
| [BookShop 프로젝트] 주문 API - LAST_INSERT_ID(), MAX()로 최신 데이터 가져오기, insertId, 벌크 INSERT (0) | 2026.02.20 |
|---|---|
| [BookShop 프로젝트] 장바구니 API - fk제약조건, in(~, ~) (0) | 2026.02.19 |
| [BookShop] 좋아요 API 구현 - count(), EXISTS (0) | 2026.02.18 |
| [BookShop 프로젝트] Books API 구현 - picsum (0) | 2026.02.14 |
| [BookShop 프로젝트] API 설계, dbdiagram (0) | 2026.02.09 |