이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.
이 게시물의 소스코드는 게시판 만들기(2016) / 게시판 - Login 기능 추가에서 이어집니다.
board.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.
board.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 board.git 을 clone 합니다.
- Github에서 소스코드 보기: https://github.com/a-mean-blogger/board/tree/65fef89ee675ddd26669c835bf5b409ae821cfe5
/* * 이 강의는 2020년 버전으로 update되었습니다. -> 2020년 버전 보기 */
게시판 - User Error 처리에서처럼 post의 error를 처리해 봅시다.
또한 routes/user.js 에서 사용했던 parseError함수를 post에도 그대로 사용할텐데, 이처럼 여러 파일에서 사용하게 될 함수들을 하나의 module로 분리하겠습니다.
주황색은 변경된 파일, 초록색은 추가된 파일, 회색은 변화가 없는 파일입니다.
// util.js var util = {}; util.parseError = function(errors){ var parsed = {}; if(errors.name == 'ValidationError'){ for(var name in errors.errors){ var 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; } util.getDate = function(dateObj){ if(dateObj instanceof Date) return dateObj.getFullYear() + "-" + get2digits(dateObj.getMonth()+1)+ "-" + get2digits(dateObj.getDate()); } util.getTime = function(dateObj){ if(dateObj instanceof Date) return get2digits(dateObj.getHours()) + ":" + get2digits(dateObj.getMinutes())+ ":" + get2digits(dateObj.getSeconds()); } module.exports = util; // private functions function get2digits (num){ return ("0" + num).slice(-2); }
원래 routes/users.js에 있던parseError
와 model/Post.js에 있던 시간관련 함수들을 module로 만들어 util.js로 분리했습니다.
// routes/users.js //... var util = require("../util"); // 1 // Index ... // New ... // 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)); // 1 return res.redirect("/users/new"); } res.redirect("/users"); }); }); // show ... // edit ... // update router.put("/:username",function(req, res, next){ User.findOne({username:req.params.username}) .select({password:1}) .exec(function(err, user){ if(err) return res.json(err); // update user object ... // save updated user user.save(function(err, user){ if(err){ req.flash("user", req.body); req.flash("errors", util.parseError(err)); // 1 return res.redirect("/users/"+req.params.username+"/edit"); } res.redirect("/users/"+req.params.username); }); }); }); //...
1. util
이require
됬고, 기존에parseError
가 util.parseError
로 바뀌었습니다.
// model/Post.js var mongoose = require("mongoose"); var util = require("../util"); // 1 // schema var postSchema = mongoose.Schema({ title:{type:String, required:[true,"Title is required!"]}, // 2 body:{type:String, required:[true,"Body is required!"]}, // 2 createdAt:{type:Date, default:Date.now}, updatedAt:{type:Date}, },{ toObject:{virtuals:true} }); // virtuals postSchema.virtual("createdDate") .get(function(){ return util.getDate(this.createdAt); // 1 }); postSchema.virtual("createdTime") .get(function(){ return util.getTime(this.createdAt); // 1 }); postSchema.virtual("updatedDate") .get(function(){ return util.getDate(this.updatedAt); // 1 }); postSchema.virtual("updatedTime") .get(function(){ return util.getTime(this.updatedAt); // 1 }); // model & export var Post = mongoose.model("post", postSchema); module.exports = Post;
1. 기존에 있던 시간 관련 함수들이 지워졌고util
이require
된 후util
로 부터 각 함수들을 호출합니다.
2. validation과 에러메세지들이 추가되었습니다.
// routes/posts.js // ... var util = require("../util"); // Index ... // New router.get("/new", function(req, res){ var post = req.flash("post")[0] || {}; var 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 ... // edit router.get("/:id/edit", function(req, res){ var post = req.flash("post")[0]; var errors = req.flash("errors")[0] || {}; if(!post){ Post.findOne({_id:req.params.id}, function(err, post){ if(err) return res.json(err); res.render("posts/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 ... module.exports = router;
게시판 - User Error 처리에서 변경된 것과 동일하게 변경되었습니다. 상세한 설명은 생략합니다.
한가지 눈여겨 볼 점은 update의 Post.findOneAndUpdate
에 {runValidators:true}
이 추가된 것인데요, findOneAndUpdate
는 기본설정이 schema에 있는 validation을 작동하지 않도록 되어 있기때문에 이 option을 통해서 validation이 작동하도록 설정해 주어야 합니다.
ejs 역시 게시판 - User Error 처리와 동일하게 변경되었기 때문에 설명은 생략합니다.
<!-- views/posts/edit.ejs --> <!DOCTYPE html> <html> <head> <% include ../partials/head %> </head> <body> <% include ../partials/nav %> <div class="container post post-edit"> <% include ./partials/header %> <div class="buttons"> <a class="btn btn-default" href="/posts">Back</a> </div> <form class="post-form form-horizontal" action="/posts/<%= post._id %>?_method=put" method="post"> <div class="contentBox"> <h3 class="contentBoxTop">Edit Post</h3> <fieldset> <div class="form-group <%= (errors.title)?'has-error':'' %>"> <label for="title" class="col-sm-2 control-label">Title</label> <div class="col-sm-10"> <input class="form-control" type="text" id="title" name="title" value="<%= post.title %>"> <% if(errors.title){ %> <span class="help-block"><%= errors.title.message %></span> <% } %> </div> </div> <div class="form-group <%= (errors.body)?'has-error':'' %>"> <label for="body" class="col-sm-2 control-label">Body</label> <div class="col-sm-10"> <textarea class="form-control" id="body" name="body" rows="5"><%= post.body %></textarea> <% if(errors.body){ %> <span class="help-block"><%= errors.body.message %></span> <% } %> </div> </div> </fieldset> </div> <div class="buttons"> <button type="submit" class="btn btn-default">Submit</button> </div> <% if(errors.unhandled){ %> <div class="alert alert-danger"> <%= errors.unhandled %> </div> <% } %> </form> </div> <!-- container end --> </body> </html>
<!-- views/posts/new.ejs --> <!DOCTYPE html> <html> <head <% include ../partials/head %> </head> <body> <% include ../partials/nav %> <div class="container post post-new"> <% include ./partials/header %> <div class="buttons"> <a class="btn btn-default" href="/posts">Back</a> </div> <form class="post-form form-horizontal" action="/posts" method="post"> <div class="contentBox"> <h3 class="contentBoxTop">New Post</h3> <fieldset> <div class="form-group <%= (errors.title)?'has-error':'' %>"> <label for="title" class="col-sm-2 control-label">Title</label> <div class="col-sm-10"> <input class="form-control" type="text" id="title" name="title" value="<%= post.title %>"> <% if(errors.title){ %> <span class="help-block"><%= errors.title.message %></span> <% } %> </div> </div> <div class="form-group <%= (errors.body)?'has-error':'' %>"> <label for="body" class="col-sm-2 control-label">Body</label> <div class="col-sm-10"> <textarea class="form-control" id="body" name="body" rows="5"><%= post.body %></textarea> <% if(errors.body){ %> <span class="help-block"><%= errors.body.message %></span> <% } %> </div> </div> </fieldset> </div> <div class="buttons"> <button type="submit" class="btn btn-default">Submit</button> </div> <% if(errors.unhandled){ %> <div class="alert alert-danger"> <%= errors.unhandled %> </div> <% } %> </form> </div> <!-- container end --> </body> </html>
원래 post error 처리는 숙제로만 남기고 강의로 만들지 않을려고 했는데, 하지 않고 넘어가면 뒤에 이어질 강좌에서 코드가 지저분해 지게 되어서 강의로 추가하게 되었습니다.
다음은 post와 user 간의 관계 설정입니다.
댓글
이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기