authenticate, redux (1)_back

김종민·2022년 12월 13일
0

mern-Chat

목록 보기
3/5

들어가기
1. authenticate 관련해서, backend부터 시작해봅니다.


1. server.js

const express = require('express')
const app = express()
const dotenv = require('dotenv')
///express, dotenv를 import한다.

const databaseConnect = require('./config/db')
const authRouter = require('./routes/authRoute')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
///dataBase, bodyParser, cookieParser를
///불러준다.

dotenv.config({
  path: 'backend/config/.env',
})
///server에서 .env파일을 import하는 문법.
///file path를 저정해 준다.

app.use(bodyParser.json())
app.use(cookieParser())
///bodyParser를 넣어서 front에서 오는 data를 받을 수 있게함.

app.use('/api/messenger', authRouter)
///authRouter 사용하긋다.

const PORT = process.env.PORT || 5000
///port저정해 주기

app.get('/', (req, res) => {
  res.send('This is backend')
})
///home path접근시 controller

databaseConnect()
///위에서 부른 './db'를 실행한다.

app.listen(PORT, () => {
  console.log(`Server is running at ${PORT}`)
})
///서버 시작한다.

2. routes/authRoute.js

const { userRegister, userLogin } = require('../controller/authController')

const router = require('express').Router()

router.post('/user-register', userRegister)
router.post('/user-login', userLogin)

module.exports = router
///router를 불러주는 구 문법을 이해한다.
/// userRegister, userLogin 2개 controller를 만든다.
/// 두 개의 path를 만들어 준다

3. config/.env

PORT = 5000
DB_URL=mongodb://localhost:27017/messenger
SECRET_KEY: ASDFEW@234fSDF@
TOKEN_EXP: 7d
COOKIE_EXP=7

///.env 셋팅을 확인한다.

4. config/db.js

DB만드는 방법, DB connect를 확인한다.

const mongoose = require('mongoose')

const databaseConnect = () => {
  mongoose
    .connect(process.env.DB_URL, {
      useNewUrlParser: true, 
      useUnifiedTopology: true,
      ///위에 두개는 반사적으로 넣는다.
    })
    .then(() => {
      console.log('Mongo DB connected')
    })
    .catch((error) => {
      console.log(error)
    })
    ///.connect에 성공하면, .then, 실패하면, .catch.
}

module.exports = databaseConnect

5. models/authModel.js

const { model, Schema } = require('mongoose')

const registerSchema = new Schema(
  {
    userName: { type: String, required: true },
    email: { type: String, required: true },
    password: { type: String, required: true },
    image: { type: String, required: true },
  },
  { timestamps: true }
)

module.exports = model('user', registerSchema)

///authModel을 만드는 방법을 확인한다.

6. controller/authController.js

const formidable = require('formidable')
///json과 file을 동시에 server에서 받을 경우 사용
///client에서 formData로 보냈을때, 통으로 받음.
const validator = require('validator')
///front에서느 넘어오는 data의 유효성을 확인

const registerModel = require('../models/authModel')
///authModel을 불러줌.

const fs = require('fs')
///file을 다루는 라이브러리

const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const { options } = require('../routes/authRoute')
///bcrypt, jsonwebtoken을 import 해 준다.

///user Register부분.
module.exports.userRegister = (req, res) => {
  const form = formidable()
  form.parse(req, async (err, fields, files) => {
    const { userName, email, password, confirmPassword } = fields
    const { image } = files
    const error = []
    ///form부분을 잘 봐준다.
    ///formidalble()로 받으면, json, file을 동시에
    ///server에서 받을 수 있다
    ///json은 fields로 받고 파일은 files로 서버에서 받는다.
    ///여기서 error를 담을 error=[]를 만들어 놓는다.

	///아래부터 각각의 data가 전송되지 않았다면,
    ///error=[]에 message를 담아준다.
    if (!userName) {
      error.push('Please provide your name')
    }
    if (!email) {
      error.push('Please provide your email')
    }
    if (email && !validator.isEmail(email)) {
      error.push('Please provide your valid email')
    }
    if (!password) {
      error.push('Please provide your password')
    }
    if (!confirmPassword) {
      error.push('Please provide your confirmPassword')
    }
    if (password && confirmPassword && confirmPassword !== password) {
      error.push('password is different confirmPassword')
    }
    if (password && password.length < 6) {
      error.push('Please provide password much than 6 chat\r')
    }
    if (Object.keys(files).length === 0) {
      error.push('Please provide user image')
    }
    ///files을 check할 떄, Object.keys(files)로
    ///files를 확인하는 부분은 잘 확인해 둔다.
    
    if (error.length > 0) {
      res.status(400).json({
        error: {
          errorMessage: error,
        },
      })
      ///error에 error가 push된다면,
      ///error:{errorMessage}에 위에서 만든 error들을
      ///담아준다.
    } else {  ///error.length = 0 일 경우~
      const getImageName = files.image.originalFilename
      const randNumber = Math.floor(Math.random() * 9999)
      const newImageName = randNumber + getImageName
      files.image.originalFilename = newImageName
      ///client에서 보내온 fileName을 get해서
      ///rnadNumber를 만들어서 두 개를 결합해서
      ///newImageName을 만든 다음, 이름을 교체해 준다.

      const newPath =
        __dirname +
        `../../../frontend/public/image/${files.image.originalFilename}`
        ///사진이 저장될 path를 만들고 입력될 fileName을 
        ///${}로 만들어 준다.
        
      console.log(newImageName)
      try {
        const checkUser = await registerModel.findOne({
          email: email,
        })
        ///register controller이기 떄문에, email 중복체크함
        
        if (checkUser) {
          res.status(404).json({
            error: {
              errorMessage: ['Your email alreadt exist.'],
            },
          })
          ///같은 email이 있으면, error날려줌.
        } else {
          fs.copyFile(files.image.filepath, newPath, async (error) => {
      ///fs.copyFile을 이용해서, clinet에서 select한
      ///filePath, 위에서만든 사진이 저장될 newPath를
      ///argument로 넣고, callback함수 만듬.
      ///error가 없을 경우 아래부분 실햄.
          
            if (!error) {
              const userCreate = await registerModel.create({
                userName,
                email,
                password: await bcrypt.hash(password, 10),
                image: files.image.originalFilename,
              })
              ///DB의 registerModel을 불러서, DB에
              ///user data를 저장한다.
              ///password는 hash화 해서 넣어준다.
              
              const token = jwt.sign(
                {
                  id: userCreate._id,
                  email: userCreate.email,
                  userName: userCreate.userName,
                  image: userCreate.image,
                  registerTime: userCreate.createdAt,
                },
                ///jsonwebtoken을 이용해서 token을 만드는데,
                ///token에 userInfo를 저장해 놓는다.
                
                process.env.SECRET_KEY,
                { expiresIn: process.env.TOKEN_EXP }
              )
              ///SECRET_KEY와 expiresIn을 설정한다.
              
              const options = {
                expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
              }
              ///options로 현재시간 부터 7일 후의 시간을
              ///만든다.
              res.status(201).cookie('authToken', token, options).json({
                successMessage: 'Your Register Successful',
                token,
              })
              ///201을 날려주고, cookie에 'authToken'으로
              ///token을 저장시키고,
              ///client에 token을 return해 준다.
              ///cookie까지 저장시키는 이유는 추후 알아보자
            }
          })
        }
      } catch (error) {
        res.status(500).json({
          error: {
            errorMessage: ['Internal Server error'],
          },
        })
        ///error발생시 error=[]에 담아서 errorMessage날려줌.
      }
    }
  })
}

///User Login 부분.
module.exports.userLogin = async (req, res) => {
  const error = []
  ///error를 담을 공간을 만들어 줌.
  const { email, password } = req.body
  ///client부분에서 email, password받아줌.
  console.log(email, password)

  if (!email) {
    error.push('Please provide your email')
  }
  if (email && !validator.isEmail(email)) {
    error.push('Please provide your valid email')
  }
  if (!password) {
    error.push('Please Password provide')
  }
  if (error.length > 0) {
    res.status(400).json({
      error: {
        errorMessage: error,
      },
    })
    ///error발생시, error만들고, error에 담아줌.
    
  } else {
    try {
      const checkUser = await registerModel.findOne({
        email: email,
      })
      ///client에서 받은 email이 DB에 있는지 check함.

      if (checkUser) {
        const matchPassword = await bcrypt.compare(password, checkUser.password)
        ///matchPasswordf로 client에서 보내온 password와
        ///DB의 hash화된 password를 compare해서
        ///boolean으로 return해 줌.

		///true return일때,
        if (matchPassword) {
        
        ///register와 같이 token을 만들고 return해 준다.
          const token = jwt.sign(
            {
              id: checkUser._id,
              email: checkUser.email,
              userName: checkUser.userName,
              image: checkUser.image,
              registerTime: checkUser.createdAt,
            },
            process.env.SECRET_KEY,
            {
              expiresIn: process.env.TOKEN_EXP,
            }
          )
          console.log(token)
          const options = {
            expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
          }
          res.status(201).cookie('authToken', token, options).json({
            successMessage: 'Your login Successful',
            token,
          })
        } else {
          res.status(400).json({
            error: {
              errorMessage: ['Your password not valid'],
            },
          })
        }
      } else {
        res.status(400).json({
          error: {
            errorMessage: ['Your email nor found'],
          },
        })
      }
    } catch (err) {
      res.status(400).json({
        error: {
          errorMessage: ['Internal server error'],
        },
      })
    }
  }
}

///여기까지가 register, login 부분.
formidable부분이 신선하긴 한데,
formidable로 받은 file을 나중에 S3나 cloudflare에
넣을려면, 어떻게 해야 하는지 고민이 필요할듯.
///걍 multer로 받아야 하나 싶기도 함.

profile
코딩하는초딩쌤

5개의 댓글

comment-user-thumbnail
2023년 5월 10일

이것들은 제가 배우고 있는 복잡한 코드 조각입니다. Vampire Survivors 공유해주셔서 감사합니다

답글 달기
comment-user-thumbnail
2024년 3월 30일

Accomplish invigorating pleasure and save your life's daily practice and stress to the side for one evening. Employ the woman of your fantasies with Escorts in Naraina and draw out the stifled sexual dreams. Make all your filthy dreams and wants work out with captivating wonders and complete tactful tomfoolery.

답글 달기
comment-user-thumbnail
2024년 4월 6일

Our Beautiful Pitampura Escorts Service will surely wear revealing clothes and keep themselves naked to attract you in bed. They are always ready to give you the nourishing experience in bed and make your all moments cherishable. There are specific steps and procedures for enrolling girls at our agency.

답글 달기
comment-user-thumbnail
2024년 4월 15일

You can learn new life lessons and spend time with independent girls. Escorts Service Gurgaon offer you with greater excellence and confidence right from the first meeting.

답글 달기
comment-user-thumbnail
2024년 8월 1일

Sure Ahmedabad Escort will play with you, and selected of them will be content to thread you laterally, but it is not successful to sum too much to say nobody of the sorts of surroundings in which you are progressive time after an hour.

답글 달기