보안 전공생의 공부

게시판 만들기 - post error 본문

WEB/Node

게시판 만들기 - post error

수잉 2021. 12. 19. 16:10

routes/userjs에서 사용한 parseError함수를 post에서도 사용하여 post의 error를 처리한다.

-> 여러 파일에서 사용하게 될 함수들을 하나의 module로 분리

 

// util.js

const util = {};

util.parseError = function(errors){
  const parsed = {};
  if(errors.name == 'ValidationError'){
    for(const name in errors.errors){
      const validationError = errors.errors[name];
      parsed[name] = { message:validationError.message };
    }
  } 
  else if(errors.code == '11000' && errors.errmsg.indexOf('username') > 0) {
    parsed.username = { message:'This username already exists!' };
  } 
  else {
    parsed.unhandled = JSON.stringify(errors);
  }
  return parsed;
}

module.exports = util;

routes/users.js에 있던 parseError함수를 module로 만들어 util.js파일로 분리한다.

util.js <- 여러 곳에서 공통으로 쓰게 될 함수들을 기록

 

 

· user.js 수정

// routes/users.js

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

// 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){
  const user = req.flash('user')[0] || {};
  const errors = req.flash('errors')[0] || {};
  res.render('users/new', { user:user, errors:errors });
});

// create
router.post('/', function(req, res){
  User.create(req.body, function(err, user){
    if(err){
      req.flash('user', req.body);
      req.flash('errors', util.parseError(err));
      return res.redirect('/users/new');
    }
    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){
  const user = req.flash('user')[0];
  const errors = req.flash('errors')[0] || {};
  if(!user){
    User.findOne({username:req.params.username}, function(err, user){
      if(err) return res.json(err);
      res.render('users/edit', { username:req.params.username, user:user, errors:errors });
    });
  }
  else {
    res.render('users/edit', { username:req.params.username, user:user, errors:errors });
  }
});

// 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;
      for(var p in req.body){
        user[p] = req.body[p];
      }

      // save updated user
      user.save(function(err, user){
        if(err){
          req.flash('user', req.body);
          req.flash('errors', util.parseError(err));
          return res.redirect('/users/'+req.params.username+'/edit');
        }
        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;

// functions
function parseError(errors){
  const parsed = {};
  if(errors.name == 'ValidationError'){
    for(const name in errors.errors){
      const validationError = errors.errors[name];
      parsed[name] = { message:validationError.message };
    }
  }
  else if(errors.code == '11000' && errors.errmsg.indexOf('username') > 0) {
    parsed.username = { message:'This username already exists!' };
  }
  else {
    parsed.unhandled = JSON.stringify(errors);
  }
  return parsed;
}

const util=require('../util');

util을 require했다.

 

// create

req.flash('errors', util.parseError(err));

// update // save updated user

req.flash('errors', util.parseError(err));

parseError -> util.parseError로 변경했다.

 

 

· Post.js 수정

//models/Post.js
const mongoose = require('mongoose');
const postSchema = mongoose.Schema({
  title:{type:String, required:[true,'Title is required']},
  body:{type:String, required:[true,'Body is required']},
  createdAt:{type:Date, default:Date.now},
  updatedAt:{type:Date},
});

const Post = mongoose.model('post', postSchema);
module.exports = Post;

post schema의 custom error message를 추가한다.

 

 

· posts.js 수정

2021.12.01 - [개발/node.js] - 게시판 만들기 / User 생성, 수정 시 error 처리 (1)와 처리방식이 유사하다.

 

//routes/posts.js

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

// Index 
router.get('/', function(req, res){
  Post.find({})
  .sort('-createdAt')
  .exec(function(err, posts){
    if(err) return res.json(err);
    res.render('posts/index', {posts:posts});
  });
});

// New
router.get('/new', function(req, res){
  const post=req.flash('post')[0]||{};
  const errors=req.flash('errors')[0]||{};
  res.render('posts/new',{post:post, errors:errors});
});

// create
router.post('/', function(req, res){
  Post.create(req.body, function(err, post){
    if(err){
      req.flash('post',req.body);
      req.flash('errors',util.parseError(err));
      return res.redirect('/posts/new');
    }
    res.redirect('/posts');
  });
});

// show
router.get('/:id', function(req, res){
  Post.findOne({_id:req.params.id}, function(err, post){
    if(err) return res.json(err);
    res.render('posts/show', {post:post});
  });
});

// edit
router.get('/:id/edit', function(req, res){
  const post=req.flash('post')[0];
  const errors=req.flash('errors')[0]||{};
  if(!post){
    Post.findOne({_id:req.params.id},function(err,post){
      if(err) return res.json(err);
      res.render('post/edit',{post:post, errors:errors});
    })
  }
  else{
    post._id=req.params.id;
    res.render('posts/edit',{post:post, errors:errors});
  }
});

// update
router.put('/:id', function(req, res){
  req.body.updatedAt = Date.now(); 
  Post.findOneAndUpdate({_id:req.params.id}, req.body, {runValidators:true}, function(err, post){
    if(err) {
      req.flash('post',req.body)
      req.flash('errors',util.parseError(err))
      return res.redirect('/posts'+req.params.id+'/edit')
    }
    res.redirect("/posts/"+req.params.id);
  });
});

// destroy
router.delete('/:id', function(req, res){
  Post.deleteOne({_id:req.params.id}, function(err){
    if(err) return res.json(err);
    res.redirect('/posts');
  });
});

module.exports = router;

const util=require('../util');

util을 require했다.

 

new route

post 생성시 에러가 있는 경우 new페이지에 에러와 기존에 입력했던 값들을 보여준다.

(이때, 이 값들은 create route에서 생성된 flash로부터 받아옴)

 

flash는 배열이 오는데 이 프로그램에서는 하나 이상의 값이 저장되는 경우가 없고 , 

있더라도 오류이므로 무조건 [0]의 값을 읽어오게 했다.

값이 없으면(처음 new페이지에 들어온 경우)에는 | | { }를 사용해 빈 오브젝트를 넣어 posts/new페이지 생성한다.

 

더보기

req.flash(문자열, 저장할 값)형태로 저장할 값(숫자, 문자열, 오브젝트 등 가능)을 해당 문자열에 저장한다.

이때 배열로 저장되기 때문에 같은 문자열을 중복해서 사용하면 순서대로 배열에 저장된다.

req.flash(문자열)인 경우 - 해당 문자열에 저장된 값들을 배열로 불러온다. 

<- 저장된 값이 없으면 빈 배열([ ])을 return

create route

post 생성시 오류가 있으면 post, error flash를 만들고 new페이지로 redirect한다.

post 생성시 발생하는 오류는

 

update route

Post.findOneAndUpdate에 {runValidators:true}가 기존 코드에서 추가되었다.

findOneAndUpdate는 기본설정이 schema에 있는 validation이 작동하지 않도록 되어있어 이 option을 통해

validation이 작동하도록 설정해줘야 한다.

 

 

· edit.ejs & new.ejs 수정

<!-- views/posts/edit.ejs -->

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

    <div class="container mb-3">

      <nav aria-label="breadcrumb">
        <ol class="breadcrumb p-1 pl-2 pr-2">
          <li class="breadcrumb-item"><a href="/">Home</a></li>
          <li class="breadcrumb-item"><a href="/posts">Board</a></li>
          <li class="breadcrumb-item"><a href="/posts/<%= post._id %>"><%= post.title %></a></li>
          <li class="breadcrumb-item active" aria-current="page">Edit Post</li>
        </ol>
      </nav>

      <form action="/posts/<%= post._id %>?_method=put" method="post">

        <div class="form-group">
          <label for="title">Title</label>
          <input type="text" id="title" name="title" value="<%= post.title %>" class="form-control <%= (errors.title)?'is-invalid':'' %>">
          <% if(errors.title){ %>
            <span class="invalid-feedback"><%= errors.title.message %></span>
          <% } %>
        </div>

        <div class="form-group">
          <label for="body">Body</label>
           <textarea id="body" name="body" rows="5" class="form-control <%= (errors.body)?'is-invalid':'' %>"><%= post.body %></textarea>
          <% if(errors.body){ %>
            <span class="invalid-feedback"><%= errors.body.message %></span>
          <% } %>
        </div>

        <% if(errors.unhandled){ %>
          <div class="invalid-feedback d-block">
            <%= errors.unhandled %>
          </div>
        <% } %>
        
        <div>
          <a class="btn btn-primary" href="/posts/<%= post._id %>">Back</a>
          <button type="submit" class="btn btn-primary">Submit</button>
        </div>
      </form>

    </div>
  </body>
</html>
 
 <%= (errors.항목이름)?'is-invalid':'' %>

각 항목의 form-group class가 있는 div에 위의 코드를 추가하였다.

<% if(errors.title){ %>
            <span class="invalid-feedback"><%= errors.title.message %></span>
          <% } %>

만약 error가 있다면 bootstrap의 invalid-feedback class를 사용한다.

 

 <% if(errors.unhandled){ %>
          <div class="alert alert-danger">
            <%= errors.unhandled %>
          </div>
        <% } %>

error.unhandled는 따로 표시한다.

<!-- views/posts/new.ejs -->

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

    <div class="container mb-3">

      <nav aria-label="breadcrumb"> 
        <ol class="breadcrumb p-1 pl-2 pr-2">
          <li class="breadcrumb-item"><a href="/">Home</a></li>
          <li class="breadcrumb-item"><a href="/posts">Board</a></li>
          <li class="breadcrumb-item active" aria-current="page">New Post</li>
        </ol>
      </nav>

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

        <div class="form-group">
          <label for="title">Title</label>
          <input type="text" id="title" name="title" value="<%= post.title %>" class="form-control <%= (errors.title)?'is-invalid':'' %>">
          <% if(errors.title){ %>
            <span class="invalid-feedback"><%= errors.title.message %></span>
          <% } %>
        </div>
        
        <div class="form-group">
          <label for="body">Body</label>
          <textarea id="body" name="body" rows="5" class="form-control <%= (errors.body)?'is-invalid':'' %>"><%= post.body %></textarea>
          <% if(errors.body){ %>
            <span class="invalid-feedback"><%= errors.body.message %></span>
          <% } %>
        </div>

        <% if(errors.unhandled){ %>
          <div class="invalid-feedback d-block">
            <%= errors.unhandled %>
          </div>
        <% } %>

        <div>
          <a class="btn btn-primary" href="/posts">Back</a>
          <button type="submit" class="btn btn-primary">Submit</button>
        </div>

      </form>

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

위의 edit.ejs와 같다.

 

<실행결과>

post 작성시 Title과 Body를 작성하지 않고 Submit하면 이렇게 error message가 뜬다.

출처 및 참조 : 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-Post-Error-%EC%B2%98%EB%A6%AC

 

Node JS 첫걸음/게시판 만들기: 게시판 - Post Error 처리 - A MEAN Blog

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

www.a-mean-blog.com

 

Comments