리스트 및 뷰 페이지
추가 모듈 설치
markdown-it (마크다운 파서)
npm install --save markdown-it
컨트롤러 및 모델 등록
app/routes/posts.js
실습2 이후 나머지 부분들 까지 라우터 모두 작성함
const express = require('express'); const router = express.Router(); const postsController = require('../controllers/postsController'); // list router.get('/', postsController.getList); // New Post Form router.get('/new', postsController.getPostForm); // New Post Process router.post('/new', postsController.insertProcess); // View Post router.get('/:id', postsController.getView); // Edit Post Form - 글쓰기 폼을 공유해서 사용함 router.get('/:id/edit', postsController.getEditForm); // Edit Post Process router.put('/:id', postsController.updateProcess); // Delete Post Process router.delete('/:id', postsController.deleteProcess); module.exports = router;
app/controllers/postsController.js 현재까지의 소스
const postsModel = require('../models/postsModel'); const MarkdownIt = require('markdown-it')({ html: true, linkify: true, typographer: true, }); // 레퍼런스를 보고 마크다운에 HTML, link, 이미지등을 모두 사용한다로 설정 /** * 리스트 * @param req * @param res */ exports.getList = (req, res) => { postsModel.getList((result) => { if (result) { // console.log(result); res.render('posts/list', { title: '게시판 리스트', posts: result }); } else { res.redirect('/'); } }); }; /** * 글 작성 - 폼 * * @param req * @param res */ exports.getPostForm = (req, res) => { res.render('posts/writeForm', { 'title': '글 작성하기' }); }; /** * 글 입력 - 프로세스 * ip 찾기 - https://wedul.site/520 * * @param req * @param res */ exports.insertProcess = (req, res) => { let item = { 'name': req.body.name, 'email': req.body.email, 'password': req.body.password, 'subject': req.body.subject, 'content': req.body.content, 'ip': req.headers['x-forwarded-for'] || req.connection.remoteAddress, 'tags': req.body.tags }; postsModel.insertData(item, (result) => { if (result) { // console.log(result); if (result.affectedRows === 1) { res.redirect('/posts'); } else { res.redirect('/posts/new'); } } }); }; /** * 글 읽기 * * @param req * @param res */ exports.getView = (req, res) => { let id = req.params.id; postsModel.getView(id, (result) => { if (result) { // let md = new MarkdownIt(); result.content = MarkdownIt.render(result.content); res.render('posts/view', { title: result.subject, post: result }); } }); };
app/models/postsModel.js 현재까지의 소스
// mysql 연결 const mysqlConnObj = require('../config/mysql'); const mysqlConn = mysqlConnObj.init(); // mysqlConnObj.open(mysqlConn); // 정상적으로 연결되었는지 확인 const bcrypt = require('bcrypt'); const saltRound = 10; /** * 게시글 리스트 * * * cb : Callback function. After completing select, it returns to the controller * * @param cb */ exports.getList = (cb) => { /** * todo : 페이징 처리 * @type {string} */ let sql = 'SELECT * FROM posts ORDER BY id DESC LIMIT 10'; mysqlConn.query(sql, (err, results, fields) => { if (err) { console.error('Error code : ' + err.code); console.error('Error Message : ' + err.message); throw new Error(err); } else { cb(JSON.parse(JSON.stringify(results))); } }); }; /** * 글 보기 * * 하나의 결과값만 리턴 할 경우 자체가 JSON 형식이라 따로 JSON.parse 안해줘도 됨 * * id : 게시물 번호 * cd : 콜백 함수 * * @param id * @param cb */ exports.getView = (id, cb) => { let sql = 'SELECT `id`, `name`, `email`, `subject`, `content`, `like`, `hate`, `hit`, `comment_cnt`, inet_ntoa(`ip`) AS `ip`, `created_at`, `updated_at` FROM posts WHERE id=? LIMIT 1'; mysqlConn.query(sql, [id], (err, results, fields) => { if (err) { console.error('Error code : ' + err.code); console.error('Error Message : ' + err.message); throw new Error(err); } else { cb(results[0]); } }); }; /** * 새로운 글을 작성하면 데이터베이스에 입력한다. * * data : Input data received from the controller * cb : Callback function. After completing input, it returns to the controller * * @param data * @param cb */ exports.insertData = (data, cb) => { bcrypt.genSalt(saltRound, (err, salt) => { if (err) throw new Error(err); bcrypt.hash(data.password, salt, (err, hash) => { if (err) throw new Error(err); // 입력 구문 let now = new Date(); let sql = 'INSERT INTO posts (name, email, password, subject, content, ip, created_at) VALUES (?, ?, ?, ?, ?, inet_aton(?), ?)'; let bindParam = [ data.name, data.email, hash, // 해싱된 비밀번호 data.subject, data.content, data.ip, now ]; // 참고사이트 // https://www.npmjs.com/package/mysql // https://github.com/gnipbao/express-mvc-framework/blob/master/controllers/task.js // https://github.com/gnipbao/express-mvc-framework/blob/master/services/task.js mysqlConn.query(sql, bindParam, (err, results, fields) => { if (err) { console.error('Error code : ' + err.code); console.error('Error Message : ' + err.message); throw new Error(err); } else { cb(JSON.parse(JSON.stringify(results))); } }); /** * 위 코드에서 result 의 값으로 넘어오는 것들 * * fieldCount: 0, * affectedRows: 1, // 성공한 개수 * insertId: 2, * serverStatus: 2, * warningCount: 0, * message: '', * protocol41: true, * changedRows: 0 */ }); }); };
디자인 페이지 - 현재까지의 소스(CSS 제외)
개인적으로 디자인의 디자도 모름...😂 그냥 다른 오픈소스 디자인 사이트들을 참고하고 웹서핑하다 괜찮은 사이트의 디자인을 보고 따라 끄적여 봤음😖
CSS는 공부가 끝나면 깃헙에 PUSH 하면서 같이 올릴 예정임🎃
app/views/layouts/main_layout.pug
doctype html html(lang='ko') head meta(charset='utf-8') meta(name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=3.0') title= title link(rel='shortcut icon', href='favicon.ico', type='image/x-icon') // 폰트 link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Noto+Sans+KR:500&display=swap&subset=korean') link(rel='stylesheet', href='//fonts.googleapis.com/css?family=East+Sea+Dokdo&display=swap') link(rel='stylesheet', href='//cdn.jsdelivr.net/npm/hack-font@3.3.0/build/web/hack.css') // 기본 CSS link(rel='stylesheet', href='/stylesheets/style.css') link(rel='stylesheet', href='/stylesheets/button.css') link(rel='stylesheet', href='/stylesheets/form.css') link(rel='stylesheet', href='/stylesheets/list.css') script(src='//kit.fontawesome.com/c14d6e5016.js', crossorigin='anonymous') body#wrapper.theme-light block header include ../partials/header block content
app/views/index.pug
extends layouts/main_layout block content h1= title p.code Welcome to #{title}
app/views/partials/header.pug
header#header nav ul li a(href='/') Home li a(href='/posts') Post
app/posts/list.pug
extends ../layouts/main_layout block content div#container div.board //div#searchDiv // form(action='/posts' method='get') // input(type='hidden' name='page') // input.search-input(type='text' placeholder='Search..' name='search') div.rgt button.btn.green.pointer(type='button' onclick='location.href="/posts/new"') 글쓰기 ul li 게시판 제목 li ul.list for post, index in posts li ul li.ranking.lft div.like 추천 #{post.like} div.hate 반대 #{post.hate} div.comment 댓글 #{post.comment_cnt} div.hit 조회 #{post.hit} li.content.lft - let id = post.id; div.title(onclick="location.href='/posts/"+id+"'") #{post.subject} div.summary.ellipsis(onclick="location.href='/posts/"+id+"'") #{post.content} div.tag #태그 #태그 li.author div 글쓴이 div #{post.name} li.date div 작성일 div #{post.created_at} div.ce div#pagingDiv ul li a(href='#') « a(href='#') 1 a(href='#') 2 a.active(href='#') 3 a(href='#') 4 a(href='#') 5 a(href='#') » div.rgt button.btn.green.pointer(type='button' onclick='location.href="/posts/new"') 글쓰기
app/posts/view.pug
extends ../layouts/main_layout block content link(rel='stylesheet', href='//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/styles/an-old-hope.min.css') script(src='//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/highlight.min.js') script hljs.initHighlightingOnLoad(); link(rel='stylesheet', href='/stylesheets/md/modest.css') div#container div.row div.side-f div.main div h1 #{post.subject} div#markdown !{post.content} div button.btn.blue.pointer(type='button', onclick='location.href="/posts/' + post.id + '/edit"') 수정 div 댓글 창 나올 곳 div.side-r
app/views/writeForm.pug
extends ../layouts/main_layout block content if mode === 'edit' - var tmp_title = '글 수정하기' - var tmp_action = '/posts/' + post.id + '?_method=PUT' // 필수 else - var tmp_title = '글 작성하기' - var tmp_action = '/posts/new' h1= tmp_title div.ce form(action=tmp_action method='post') if mode === 'edit' //input(type='hidden' name='_method' value='PUT') // 있어도 되고 없어도 되고.. input(type='hidden' name='id' value='' + post.id + '') div.line40 div.post_title label textarea.textarea_title(name='subject' id='form_subject' placeholder='제목을 입력하세요.' tabindex='1') #{post.subject} div label textarea.textarea(name='content' id='form_content' tabindex='2') #{post.content} div label input.input.strong(type='text' name='tags' id='form_tags' value='' placeholder='태그1,태그2,태그3' tabindex='3') div label input.input(type='text' name='name' id='form_name' value='' + post.name + '' placeholder='작성자 이름' tabindex='4') div label input.input(type='email' name='email' id='form_email' value='' + post.email + '' placeholder='작성자 메일주소' tabindex='5') div label input.input(type='password' name='password' id='form_passworrd' placeholder='비밀번호 입력' tabindex='6') div.line20 div.ce span.m5 button.btn.green.pointer(type='submit' tabindex='7') 저 장 span.m5 button.btn.gray.pointer(type='button' tabindex='8') 취 소
'TIL > NodeJS' 카테고리의 다른 글
[JavaScript]자료형(Data type) (0) | 2020.03.17 |
---|---|
[실습]NodeJS + EXPRESS + MySQL 을 이용한 게시판 만들기 4(MVC) (0) | 2020.03.15 |
[실습]NodeJS + EXPRESS + MySQL 을 이용한 게시판 만들기 2(MVC) (0) | 2020.03.14 |
[실습]NodeJS + EXPRESS + MySQL 을 이용한 게시판 만들기 1(MVC) (0) | 2020.03.13 |
실행 중간에 프로세스 멈추기 (0) | 2020.03.09 |