ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백오피스 프로젝트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에 대한 삭제제)

     

     

    댓글

Designed by Tistory.