게시판 만들기 14장 Do it! Node.js 프로그래밍 이지스퍼블리싱 제공 강의 교안 2017/03 ○ 본 강의 자료는 이지스퍼블리싱에서 제공하는 강의 교안입니다. ○ 본 강의 교안은 아래 출판 서적의 내용을 기준으로 구성되었습니다. 또한 다수의 기타 서적이나 사이트를 참조하였습니다. 레퍼런스를 참조하십시오. 2017, 정재곤, “Do it! Node.js 프로그래밍 (개정판)”, 이지스퍼블리싱 - 강의 교안에 사용된 화면 캡처나 실습 자료의 경우에는 문서 업데이트에 따라 변경될 수 있습니다.
강의 주제 및 목차 게시판을 어떻게 만들 수 있는지 알아보자. 강의 주제 목 차 스키마 추가하고 페이지 단위 조회방식 이해하기 목 차 1 스키마 추가하고 페이지 단위 조회방식 이해하기 2 글쓰기와 글 조회 기능 만들기 3 페이지 단위로 글 리스트 조회하기
1. 스키마 추가하고 페이지 단위 조회 방식 이해하기 난이도 소요시간 20분
스키마 파일 만들기 database 폴더 안에 post_schema.js 파일 작성 var SchemaObj = { }; SchemaObj.createSchema = function(mongoose) { // 글 스키마 정의 var PostSchema = mongoose.Schema({ title : {type : String, trim : true, 'default' : ''}, // 글 제목 contents : {type : String, trim :true, 'default' : ''}, // 글 내용 writer : {type : mongoose.Schema.ObjectId, ref : 'users'}, // 글 쓴 사람 tags : {type : [ ], 'default' : ''}, created_at : {type : Date, index : {unique : false}, 'default' : Date.now}, updated_at : {type : Date, index : {unique : false}, 'default' : Date.now}, comments : [{ // 댓글 contents : {type : String, trim : true, 'default' : ''}, // 댓글 내용 writer : {type : mongoose.Schema.ObjectId, ref : 'users'}, created_at : {type : Date, 'default' : Date.now} }] });
스키마 칼럼 정보 게시판을 위한 칼럼 정보 정의 칼럼 이름 설명 title 글 제목 contents 글 내용 writer 글 쓴 사람 글쓴이 정보를 참조할 수 있도록 ObjectId가 저장됨 tags 글에 붙이는 태그 정보 created_at 글을 쓴 일시 updated_at 글을 수정한 일시
칼럼 유효성 확인 제목과 내용 칼럼에 값이 들어가 있는지 확인 // 필수 속성의 'required' validation PostSchema.path('title').required(true, '글 제목을 입력하셔야 합니다.'); PostSchema.path('contents').required(true, '글 내용을 입력하셔야 합니다.');
스키마에 메소드 추가 글 저장, 댓글 추가, 댓글 삭제를 위한 메소드 추가 PostSchema.methods = { savePost : function(callback) { // 글 저장 var self = this; this.validate(function(err) { if(err) return callback(err); self.save(callback); }); }, addComment : function(user, comment, callback) { // 댓글 추가 this.comment.push({ contents : comment.contents, writer : user._id this.save(callback);
스키마에 static 메소드 추가 글 찾기, 글 리스트 등을 위한 메소드 추가 PostSchema.statics = { load : function(id, callback) { this.findOne({_id : id}) .populate('writer', 'id name email') .populate('comments.writer') .exec(callback); }, list : function(options, callback) { var criteria = options.criteria || { }; this.find(criteria) .sort({'created_at' : -1}) .limit(options.perPage) .skip(options.perPage * options.page) }
칼럼에 지정된 객체 다른 컬렉션을 참조하는 속성에는 populate() 메소드로 값을 찾아 넣어줌
페이지 단위로 조회하기 일부 데이터를 페이지 단위로 조회할 수 있어야 함
페이지 단위로 조회할 때 데이터를 결정하는 방식 몇 가지 변수 값을 이용해 계산
2. 글쓰기와 글 조회 기능 만들기 난이도 소요시간 30분
글쓰기를 위한 라우팅 함수 추가 글쓰기를 위한 addpost 함수 추가 var addpost = function(req, res) { console.log('post 모듈 안에 있는 addpost 호출됨.'); var paramTitle = req.body.title || req.query.title; var paramContents = req.body.contents || req.query.contents; var paramWriter = req.body.writer || req.query.writer; console.log('요청 파라미터 : ' + paramTitle + ', ' + paramContents + ', ' + paramWriter); var database = req.app.get('database'); if(database.db) { // 1. 아이디를 사용해 사용자 검색 database.UserModel.findById(paramWriter, function(err, results) { if(err) {throw err;} if(results == undefined || results.length < 1) { res.writeHead('200', {'Content-Type' : 'text/html;charset=utf8'}); res.write('<h2>사용자 [' + paramWriter + ']를 찾을 수 없습니다.</h2>'); res.end(); return; }
글쓰기를 위한 라우팅 함수 추가 글쓰기를 위한 addpost 함수 추가 var userObjectId = results[0]._doc._id; console.log('사용자 ObjectId : ' + paramWriter +' -> ' + userObjectId); // save( )로 저장 var post = new database.PostModel({ title : paramTitle, contents : paramContents, writer : userObjectId }); post.savePost(function(err, result) { if(err) { ….. } console.log("글 데이터 추가함."); console.log('글 작성', '포스팅 글을 생성했습니다. : ' + post._id); return res.redirect('/process/showpost/' + post._id);
글조회를 위한 라우팅 함수 추가 글조회를 위한 showpost 함수 추가 var showpost = function(req, res) { console.log('post 모듈 안에 있는 showpost 호출됨.'); var paramId = req.body.id || req.query.id || req.params.id; var database = req.app.get('database'); if(database.db) { // 1. 글 조회 database.PostModel.load(paramId, function(err, results) { if(err) {throw err;} if(results) { console.dir(results); res.writeHead('200', {'Content-Type' : 'text/html;charset=utf8'});
글조회를 위한 라우팅 함수 추가 글조회를 위한 showpost 함수 추가 // 뷰 템플릿을 사용하여 렌더링한 후 전송 var context = { title : '글 조회 ', posts : results, Entities : Entities }; req.app.render('showpost', context, function(err, html) { if(err) {throw err;} console.log('응답 웹 문서 : ' + html); res.end(html); });
글 조회를 위한 템플릿 작성 글 조회에 사용될 showpost.ejs 템플릿 작성 <div class = "ui blue fluid card"> <div class = "content"> <% var curTitle = posts._doc.title; var curContents = posts._doc.contents; var curWriter = posts._doc.writer.id; // html-entities module is required in post.js var entities = new Entities(); var decodedContents = entities.decode(curContents); %> <div id = "titleOutput" class = "header"><% = curTitle %></div> …….
글 쓰기를 위한 웹문서 작성 글 쓰기에 사용될 addpost.html 파일 작성 <script src = "/public/jquery-2.1.4.min.js"></script> <script src = "/public/semantic.min.js"></script> <script src = "/public/ckeditor/ckeditor.js"></script> <script> $(document).ready(function() { CKEDITOR.config.extraPlugins = 'colorbutton'; CKEDITOR.env.isCompatible = true; CKEDITOR.replace('contents'); }); </script> ……. <div class = "field"> <label>내용</label> <textarea id = "contents" name = "contents" rows = "10" cols = "80"></textarea> </div>
글쓰기 기능 확인 글을 쓴 후 그 결과 확인
3. 페이지 단위로 글 목록 조회하기 난이도 소요시간 30분
글 목록 조회 기능을 만드는 순서 네 단계로 구성됨 (1) 라우팅 함수 만들기 (2) config.js 파일에 라우팅 함수 등록하기 (3) 응답 웹 문서를 구성할 뷰 템플릿 만들기 (4) 글 목록 조회를 요청하는 웹 페이지 만들기 database.PostModel.list(options, function(err, results) { if(err) { ….. } if(results) { console.dir(results); database.PostModel.count().exec(function(err, count) { res.writeHead('200', {'Content-Type' : 'text/html;charset=utf8'}); var context = { title : '글 목록', posts : results, page : parseInt(paramPage), pageCount : Math.ceil(count / paramPerPage), perPage : paramPerPage, totalRecords : count, size : paramPerPage }; req.app.render('listpost', context, function(err, html) { res.end(html); });
라우팅 함수 등록하기 라우팅 함수를 config.js 파일에 등록 module.exports = { ……. route_info : [ ,{file : './post', path : '/process/listpost', method : 'listpost', type : 'post'} ,{file : './post', path : '/process/listpost', method : 'listpost', type : 'get'} ],
응답 문서를 위한 뷰 템플릿 만들기 글 리스트를 위한 뷰 템플릿 작성 <div class = "ui grid"> <div class = "two wide column">번호</div> <div class = "eight wide column">제목</div> <div class = "two wide column">작성자</div> <div class = "two wide column">작성일</div> <div class = "two wide column">조회수</div> </div> <div class = "ui very relaxed selection celled list"> <% var noStart = (pageCount - page) * perPage; for(var i = 0; i < posts.length; i++) { var curTitle = posts[i]._doc.title; var curContents = posts[i]._doc.contents; var curWriter = posts[i]._doc.writer.email; var curNo = noStart - i; %>
페이지 선택 버튼 추가 뷰 템플릿에 페이지 선택 버튼 추가 <div class = "tiny ui basic buttons"> <% if(page > 0) { %> <div class = "ui icon button" onclick = "listpost(0, <% = perPage %>)"> <i class = "fast backward icon"></i> </div> <div class = "ui icon button" onclick = "listpost(<% = (page - 1) %>, <% = perPage %>)"> <i class = "left chevron icon"></i> } else { <div class = "ui disabled icon button">
웹 페이지 작성 글 목록을 요청하는 페이지 작성 <form method = "post" action = "/process/listpost"> <table> <tr> <td><label>아래 [전송] 버튼을 누르세요.</label></td> </tr> </table> <input type = "hidden" name = "page" value = "0"/> <input type = "hidden" name = "perPage" value = "2"/> <input type = "submit" value = "전송" name = ""/> </form> …….
글 목록 기능 확인 페이지 번호 등이 정상적으로 표시되는지 확인 ▶ http://localhost:3000/public/listpost.html
참고 문헌 [ References] 기본 서적 2017, 정재곤, “Do it! Node.js 프로그래밍 (개정판)”, 이지스퍼블리싱 Node.js Site http://nodejs.org/