NodeJS
MySQL
을 이용해 로그인 기반 게시판을 구현했다.- 게시글 목록까지는 로그인 없이도 접근 가능하지만 게시글을 읽거나 쓰려면 로그인이 필요하다.
MongoDB
사용, 예전 게시글에 구현 설명 해놨습니다.#1
게시글에서는 게시글 작성까지만 설명하겠습니다.
🔽
server.js
🔽
const express = require('express');
const app = express();
app.use(express.static(__dirname + ''));
// importing body-parser to create bodyParser object
const bodyParser = require('body-parser');
// allows you to use req.body var when you use http post method.
app.use(bodyParser.urlencoded({ extended: true }));
// Cookies.
const cookieParser = require('cookie-parser');
app.use(cookieParser());
// allows you to ejs view engine.
app.set('view engine', 'ejs');
// Socket.
const connectSocket = require('./controllers/chat/connectSocket');
// MongoDB.
const { connectMongoDB } = require('./models/connectMongoDB');
// Routers.
const homeRouter = require('./routes/homeRouter');
const game2048Router = require('./routes/2048Router');
const gameTetrisRouter = require('./routes/tetrisRouter');
const userRouter = require('./routes/userRouter');
const chatRouter = require('./routes/chatRouter');
const boardRouter = require('./routes/boardRouter');
const port = 80;
const server = app.listen(port, function() {
console.log('Listening on '+port);
});
connectSocket(server);
connectMongoDB();
app.use('/', homeRouter);
app.use('/2048', game2048Router);
app.use('/tetris', gameTetrisRouter);
app.use('/user', userRouter);
app.use('/chat', chatRouter);
app.use('/board', boardRouter);
🔽
boardRouter.js
🔽
// boardRouter.js
const express = require("express");
const router = express.Router();
// Importing controller
const boardMiddleWare = require('../controllers/board/board.controller');
const auth = require("../controllers/authMiddleware");
// path: /board/
router.use('/', auth);
router.get('/', boardMiddleWare.showMain);
router.get('/', boardMiddleWare.searchByTitle);
router.get('/write', boardMiddleWare.boardWrite);
router.get('/:id', boardMiddleWare.showPost);
router.get('/:keyStroke', boardMiddleWare.autoComplete);
router.delete('/:id', boardMiddleWare.deleteArticle);
router.post('/article', boardMiddleWare.insertArticle);
router.get('/article/:id', boardMiddleWare.editArticle);
router.put('/article/:id', boardMiddleWare.submitEditedArticle);
module.exports = router;
🔽
board.controller.js
🔽
// board.controller.js
const express = require("express");
const app = express();
// importing body-parser to create bodyParser object
const bodyParser = require('body-parser');
// allows you to use req.body var when you use http post method.
app.use(bodyParser.urlencoded({ extended: true }));
const path = require('path');
// allows you to ejs view engine.
app.set('view engine', 'ejs');
const dbMySQLModel = require('../../models/boardDBController');
function getUniqueElements(array) {
let unique = array.filter((element, index) => {
return array.indexOf(element) === index;
});
return unique;
}
function getTitlesIncludeString(titles, search) {
let result = [];
for(let i=0;i<titles.length;i++) {
if(titles[i].includes(search)) result.push(titles[i]);
}
return result;
}
async function getPageItems(article_num, page, limit) {
page = Math.max(1, parseInt(page));
limit = Math.max(1, parseInt(limit));
page = !isNaN(page)?page:1;
limit = !isNaN(limit)?limit:10;
let page_max = Math.ceil(article_num/limit);
let range_max;
const range_min = (page-1)*limit;
(page === page_max) ? (range_max = article_num) : (range_max = page*limit)
const obj = {
page:page,
limit:limit,
page_max:page_max,
range_min:range_min,
range_max:range_max
}
return obj;
}
// Main login page.
exports.showMain = async (req, res, next) => {
if(req.query.search) return next();
let user;
if(!req.decoded) user = 'Guest';
else user = req.decoded.id;
let { search, page, limit } = req.query;
const articles = await dbMySQLModel.showTable();
const boardObject = await getPageItems(articles.length, page, limit);
return res.render(path.join(__dirname, '../../views/board/board'), {
articles:articles,
user: user,
page_current:boardObject.page,
page_max:boardObject.page_max,
length:articles.length,
limit:boardObject.limit,
range_min:boardObject.range_min,
range_max:boardObject.range_max,
search:search
});
}
exports.searchByTitle = async (req, res) => {
let { search, page, limit } = req.query;
let articles = await dbMySQLModel.getMatchingArticles(search);
if(articles.length === 0) {
return res.send("<script>alert('No matching article.'); window.location.href = '/board';</script>");
}
const boardObject = await getPageItems(articles.length, page, limit);
return res.render(path.join(__dirname, '../../views/board/board'), {
articles:articles,
page_current:boardObject.page,
page_max:boardObject.page_max,
length:articles.length,
limit:boardObject.limit,
range_min:boardObject.range_min,
range_max:boardObject.range_max,
search:search
});
}
exports.showPost = async (req, res, next) => {
if(req.query.keyStroke) return next();
if(req.query.search) return next();
const user = req.decoded;
if(user) {
const article_num = req.params.id;
let article = await dbMySQLModel.showArticleByNum(article_num);
return res.render(path.join(__dirname, '../../views/board/article'), {user:user, article: article});
} else {
return res.sendFile(path.join(__dirname, '../../views/board/login.html'));
}
}
exports.autoComplete = async (req, res, next) => {
if(req.query.search) return next();
const keyStroke = req.query.keyStroke;
const titles = await dbMySQLModel.getAllTitles();
const result = await getTitlesIncludeString(titles, keyStroke);
return res.status(200).send(result).end();
}
// Writing page.
exports.boardWrite = (req, res) => {
const user = req.decoded;
if(user) {
return res.render(path.join(__dirname, '../../views/board/boardWrite'), {user:user});
} else {
return res.sendFile(path.join(__dirname, '../../views/board/login.html'));
}
}
exports.insertArticle = async (req, res) => {
const user = req.decoded;
const { title, content } = req.body;
if(user) {
const author = user.id;
await dbMySQLModel.insert(title, content, author);
return res.status(200).send('Article has been posted.').end();
} else {
return res.sendFile(path.join(__dirname, '../../views/board/login.html'));
}
}
exports.deleteArticle = async (req, res) => {
const user = req.decoded;
const { article_num } = req.body;
const article = await dbMySQLModel.showArticleByNum(article_num);
if(user.id === article.AUTHOR) {
await dbMySQLModel.deleteByNum(article_num);
return res.status(200).send('Article has been removed.').end();
} else {
return res.status(200).send('Account not matched.').end();
}
}
exports.editArticle = async (req, res) => {
const user = req.decoded;
const article_num = req.params.id;
const article = await dbMySQLModel.showArticleByNum(article_num);
if(user.id === article.AUTHOR) {
return res.render(path.join(__dirname, '../../views/board/editArticle'), {user:user, article:article});
}
}
exports.submitEditedArticle = async (req, res) => {
const user = req.decoded;
const article_num = req.body.id;
const title = req.body.title;
const content = req.body.content;
let article = await dbMySQLModel.showArticleByNum(article_num);
const date_obj = new Date();
article.UPDATE_DATE = date_obj.getFullYear() +"-"+ parseInt(date_obj.getMonth()+1) +"-"+ date_obj.getDate();
await dbMySQLModel.editArticle(article_num, title, content, article.UPDATE_DATE);
return res.status(200).send('Your article has been editied.');
}
🔽
boardDBController.js
🔽
// boardDBController.js
// connecting MySQL
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const conn = require('../models/connectMySQL');
const util = require('util');
// node native promisify
const query = util.promisify(conn.query).bind(conn);
function convertDateFormat(date) {
date = date.toLocaleString('default', {year:'numeric', month:'2-digit', day:'2-digit'});
let year = date.substr(6,4);
let month = date.substr(0,2);
let day = date.substr(3,2);
let convertedDate = `${year}-${month}-${day}`;
return convertedDate;
}
function convertTableDateFormat(table) {
for(let i=0;i<table.length;i++) {
table[i].POST_DATE = convertDateFormat(table[i].POST_DATE);
table[i].UPDATE_DATE = convertDateFormat(table[i].UPDATE_DATE);
}
return table;
}
function convertArticleDateFormat(article) {
article.POST_DATE = convertDateFormat(article.POST_DATE);
article.UPDATE_DATE = convertDateFormat(article.UPDATE_DATE);
return article;
}
exports.showTable = async () => {
let table = await query("SELECT * FROM BOARD ORDER BY BOARD_NO DESC;");
table = convertTableDateFormat(table);
return table;
}
exports.getAllTitles = async () => {
let titles = await query("SELECT TITLE FROM BOARD ORDER BY BOARD_NO DESC;");
for(let i=0;i<titles.length;i++) titles[i] = titles[i].TITLE;
return titles;
}
exports.getMatchingArticles = async (title) => {
let articles = await query("SELECT * FROM BOARD WHERE TITLE LIKE '%"+title+"%';");
return convertTableDateFormat(articles);
}
exports.showArticleByNum = async (article_num) => {
const sql = "SELECT * FROM BOARD WHERE BOARD_NO="+article_num+";";
let article = await query(sql);
article = article[0];
article = convertArticleDateFormat(article);
return article;
}
exports.insert = async (title, content, author) => {
// Query to insert multiple rows
let query = `INSERT INTO BOARD (TITLE, content, POST_DATE, UPDATE_DATE, AUTHOR) VALUES ?;`;
const date_obj = new Date();
let post_date = date_obj.getFullYear() +"-"+ parseInt(date_obj.getMonth()+1) +"-"+ date_obj.getDate();
const update_date = post_date;
// Values to be inserted
let values = [
[title, content, post_date, update_date, author]
];
// Executing the query
await conn.query(query, [values]);
}
exports.deleteByNum = async (article_num) => {
let query = `DELETE FROM BOARD WHERE BOARD_NO=`+article_num+`;`;
await conn.query(query);
}
exports.editArticle = async (article_num, title, content, update) => {
let query = "UPDATE BOARD SET TITLE='"+title+"', content='"+content+"', UPDATE_DATE='"+update+"' WHERE BOARD_NO="+article_num+";";
await conn.query(query);
}
🔽
board.ejs
🔽
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/public/css/styles.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Onam Kwon</title>
<body class="body" style="background-color: #1c1c1e; color: rgb(221,221,221); font-family: Sans-serif; margin:0 auto;"></body>
<!-- Bootstrap CSS
<body class="body" style="background-color: #1c1c1e; color: rgb(221,221,221); font-family: Sans-serif; margin:0 auto;">
-->
</head>
<body>
<div class="navbar">
<a class="active" href="/">Home</a>
<a href="/dev">Dev</a>
<a href="/private">Private</a>
<a href="/user">My page</a>
</div>
<div class="wrapper">
<ul class="item-list mySelf">
<ul class="about">
<div>
<h1>
<a href="/board" style="color: white; text-decoration: none; display: inline-block;"> Board </a>
<a style="display: inline-block; float: right; margin-right: 3%;"> Welcome <%= user %>! </a>
</h1>
<div >
<input type="text" list="titles" id="search" name="search" placeholder="Title: " oninput="detectKeystroke()" style="display: inline-block;"/>
<datalist id="titles">
</datalist>
<button type="button" id="searchButton" onclick="search()">Search</button> <br><br>
</div>
<table id="board">
<tr>
<th style="height: 6vh; width: 15%;"> Article Number </th>
<th style="height: 6vh;"> Title </th>
<th style="height: 6vh; width: 10%;"> Posted date </th>
<th style="height: 6vh; width: 10%;"> Author </th>
</tr>
<% for(let i=range_min; i< range_max; i++) { %>
<tr>
<td style="height: 5vh;"><%= articles[i].BOARD_NO %></td>
<td style="height: 5vh;"><a href="/board/<%= articles[i].BOARD_NO %>"><%= articles[i].TITLE %></a></td>
<td style="height: 5vh;"><%= articles[i].POST_DATE %></td>
<td style="height: 5vh;"><%= articles[i].AUTHOR %></td>
</tr>
<% } %>
</table >
</div>
<button type="button" id="new" onclick="location.href='/board/write'">New</button>
<center>
<nav class="col-8">
<%
var offset = 2;
var previousBtnEnabled = page_current>1;
var nextBtnEnabled = page_current< page_max;
%>
<ul class="pagination pagination-sm justify-content-center align-items-center h-100 mb-0">
<li class="page-item <%= previousBtnEnabled?'':'disabled' %>">
<a class="page-link" href="/board?page=<%= page_current-1 %>&limit=<%= limit %>&search=<%= search %>" <%= previousBtnEnabled?'':'tabindex=-1' %>>«</a>
</li>
<% for(i=1;i<=page_max;i++){ %>
<% if(i==1 || i==page_max || (i>=page_current-offset && i<=page_current+offset)){ %>
<li class="page-item <%= page_current==i?'active':'' %>"><a class="page-link" href="/board?page=<%= i %>&limit=<%= limit %>&search=<%= search %>"> <%= i %> </a></li>
<% } else if(i==2 || i==page_max-1){ %>
<li><a class="page-link">...</a></li>
<% } %>
<% } %>
<li class="page-item <%= nextBtnEnabled?'':'disabled' %>">
<a class="page-link" href="/board?page=<%= page_current+1 %>&limit=<%= limit %>&search=<%= search %>" <%= nextBtnEnabled?'':'tabindex=-1' %>>»</a>
</li>
</ul>
</nav>
</center>
</ul>
</ul>
</div>
<script>
function search() {
const search = document.getElementById('search').value;
window.location.href = "/board?search="+search+"&page=<%= page_current %>&limit=<%= limit %>";
// window.location.href = '/board?search='+search;
}
function detectKeystroke() {
const keyStroke = document.getElementById('search').value;
if(keyStroke==='') {
$("#titles").empty();
return ;
}
$.ajax({
type: "get",
url: '/board/:keyStroke',
data: {keyStroke:keyStroke},
dataType:'json',
success: function(res) {
let temp = '';
for(let i=0;i<res.length;i++) {
temp += `<option value="${res[i]}" id="title">`;
}
$('#titles').empty().append(temp);
}
});
}
function signOut() {
$.ajax({
type: "delete",
url: '/user/logout',
data: {},
dataType:'text',
success: function(res) {
location.reload();
}
});
}
</script>
<style>
.ribht {
float: right;
}
#new, {
float: right;
margin-right: 3%;
}
table {
width: 97%;
}
tr {;
height: 30px;
}
table, tr, td, th {
border: 1px solid;
text-align: center;
}
#new, #searchButton {
margin-top: 2%;
width: 19%;
height: 34px;
border-radius: 50px;
background: black;
color: white;
display: inline-block;
}
</style>
<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
</body>
</html>
- 본인 정보로 교체하시면 됩니다.
🔽.env
🔽
# For user account DB in mongodb
MONGO_URI = mongodb+srv://'YourID':'YourPW'@cluster0.cefr7.mongodb.net/'YourDB'?retryWrites=true&w=majority
# For jsonwebtoken secret key
SECRET_KEY = 'YourjwtSecret';
# For MySQL board DB
SQL_USER = 'YourID'
SQL_PASSWORD = 'YourPW'
🔽
boardWrite.ejs
🔽
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/public/css/styles.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Onam Kwon</title>
<body class="body" style="background-color: #1c1c1e; color: rgb(221,221,221); font-family: Sans-serif; margin:0 auto;"></body>
<!-- Bootstrap CSS
<body class="body" style="background-color: #1c1c1e; color: rgb(221,221,221); font-family: Sans-serif; margin:0 auto;">
-->
</head>
<body>
<div class="navbar">
<a class="active" href="/">Onam Kwon</a>
<a href="/dev">Dev</a>
<a href="/private">Private</a>
<a href="/about">About</a>
</div>
<div class="wrapper">
<ul class="item-list mySelf">
<ul class="about">
<article class="about-text">
<div>
<h1><%= user.id %>, Welcome! </h1>
</div>
<div>
<form id="board">
<div>
<h1 style="display: inline-block;">Board</h1>
</div>
<center>
<div class="field">
<label for="title"> <h3>Title: </h3></label> <br>
<input type="text" id="title" name="title" placeholder="Enter a title here." style="width: 53%;"/> <br><br>
</div>
<div class="field">
<label for="content"> <h3>Contents: </h3></label> <br>
<textarea id="content" rows="10%" cols="100%" /></textarea>
</div>
<button type="button" id="back" onclick="location.href='/board'">Back</button>
<button type="button" id="post" onclick="postArticle()" >Post</button> <br><br>
</center>
</form>
</div>
<div>
</div>
<script>
function postArticle() {
const title = document.getElementById("title").value;
const content = document.getElementById("content").value;
$.ajax({
type: "post",
url: '/board/article',
data: {title: title, content:content},
dataType:'text',
success: function(res) {
alert(res);
window.location.href = '/board';
}
});
}
</script>
<style>
#back, #post {
margin-top: 2%;
width: 19%;
height: 34px;
border-radius: 50px;
background: black;
color: white;
display: inline-block;
}
</style>
</article>
</ul>
</ul>
</div>
<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
</body>
</html>
🔽
login.html
🔽
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/public/css/styles.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Onam Kwon</title>
<body class="body" style="background-color: #1c1c1e; color: rgb(221,221,221); font-family: Sans-serif; margin:0 auto;"></body>
<!-- Bootstrap CSS
<body class="body" style="background-color: #1c1c1e; color: rgb(221,221,221); font-family: Sans-serif; margin:0 auto;">
-->
</head>
<body>
<div class="navbar">
<a class="active" href="/">Onam Kwon</a>
<a href="/dev">Dev</a>
<a href="/private">Private</a>
<a href="/about">About</a>
</div>
<div class="wrapper">
<ul class="item-list mySelf">
<ul class="about">
<article class="about-text">
<center>
<form id="signup">
<h1>Sign up</h1>
<div class="field">
<label for="id">ID:</label>
<input type="text" id="id" name="id" placeholder="Enter your fullname" /><br><br>
</div>
<div class="field">
<label for="pw">Address:</label>
<input type="text" id="address" name="address" placeholder="Enter your address" /><br><br>
</div>
<div class="field">
<label for="pw">PW:</label>
<input type="text" id="pw" name="pw" placeholder="Enter your password" /><br><br>
</div>
<div class="field">
<label for="pwc">PW confirmation:</label>
<input type="text" id="pwc" name="pwc" placeholder="PW confirmation" /><br><br>
</div>
<button id="signUp" type="button" onclick="SignUpAjax()">Sign Up</button><br><br>
</form>
</center>
<center>
<form id="singin">
<h1>Sign in</h1>
<div class="field">
<label for="signinID">ID:</label>
<input type="text" id="signinID" name="signinID" placeholder="Enter your fullname" /><br><br>
</div>
<div class="field">
<label for="signinPW">PW:</label>
<input type="text" id="signinPW" name="signinPW" placeholder="Enter your password" /><br><br>
</div>
<!-- <button type="text">Log in</button><br><br> -->
<button id="signIn" type="button" onclick="signInAjax()">Log in</button><br><br>
</form>
</center>
</article>
</ul>
</ul>
</div>
<script>
function SignUpAjax() {
const id = document.getElementById("id").value;
const address = document.getElementById("address").value;
const pw = document.getElementById("pw").value;
const pwc = document.getElementById("pwc").value;
document.getElementById("id").value = "";
document.getElementById("address").value = "";
document.getElementById("pw").value = "";
document.getElementById("pwc").value = "";
$.ajax({
type: "post",
url: '/user/:id/:address/:pw/:pwc',
data: {id:id,address:address,pw:pw,pwc:pwc},
dataType:'text',
success: function(res) {
window.alert(res);
}
});
}
function signInAjax() {
const id = document.getElementById("signinID").value;
const pw = document.getElementById("signinPW").value;
document.getElementById("signinID").value = "";
document.getElementById("signinPW").value = "";
$.ajax({
type: "post",
url: '/user/:id/:pw',
data: {id:id,pw:pw},
dataType:'text',
success: function(res) {
if(res.length == 0) {
location.reload();
} else {
alert(res);
}
}
});
}
$("#pwc").on("keyup", function () {
if(window.event.keyCode==13 && $(this).val()!=""){
SignUpAjax();
}
});
$("#signinPW").on("keyup", function () {
if(window.event.keyCode==13 && $(this).val()!=""){
signInAjax();
}
});
</script>
<style>
#signIn, #signUp {
margin-top: 2%;
width: 19%;
height: 34px;
border-radius: 50px;
background: black;
color: white;
display: inline-block;
}
</style>
<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
</body>
</html>
- 로그인은 전에 이미 구현해 놨으므로 그대로 가져온 후
/board
경로에서도 사용할 예정- 아래 부터는 설명의 편의를 위해 코드의 일부만 가져오므로 코드 복붙은 위에서 해주세요.
🔽
boardWrite.js
🔽
<div>
<form id="board">
<center>
<div class="field">
<label for="title"> <h3>Title: </h3></label> <br>
<input type="text" id="title" name="title" placeholder="Enter a title here." style="width: 53%;"/> <br><br>
</div>
<div class="field">
<label for="content"> <h3>Contents: </h3></label> <br>
<textarea id="content" rows="10%" cols="100%" /></textarea>
</div>
<button type="button" id="back" onclick="location.href='/board'">Back</button>
<button type="button" id="post" onclick="postArticle()" >Post</button> <br><br>
</center>
</form>
</div>
<script>
function postArticle() {
const title = document.getElementById("title").value;
const content = document.getElementById("content").value;
$.ajax({
type: "post",
url: '/board/article',
data: {title: title, content:content},
dataType:'text',
success: function(res) {
alert(res);
window.location.href = '/board';
}
});
}
</script>
title
content
부분을 작성하고post
버튼을 누르면 자바스크립트의postArticle()
함수를 호출한다.
/board/article
라우터로title
content
값과 함께POST
요청한다.
🔽
server.js
🔽
const boardRouter = require('./routes/boardRouter');
app.use('/board', boardRouter);
- 요청은
/board
경로의boardRouter
경로로 타고들어간다.
🔽
boardRouter.js
🔽
// Importing controller
const boardMiddleWare = require('../controllers/board/board.controller');
const auth = require("../controllers/authMiddleware");
// path: /board
router.use('/', auth);
// path: /board/article
router.post('/article', boardMiddleWare.insertArticle);
module.exports = router;
router.use('/', auth);
로/board
경로로 들어오는 요청들에게 로그인 토큰을 가지고 있는지 확인할 수 있다.router.post('/article', boardMiddleWare.insertArticle);
에서 실제 경로는/board/article
이지만server.js
파일에서/board
로 라우팅을 해줬기 때문에/board.article
이 아닌/article
만 써줘야 한다.
- 이 라우터에서
boardMiddleWare
에서 가져온insertArticle
함수를 호출해준다.
boardMiddleWare.insertArticle()
🔽board.controller.js
🔽
const dbMySQLModel = require('../../models/boardDBController');
exports.insertArticle = async (req, res) => {
const user = req.decoded;
const { title, content } = req.body;
if(user) {
const author = user.id;
await dbMySQLModel.insert(title, content, author);
return res.status(200).send('Article has been posted.').end();
} else {
return res.sendFile(path.join(__dirname, '../../views/board/login.html'));
}
}
boardRouter
에서auth
를 이용해 로그인을 확인 한 후- 로그인 한 상태라면
- 유저의 아이디값을
author
에 초기화 한다.await dbMySQLModel.insert(title, content, author);
를 이용해body
에서 받아온title
content
author
값을 넣어준다.await
를 사용했으므로 위 작업이 완료 된 후 완료 메시지 응답.- 로그인 안한 상태라면
- 로그인 화면 응답.
- 🔽
MySQL
=>board_db
DB,BOARD
table. 🔽
mysql> use board_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show columns from BOARD;
+-------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+----------------+
| BOARD_NO | int | NO | PRI | NULL | auto_increment |
| TITLE | text | NO | | NULL | |
| content | longtext | NO | | NULL | |
| POST_DATE | date | NO | | NULL | |
| UPDATE_DATE | date | NO | | NULL | |
| AUTHOR | text | NO | | NULL | |
+-------------+----------+------+-----+---------+----------------+
- 위와같이 게시판 게시글을 저장할 테이블을 만들어 준다.
dbMySQLModel.insert()
🔽boardDBController.js
🔽
exports.insert = async (title, content, author) => {
// Query to insert multiple rows
let query = `INSERT INTO BOARD (TITLE, content, POST_DATE, UPDATE_DATE, AUTHOR) VALUES ?;`;
const date_obj = new Date();
let post_date = date_obj.getFullYear() +"-"+ parseInt(date_obj.getMonth()+1) +"-"+ date_obj.getDate();
const update_date = post_date;
// Values to be inserted
let values = [
[title, content, post_date, update_date, author]
];
// Executing the query
await conn.query(query, [values]);
}
- 브라우저에서 새로운 게시글을 생성할 때 마다 테이블에 아래처럼 게시글이 저장된다.
mysql> select * from BOARD;
+----------+----------------------------+---------------------------------------+------------+-------------+--------+
| BOARD_NO | TITLE | content | POST_DATE | UPDATE_DATE | AUTHOR |
+----------+----------------------------+---------------------------------------+------------+-------------+--------+
| 1 | first | first content | 2022-08-21 | 2022-08-22 | |
| 3 | this is a title23 | this is a content 2 3 | 2022-08-22 | 2022-08-25 | one |
| 4 | this is a title445 | this is a content 4 4 5 | 2022-08-22 | 2022-08-25 | one |
| 5 | this is a title4 | this is a content 4 | 2022-08-22 | 2022-08-23 | one |
| 6 | this is a title6 | this is a content 6 | 2022-08-22 | 2022-08-25 | one |
| 7 | this is a title5 | this is a content 5 | 2022-08-22 | 2022-08-23 | one |
| 11 | title from user five | here I go by user five. | 2022-08-24 | 2022-08-24 | five |
| 12 | another one from user five | here I go | 2022-08-24 | 2022-08-24 | five |
| 13 | wefewf | aassdf | 2022-08-24 | 2022-08-24 | five |
| 15 | helloooo | hiiii | 2022-08-24 | 2022-08-24 | five |
| 18 | this is a titl5555 | this is a content 5555 | 2022-08-24 | 2022-08-26 | one |
| 19 | working good? edit good? | I think so yeah?
I thinks so yeah?? | 2022-08-25 | 2022-08-25 | one |
| 20 | ff | ke | 2022-08-25 | 2022-08-25 | one |
| 21 | test | test2 | 2022-08-25 | 2022-08-25 | one |
| 22 | 1 | 1 | 2022-08-25 | 2022-08-25 | one |
| 23 | test | done? | 2022-08-26 | 2022-08-26 | one |
| 24 | Writing a new article | This is a content | 2022-08-29 | 2022-08-29 | one |
+----------+----------------------------+---------------------------------------+------------+-------------+--------+
17 rows in set (0.00 sec)
- 클라이언트에서 게시글 확인은 페이지 기능으로 인해 다음 글에 올리도록 하겠습니다.