-
백오피스 프로젝트4. 회원가입 시 이메일인증, 회원가입 CRUD, 백엔드 API 기능구현Projects/Qwerty - 배달의민족 (team) 2023. 7. 24. 17:58
고객님 이메일 인증 회원가입 및 로그인 시연영상
사장님 프론트엔드-백엔드 연결 시연영상
고객님 파트 회원가입 이메일 인증 및 고객 CRUD 코드
// routes>users.routes.js const express = require('express'); const bcrypt = require('bcrypt'); const nodemailer = require('nodemailer'); const router = express.Router(); // Middleware const authMiddleware = require('../middlewares/cusAuthMiddleware.js'); // JWT const jwt = require('jsonwebtoken'); // Model const { Users, Menus, Stores } = require('../models/index.js'); const { Op } = require('sequelize'); // 회원가입 API (POST) router.post('/user/signup', async (req, res) => { console.log('req.body =>', req.body); const { email, verifyNumberInput, password, passwordConfirm } = req.body; try { const existUserEmail = await Users.findOne({ where: { email } }); const passwordCheck = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$/; const hashedPassword = await bcrypt.hash(password, 10); console.log('hashedPassword =>', hashedPassword); if (!email || !verifyNumberInput || !password || !passwordConfirm) { return res.status(400).json({ message: '입력값이 유효하지 않습니다.' }); } // Session에서 verifyNumber 조회 const verifyNumber = req.session.verifyNumber; console.log('verfifyNumber of Session =>', verifyNumber); if (verifyNumberInput != verifyNumber) { return res.status(412).json({ message: '인증번호가 일치하지 않습니다.' }); } if (existUserEmail) { return res.status(412).json({ message: '중복된 email입니다.' }); } if (!password || password.length < 4 || !passwordCheck.test(password)) { return res.status(412).json({ message: '비밀번호 형식이 올바르지 않습니다.' }); } if (password !== passwordConfirm) { return res.status(412).json({ message: '비밀번호가 일치하지 않습니다.' }); } await Users.create({ email: email, password: hashedPassword }); // 사용한 verifyNumber 삭제 delete req.session.verifyNumber; return res.status(201).json({ message: '회원 가입에 성공하였습니다.' }); } catch { return res.status(400).json({ message: '사용자 계정 생성에 실패하였습니다.' }); } }); // e-mail 인증 API (POST) router.post('/user/signup/email', async (req, res) => { console.log(req.body); const { email } = req.body; console.log(email); try { const transporter = nodemailer.createTransport({ service: 'gmail', port: 587, host: 'smtp.gmail.com', secure: false, requireTLS: true, auth: { user: 'electruc0095@gmail.com', pass: 'yfjlkxwnfxkjxmed', }, }); const min = 100000; const max = 999999; const verifyNumber = Math.floor(Math.random() * (max - min + 1)) + min; // Session에 verifyNumber 저장 req.session.verifyNumber = verifyNumber; transporter.sendMail({ from: '쿼티의 민족', to: email, subject: '[쿼티의 민족] 반갑습니다! 인증번호를 보내드립니다.', text: `우측의 6자리 인증번호를 '인증번호 입력란'에 입력해주세요! => ${verifyNumber}`, }); return res.status(200).json({ message: '전송 성공' }); } catch { return res.status(400).json({ message: '전송 실패' }); } }); // log-in API (POST) router.post('/login', async (req, res) => { const { email, password } = req.body; const existUser = await Users.findOne({ where: { email } }); try { if (!req.body) { return res.status(404).json({ message: '입력값이 존재하지 않습니다.' }); } if (!existUser) { //!passwordMatch return res.status(412).json({ message: 'email 또는 비밀번호를 확인해주세요.' }); } // JWT 생성 const token = jwt.sign({ userId: existUser.userId }, 'customized-secret-key'); // Cookie 발급 res.cookie('authorization', `Bearer ${token}`); return res.status(200).json({ token: token }); } catch (error) { console.log(error); return res.status(400).json({ message: 'log-in에 실패하였습니다.' }); } }); // log-out API (POST) router.post('/logout', authMiddleware, async (req, res) => { try { res.clearCookie('authorization'); return res.status(200).json({ message: 'log-out 되었습니다.' }); } catch { return res.status(400).json({ message: 'log-out에 실패하였습니다.' }); } }); // 음식점 조회 API (GET) router.get('/user/stores', async (req, res) => { try { const stores = await Stores.findAll({ attributes: ['storeId', 'storeImage', 'storeName', 'totalRating'], order: [['totalRating', 'DESC']], }); return res.status(200).json(stores); } catch { return res.status(400).json({ message: '음식점 조회에 실패하였습니다.' }); } }); // 고객 메뉴조회 API (GET) router.get('/user/:storeId/getMenuAll', async (req, res) => { const { storeId } = req.params; try { const menus = await Menus.findAll({ attributes: ['menuId', 'menuName', 'menuImage', 'price'], order: [['menuId', 'DESC']], where: { storeId: storeId }, }); res.status(200).json(menus); } catch (error) { console.log(error); res.status(400).json({ message: '요청을 정상적으로 받아들이지 못했습니다.' }); } }); // e-mail 인증 API (POST) router.post('/user/signup/email', async (req, res) => { console.log(req.body); const { email } = req.body; console.log(email); try { const transporter = nodemailer.createTransport({ service: 'gmail', port: 587, host: 'smtp.gmail.com', secure: false, requireTLS: true, auth: { user: 'electruc0095@gmail.com', pass: 'yfjlkxwnfxkjxmed', }, }); const min = 100000; const max = 999999; const verifyNumber = Math.floor(Math.random() * (max - min + 1)) + min; // Session에 verifyNumber 저장 req.session.verifyNumber = verifyNumber; transporter.sendMail({ from: '쿼티의 민족', to: email, subject: '[쿼티의 민족] 반갑습니다! 인증번호를 보내드립니다.', text: `우측의 6자리 인증번호를 '인증번호 입력란'에 입력해주세요! => ${verifyNumber}`, }); return res.status(200).json({ message: '전송 성공' }); } catch { return res.status(400).json({ message: '전송 실패' }); } }); // 사용자 정보 조회 API (GET) router.get('/users/:userId', authMiddleware, async (req, res) => { const { userId } = res.locals.user; try { const user = await Users.findOne({ attributes: ['userId', 'email', 'point', 'createdAt', 'updatedAt'], where: { userId }, }); return res.status(200).json({ data: user }); } catch { return res.status(400).json({ message: '사용자 정보 조회에 실패하였습니다.' }); } }); // 사용자 정보 수정 API (PUT) router.put('/users/:userId', authMiddleware, async (req, res) => { const { userId } = res.locals.user; const { password, newPassword, newPasswordConfirm } = req.body; try { const existUser = await Users.findOne({ where: { userId } }); const passwordCheck = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$/; const passwordMatch = await bcrypt.compare(password, existUser.password); const hashedNewPassword = await bcrypt.hash(newPassword, 10); if (!password) { return res.status(400).json({ message: '입력값이 유효하지 않습니다.' }); } if (!passwordMatch) { return res.status(400).json({ message: '비밀번호가 일치하지 않습니다.' }); } if (newPassword !== newPasswordConfirm) { return res.status(412).json({ message: '변경된 비밀번호가 일치하지 않습니다.' }); } if (!newPassword || newPassword.length < 4 || !passwordCheck.test(newPassword)) { return res.status(412).json({ message: '변경된 비밀번호 형식이 올바르지 않습니다.' }); } await Users.update({ password: hashedNewPassword }, { where: { userId } }); return res.status(200).json({ message: '사용자 정보 수정에 성공하였습니다.' }); } catch { return res.status(400).json({ message: '사용자 정보 수정에 실패하였습니다.' }); } }); // 회원탈퇴 API (DELETE) router.delete('/users/:userId', authMiddleware, async (req, res) => { const { userId } = res.locals.user; const { email, password } = req.body; try { const existUser = await Users.findOne({ where: { userId } }); const passwordMatch = await bcrypt.compare(password, existUser.password); if (!email || !password) { return res.status(400).json({ message: '입력값이 유효하지 않습니다.' }); } if (email !== existUser.email || !passwordMatch) { return res.status(412).json({ message: 'email 또는 비밀번호를 확인해주세요.' }); } await Users.destroy({ where: { [Op.and]: [{ userId }, { email: existUser.email }] }, }); return res.status(200).json({ message: '사용자 정보 삭제에 성공하였습니다.' }); } catch { return res.status(400).json({ message: '사용자 정보 조회에 실패하였습니다.' }); } }); module.exports = router;
백엔드에서 구현한 기능들 (고객님 파트)
No. / 기능명/ API URL / Method / request / response /// userRoute.js 1. 회원 가입 API API URL: '/user/signup' Method: post Request: {"email":"", "verifyNumberInput":"", "password":"", "passwordConfirm":""} Response: {"message":'회원 가입에 성공하였습니다.} 2. email 인증키 발송 API API URL: '/user/signup/email' Method: post Request: {"email":""} Response: {"message':"전송 성공"} 3. 고객 로그인 API API URL: '/login' Method: post Request: {"email":"", "password":""} Response: {"message':"log-in 되었습니다."} 4. 고객 로그아웃 API API URL: '/logout' Method: post Request: (request 없음) Response: {"message":"log-out 되었습니다."} 5. 음식점 조회 APi API URL: "/user/stores" Method: get Request: (request 없음) Response: {"storeId":"",'storeImage':"", 'storeName':"", 'totalRating':""} 6. 고객 메뉴 조회 API API URL: '/user/:storeId/getMenuAll' Method: get Request: (request 없음) Response: {'menuId':"", 'menuName':"", 'menuImage':"", 'price':""} 7. 사용자 정보 조회 API API URL: '/users/:userId' Method: get Request: (request 없음) Response: {'userId':"", 'email':"", 'point':"", 'createdAt':"", 'updatedAt':""} 8. 사용자 정보 수정 APi API URL: '/users/:userId' Method: put Request: { "password":"", "newPassword":"", "newPasswordConfirm":"" } Response: {"message':"사용자 정보 수정에 성공하였습니다."} 9. 회원탈퇴 API API URL: '/users/:userId' Method: delete Request: { "email":"", "password":"" } Response: {"message':"사용자 정보 삭제에 성공하였습니다."} // userReviewRoute.js 1. 리뷰 작성 APi API URL: '/user/store/:storeId/review' Method: post Request: {"rating":"","content":""} Response: {"message":"리뷰 작성에 성공하였습니다."} => 고객님 로그인 후 사용가능 2. 리뷰 목록 조회 API API URL: '/user/store/:storeId/review' Method: post Request: (request 없음) Response: {'reviewId':'', 'storeId':'', 'userId':'', 'rating':'', 'content':'', 'createdAt', 'updatedAt':''} 3. 리뷰 수정 API API URL: '/user/store/:storeId/review/:reviewId' Method: put Request: {"reating":"","content":""} Response: {"data":'리뷰 수정에 성공하였습니다.} 4. 리뷰 삭제 API API URL: '/user/store/:storeId/review/:reviewId' Method: delete Request: (request 없음) Response: {"data":'리뷰 삭제에 성공하였습니다.} // ordersRoute.js 1. 고객 주문메뉴 생성 API API URL: '/user/store/:storeId/order/menu/:menuId' Method: POST Request: {"quantity"} Response: {"message":'성공'} // 실패시 {"message":'실패'} => 고객님 로그인 후 사용가능 2 고객 주문 생성 API API URL : '/user/store/:storeId/order' Method: POST Request: {"address"} Response: {"message":'주문 완료!'} => 고객님 로그인 후, 고객 주문메뉴 생성 이후 사용가능 3. 고객이 배달을 받았는지 여부 API API URL: '/user/order/:orderId/delivery' Method: POST Request 없음 Response: { message: '배달 완료 처리되었습니다!'} => 고객님 로그인 후, 고객 주문메뉴 생성 이후 사용가능 4. 고객 주문조회 API API URL: '/user/order/:orderId' Method: GET Request 없음 Response: { data: order} => 고객님 로그인 후, 고객 주문메뉴 생성 이후 사용가능 //여기부터는 사장님 파트입니다. 5. 사장님 주문 조회 API API URL: '/ceo/:storeId/order' Method: GET Request 없음 Response: { data: order} => 사장님 로그인 후, 고객이 주문 생성했을때 조회 가능. (해당 storeId의 주문 목록조회) 6. 사장님 주문 상세 조회 API URL: '/ceo/:storeId/order/:orderId' Method: GET Request 없음 Response: { data: orderMenus} => 사장님 로그인 후, 고객이 주문 생성했을때 조회 가능. (해당 orderId의 주문 상세조회) 7. 사장님 주문 취소 API API URL: '/ceo/:storeId/order/:orderId' Method: DELETE Request 없음 Response: { data: '사장님 주문 취소 완료'} => 사장님 로그인 후, 고객이 주문 생성했을때 삭제 가능. (해당 orderId에 대한 삭제제)
'Projects > Qwerty - 배달의민족 (team)' 카테고리의 다른 글
백오피스 프로젝트 5. 프로젝트 완성 (0) 2023.07.24 백오피스 프로젝트 3. view Engline (ejs), 리뷰 CRUD, 프론트엔드 - 백엔드 연결 (0) 2023.07.24 백오피스 프로젝트 2. 고객님 주문, 주문조회, 회원가입, 이메일인증, 트랜젝션 (0) 2023.07.18 백오피스 프로젝트 1.역할분배, 와이어프레임, ERD, API 명세 (0) 2023.07.17