보안 전공생의 공부

게시판 만들기 / 회원가입 본문

WEB/Node

게시판 만들기 / 회원가입

수잉 2021. 11. 16. 14:08

회원가입은 회원(user) model을 만들고,

회원 데이터를 생성해(CRUD-create) DB에 저장하면 된다.

다만 주의해야할 점이 비밀번호가 추가된다는 점이다.

 

// models/User.js
const mongoose = require('mongoose');

// schema 
const userSchema = mongoose.Schema({
  username:{type:String, required:[true,'Username is required!'], unique:true},
  password:{type:String, required:[true,'Password is required!'], select:false},
  name:{type:String, required:[true,'Name is required!']},
  email:{type:String}
},{
  toObject:{virtuals:true}
});

// virtuals
userSchema.virtual('passwordConfirmation')
  .get(function(){ return this._passwordConfirmation; })
  .set(function(value){ this._passwordConfirmation=value; });

userSchema.virtual('originalPassword')
  .get(function(){ return this._originalPassword; })
  .set(function(value){ this._originalPassword=value; });

userSchema.virtual('currentPassword')
  .get(function(){ return this._currentPassword; })
  .set(function(value){ this._currentPassword=value; });

userSchema.virtual('newPassword')
  .get(function(){ return this._newPassword; })
  .set(function(value){ this._newPassword=value; });

// password validation
userSchema.path('password').validate(function(v) {
  const user = this; 
  
  // create user 
  if(user.isNew){ 
    if(!user.passwordConfirmation){
      user.invalidate('passwordConfirmation', 'Password Confirmation is required.');
    }

    if(user.password !== user.passwordConfirmation) {
      user.invalidate('passwordConfirmation', 'Password Confirmation does not matched!');
    }
  }

  // update user 
  if(!user.isNew){
    if(!user.currentPassword){
      user.invalidate('currentPassword', 'Current Password is required!');
    }
    else if(user.currentPassword != user.originalPassword){
      user.invalidate('currentPassword', 'Current Password is invalid!');
    }

    if(user.newPassword !== user.passwordConfirmation) {
      user.invalidate('passwordConfirmation', 'Password Confirmation does not matched!');
    }
  }
});

// model & export
const User = mongoose.model('user',userSchema);
module.exports = User;

required:[true,'별명 필요행 ㅇㅅㅇ!']

required:true/fase 대신 배열([ ])이 들어갔다. 첫번째는 true/false값이고, 두번째는 에러메세지이다.

-> 배열을 이용해 에러메세지의 내용을 원하는대로 작성 가능

select:false

DB에서 해당 모델을 읽어올 때, 해당 값을 읽어오지 않는다. 

-> DB에서 비밀번호 값을 읽어오지 않게 설정

 

DB에 저장되는 값 이외의 항목이 필요한 경우 virtual 항목으로 만든다.

userSchema.virtual('...')

DB에 저장될 필요는 없지만, model에서 사용하고 싶은 항목들을 virtual로 만들면 된다.

 

 
userSchema.path('password').validate(function(v) {
...
});

DB에 user을 create, update 하기 전에 pw 값이 유효한지 확인하는 코드이다.

이때 사용되는 this는 user model이다.

user.isnew를 통해 현재 pw validation이 회원가입 단계인지, 회원정보 수정 단계인지 확인한다.

회원가입 단계인 경우 -> pwconfirm값이 없는 경우 / pwconfirm값과 pw값이 일치하지 않는 경우

회원정보 수정 단계인 경우 -> pw값이 없는 경우 / 현재pw값과 원래pw값이 일치하지 않는 경우 /  새pw값과 pwconfirm값이 다른 경우

로 분류하여 invalidate(유효하지 않음)처리를 하게 된다.

user.invalidate함수를 사용하는데

이때 첫 번째 인자는 항목이름, 두 번째 인자는 에러메시지를 받는다.

 

// routes/users.js

const express = require('express');
const router = express.Router();
const User = require('../models/User');

// Index
router.get('/', function(req, res){
  User.find({})
    .sort({username:1})
    .exec(function(err, users){
      if(err) return res.json(err);
      res.render('users/index', {users:users});
    });
});

// New
router.get('/new', function(req, res){
  res.render('users/new');
});

// create
router.post('/', function(req, res){
  User.create(req.body, function(err, user){
    if(err) return res.json(err);
    res.redirect('/users');
  });
});

// show
router.get('/:username', function(req, res){
  User.findOne({username:req.params.username}, function(err, user){
    if(err) return res.json(err);
    res.render('users/show', {user:user});
  });
});

// edit
router.get('/:username/edit', function(req, res){
  User.findOne({username:req.params.username}, function(err, user){
    if(err) return res.json(err);
    res.render('users/edit', {user:user});
  });
});

// update
router.put('/:username', function(req, res, next){
  User.findOne({username:req.params.username})
    .select('password')
    .exec(function(err, user){
      if(err) return res.json(err);

      // update user object
      user.originalPassword = user.password;
      user.password = req.body.newPassword? req.body.newPassword : user.password; // 2-3
      for(const p in req.body){
        user[p] = req.body[p];
      }

      // save updated user
      user.save(function(err, user){
        if(err) return res.json(err);
        res.redirect('/users/'+user.username);
      });
  });
});

// destroy
router.delete('/:username', function(req, res){
  User.deleteOne({username:req.params.username}, function(err){
    if(err) return res.json(err);
    res.redirect('/users');
  });
});

module.exports = router;

index route에 보면 sort함수가 있다.

.sort({username:1})
username을 기준으로 오름차순(asc)정렬을 하고 있다 (-1을 넣으면 내림차순(desc)이 된다)
sort처럼 다양한 함수가 sort함수에 끼게 되면 callback함수를 exec함수에 인자로 넣어서 사용한다.
 
select함수를 이용하면 DB에서 어떤 항목을 선택할지, 안할지 결정할 수 있다.
User.js에서 password의 select를 false로 설정해서
DB에 password가 있더라도 password를 읽어오지못하는데,
.select({'password'})를 통해 password를 읽어올 수 있게 됐다.

 <select함수 이용> 

- 항목이름 앞에 -를 붙이면 기본적으로 읽어오게 되어 있는 항목을 안 읽어오게 할 수 있다.

- 하나의 select 함수로 여러 항목을 동시에 정할 수도 있다.

 ex) password 읽어오기 & name 안 읽어오기

  ->.select('password - name')

 

 user.password = req.body.newPassword? req.body.newPassword : user.password;

삼항연산자를 이용하여 정보를 update할 때 password를 처리하는 것에 대해 정의하고 있다. 

사용자가 password를 update했으면 이를 user.password에 담고, 아니면 그대로 user.password를 담는다.

req.body는 form에 입력되는 값이다.

 

for(const p in req.body){
   user[p] = req.body[p];
}

· for...in반복문 : 객체의 속성들에 대해 반복

req.body 속성값(username, password, name, email)을 user model 속성값(username, password, name, email)에서 순환을 돌면서 적용한다.

 

 
//index.js
app.use('/users', require('./routes/users'))

index.js에 users route를 추가한다.

 

views/partials/nav.ejs 에 Users와 Sign up메뉴를 추가하였다.

ml-auto클래스를 통해 우측정렬을 시켰다.

nav 실행결과

 

ejs파일들

<!-- views/users/index.ejs -->

<!DOCTYPE html>
<html>
  <head>
    <%- include('../partials/head') %>
  </head>
  <body>
    <%- include('../partials/nav') %>

    <div class="container mb-3">

      <h3 class="mb-3">Users</h3>

      <ul class="list-group">
        <% if(users == null || users.length == 0){ %>
          <li class="list-group-item"> There is no user yet.</li>
        <% } %>
        <% users.forEach(function(user) { %>
          <li class="list-group-item">
            <a href="/users/<%= user.username %>"><%= user.username %></a>
          </li>
        <% }) %>
      </ul>

    </div>
  </body>
</html>
<!-- views/users/new.ejs -->

<!DOCTYPE html>
<html>
  <head>
    <%- include('../partials/head') %>
  </head>
  <body>
    <%- include('../partials/nav') %>

    <div class="container mb-3">

      <h3 class="contentBoxTop mb-3">New User</h3>

      <form action="/users" method="post">

        <div class="form-group row">
          <label for="username" class="col-sm-3 col-form-label">Username*</label>
          <div class="col-sm-9">
            <input type="text" id="username" name="username" value="" class="form-control">
          </div>
        </div>
        <div class="form-group row">
          <label for="name" class="col-sm-3 col-form-label">Name*</label>
          <div class="col-sm-9">
            <input type="text" id="name" name="name" value="" class="form-control">
          </div>
        </div>
        <div class="form-group row">
          <label for="email" class="col-sm-3 col-form-label">Email</label>
          <div class="col-sm-9">
            <input type="text" id="email" name="email" value="" class="form-control">
          </div>
        </div>
        <div class="form-group row">
          <label for="password" class="col-sm-3 col-form-label">Password*</label>
          <div class="col-sm-9">
            <input type="password" id="password" name="password" value="" class="form-control">
          </div>
        </div>
        <div class="form-group row">
          <label for="passwordConfirmation" class="col-sm-3 col-form-label">Password Confirmation*</label>
          <div class="col-sm-9 col-sm-offset-3">
            <input type="password" id="passwordConfirmation" name="passwordConfirmation" value="" class="form-control">
          </div>
        </div>
        <p>
          <small>*Required</small>
        </p>

        <div class="form-group">
          <button type="submit" class="btn btn-primary">Submit</button>
        </div>
      </form>

    </div>
  </body>
</html>
<!-- views/users/edit.ejs -->

<!DOCTYPE html>
<html>
  <head>
    <%- include('../partials/head') %>
  </head>
  <body>
    <%- include('../partials/nav') %>

    <div class="container mb-3">

      <h3 class="mb-3">Edit User</h3>

      <form action="/users/<%= user.username %>?_method=put" method="post">

        <div class="form-group row">
          <label for="currentPassword" class="col-sm-3 col-form-label">Current Password*</label>
          <div class="col-sm-9 col-sm-offset-3">
            <input type="password" id="currentPassword" name="currentPassword" value="" class="form-control">
          </div>
        </div>

        <hr></hr>

        <div class="form-group row">
          <label for="username" class="col-sm-3 col-form-label">Username*</label>
          <div class="col-sm-9">
            <input type="text" id="username" name="username" value="<%= user.username %>" class="form-control">
          </div>
        </div>

        <div class="form-group row">
          <label for="name" class="col-sm-3 col-form-label">Name*</label>
          <div class="col-sm-9">
            <input type="text" id="name" name="name" value="<%= user.name %>" class="form-control">
          </div>
        </div>

        <div class="form-group row">
          <label for="email" class="col-sm-3 col-form-label">Email</label>
          <div class="col-sm-9">
            <input type="text" id="email" name="email" value="<%= user.email %>" class="form-control">
          </div>
        </div>

        <div class="form-group row">
          <label for="newPassword" class="col-sm-3 col-form-label">New Password</label>
          <div class="col-sm-9 col-sm-offset-3">
            <input type="password" id="newPassword" name="newPassword" value="" class="form-control">
          </div>
        </div>

        <div class="form-group row">
          <label for="passwordConfirmation" class="col-sm-3 col-form-label">Password Confirmation</label>
          <div class="col-sm-9 col-sm-offset-3">
            <input type="password" id="passwordConfirmation" name="passwordConfirmation" value="" class="form-control">
          </div>
        </div>

        <p>
          <small>*Required</small>
        </p>

        <div class="buttons">
          <a class="btn btn-primary" href="/users/<%= user.username %>">Back</a>
          <button type="submit" class="btn btn-primary">Submit</button>
        </div>

      </form>

    </div>
  </body>
</html>
<!-- views/users/show.ejs -->

<!DOCTYPE html>
<html>
  <head>
    <%- include('../partials/head') %>
  </head>
  <body>
    <%- include('../partials/nav') %>

    <div class="container mb-3">

      <h3 class="contentBoxTop"><%= user.username %></h3>

      <form class="user-form" action="/users" method="post">
        <fieldset disabled>
          <div class="form-group row">
            <label for="name" class="col-sm-3 col-form-label">Name</label>
            <div class="col-sm-9">
              <input class="form-control" type="text" id="name" name="name" value="<%= user.name %>">
            </div>
          </div>
          <div class="form-group row">
            <label for="email" class="col-sm-3 col-form-label">Email</label>
            <div class="col-sm-9">
              <input class="form-control" type="text" id="email" name="email" value="<%= user.email %>">
            </div>
          </div>
        </fieldset>
      </form>

      <div>
        <a class="btn btn-primary" href="/users">Back</a>
        <a class="btn btn-primary" href="/users/<%= user.username %>/edit">Edit</a>
        <form action="/users/<%= user.username %>?_method=delete" method="post" class="d-inline">
          <a class="btn btn-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a>
        </form>
      </div>

    </div>
  </body>
</html>

 

<실행결과>

가입
index
회원정보 show
회원정보 edit

current password가 현재 DB에 저장된 password와 일치해야 한다.

 

출처 : https://www.a-mean-blog.com/ko/blog/Node-JS-%EC%B2%AB%EA%B1%B8%EC%9D%8C/%EA%B2%8C%EC%8B%9C%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B0/%EA%B2%8C%EC%8B%9C%ED%8C%90-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85

 

Node JS 첫걸음/게시판 만들기: 게시판 - 회원가입 - A MEAN Blog

소스코드 이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실

www.a-mean-blog.com

 

Comments