MySQL을 이용해서 DB에 정보를 저장하고, DB의 값을 읽어와서 view에 table형식으로 보여주기 (CRUD)
먼저, npm(node package manager)를 초기화 한다.
npm init
그 다음 프로젝트에 필요한 dependeny를 설치한다.
npm install express
npm install ejs
npm install body-parser
npm install mysql
MVC 패턴에 필요한 폴더를 생성해주고 main index.js 파일을 생성한다. 기본경로를 /visitor 로 설정해주었다.
/* index.js */
const express = require("express");
const app = express();
const port = 8080;
const bodyParser = require("body-parser");
app.set("view engine", "ejs");
app.use( express.static( "uploads" ));
app.use(express.urlencoded({extended: true}));
app.use( bodyParser.json() );
const router = require("./routes");
app.use("/visitor", router);
app.listen(port, () => {
console.log( "Server Port: ", port);
})
Vistior model을 생성하기 전, MySQL을 이용해서 visitor라는 테이블을 생성했다.
그리고 DML 명령어를 이용하여 데이터를 생성했다.
CRUD를 하기 위해 새로운 계정을 생성한다. 기존에 사용하던 root 계정은 최상위 계정이라 외부로부터의 비밀번호 접근을 허용하지 않기 때문이다. 즉, 새로운 사용자를 만들고 그 사용자로 접근을 해야한다.
새로운 계정 생성하기
CREATE USER '사용자이름'@'%' IDENTIFIED BY '비번';
모든 DB, 테이블 관리 권한 부여하기
GRANT ALL PRIVILEGES ON *.* TO '사용자이름'@'%' WITH GRANT OPTION;
권한 부여 내용을 메모리에 반영하기 (권한 적용하기)
FLUSH PRIVILEGES;
기본 인증 암호화 플러그인이 caching_sha2_password
를 사용하게 되어있다. mysql_native_password
방식을 사용하기 위해 변경해준다.
ALTER USER '사용자이름'@'%' IDENTIFIED WITH mysql_native_password BY '비번';
Vistior model을 생성하는데, mysql module을 이용한다. mysql.createConnection()을 이용하면 DB와 서버를 연결하는 객체를 만들 수 있으며, 이를 이용해서 여러 쿼리(query)를 실행시킬 수 있다.
/* ./model/Visitor */
const mysql = require("mysql");
const cnn = mysql.createConnection({
host: 'localhost',
user: 'user_name',
password: 'password',
database: 'database_name'
});
//방명록 전체 정보 조회
exports.get_visitors = (cb) => {
cnn.query('SELECT * FROM visitor;', (err, rows)=> {
if ( err ) throw err;
console.log( rows );
cb(rows);
});
}
//방명록 등록
exports.insert = ( name, comment, cb ) => {
var sql = "INSERT INTO visitor (name, comment) VALUES ('" + name + "', '" + comment + "');";
cnn.query(sql, (err, rows) => {
if ( err ) throw err;
console.log( rows );
cb(rows.insertId);
});
}
//방명록 단일 정보 조회
exports.get_visitor = (id, cb) => {
// id 컬럼의 값이 id인 데이터를 1개만 검색한다.
cnn.query(`SELECT * FROM visitor Where id= ${id} limit 1;`, (err, rows) => {
if ( err ) throw err;
console.log( rows );
cb(rows);
});
}
//방명록 정보 수정
exports.update = (data, cb) => {
var sql = `UPDATE visitor SET name='${data.name}', comment='${data.comment}' WHERE id=${data.id}`;
cnn.query(sql, (err, rows) => {
if ( err ) throw err;
console.log( rows );
cb(rows);
})
}
//방명록 정보 삭제
exports.delete = (id, cb) => {
cnn.query(`DELETE FROM visitor WHERE id=${id}`, (err, rows) => {
if ( err ) throw err;
console.log( rows );
cb(rows);
})
}
get_visitors()는 SELECT 쿼리를 이용해서 전체 정보를 불러오는 함수이다. 콜백 함수로 해당 데이터를 Controller에 넘겨준다.
insert()는 INSERT ~ INTO 쿼리를 사용해서 데이터를 추가할 때 사용하는 함수이다. 콜백 함수로 insertId만 Controller에 넘겨준다.
get_visitor()는 id로 쿼리를 조회해서 단일 정보만 읽어오는 함수이다.
update()는 id로 정보를 수정하는 함수이다.
delete()는 id로 정보를 삭제하는 함수이다.
get_visitors()는 model을 통해 DB 정보를 가져와서 view 쪽으로 넘겨준다.
post_comment()는 name과 comment 정보를 model쪽으로 넘겨주어 DB에 저장되도록 한 후, insertId 정보를 돌려 받아 view 쪽으로 넘겨준다.
get_visitor()는 get_visitors() 처럼 단일 정보를 가져와서 view쪽으로 정보를 넘겨준다.
patch_comment()는 Visitor 모델의 update()를 이용해서 정보를 수정한다.
delete_comment()는 Visitor 모델의 delete()를 이용해서 정보를 삭제한다.
/* ./controller/VisitorController.js */
const Visitor = require("../model/Visitor");
//방명록 전체 정보 조회
exports.get_visitors = (req, res) => {
Visitor.get_visitors(function( result ) {
res.render("index", {data: result});
});
}
//방명록 등록
exports.post_comment = (req, res) => {
Visitor.insert( req.body.name, req.body.comment, function( result ) {
res.send( {id: result} );
});
}
//방명록 단일 정보 조회
exports.get_visitor = (req, res) => {
Visitor.get_visitor( req.query.id, function(result) {
res.send({ result: result[0]});
})
}
//방명록 정보 수정
exports.patch_comment = (req, res) => {
Visitor.update( req.body, function(result) {
res.send( "success Update!" );
});
}
//방명록 정보 삭제
exports.delete_comment = (req, res) => {
Visitor.delete( req.body.id, function(result) {
res.send( "success Delete!" );
});
}
/* .routes/index.js */
const express = require("express");
const visitorRouter = express.Router();
const visitor = require("../controller/VisitorController");
visitorRouter.get("/", visitor.get_visitors);
visitorRouter.post("/write", visitor.post_comment);
visitorRouter.get("/get", visitor.get_visitor);
visitorRouter.patch("/edit", visitor.patch_comment);
visitorRouter.delete("/delete", visitor.delete_comment);
module.exports = visitorRouter;
View는 index.ejs 파일 하나만 생성했다.
BODY
<body>
<div>
<form id="form_comment">
<fieldset>
<legend>방명록 등록</legend>
<div><input type="hidden" name="id" placeholder="사용자 아이디"></div>
<div><input type="text" name="name" placeholder="사용자 이름"></div>
<div><input type="text" name="comment" placeholder="방명록"></div>
<div id="button-group">
<button type="button" onclick="writeComment();">등록</button>
</div>
</fieldset>
</form>
</div>
<br/>
<table id="visit_list">
<thead>
<tr>
<th>ID</th>
<th>작성자</th>
<th>방명록</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
<tbody>
<%
for (let i=0; i<data.length; i++ ) {
%>
<tr id="tr_<%=data[i].id%>">
<td><%=data[i].id%></td>
<td><%=data[i].name%></td>
<td><%=data[i].comment%></td>
<td><button type="button" onclick="editComment('<%=data[i].id%>');">수정</button></td>
<td><button type="button" onclick="deleteComment('<%=data[i].id%>');">삭제</button></td>
</tr>
<%
}
%>
</tbody>
</table>
</body>
Script
<script>
function writeComment() {
var form = document.getElementById("form_comment");
axios({
method: 'post',
url: 'http://localhost:8080/visitor/write',
data: {
name: form.name.value,
comment: form.comment.value
}
})
.then((response) => { return response.data; })
.then((data) => {
let html = "<tr id='tr_" + data.id + "'><td>" + data.id + "</td><td>" + form.name.value + "</td><td>" + form.comment.value + "</td>" +
"<td><button type='button' onclick='editComment(" + data.id + ");'>수정</button></td>" +
"<td><button type='button' onclick='deleteComment(" + data.id + ");'>삭제</button></td></tr>";
$("table").append(html);
})
}
// 수정버튼 눌렀을 때 input 등록창 update
function editComment( id ) {
axios({
method: 'get',
url: 'http://localhost:8080/visitor/get?id=' + id
})
.then((response) => { return response.data })
.then(( data ) => {
var form = document.getElementById("form_comment");
form.name.value = data.result.name;
form.comment.value = data.result.comment;
var html = "<button type='button' onclick='editDo(" + id + ");'>수정</button>"
+ "<button type='button' onclick='editCancel();'>수정 취소</button>";
//등록 button을 수정 button으로 바꾼다
document.getElementById("button-group").innerHTML = html;
});
}
// DB 수정 + table 내용 update
function editDo( id ) {
var form = document.getElementById("form_comment");
axios({
method: 'patch',
url: 'http://localhost:8080/visitor/edit',
data: {
id: id,
name: form.name.value,
comment: form.comment.value
}
})
.then((response) => { return response.data })
.then((data) => {
var tr = document.getElementById("tr_"+id);
var children = tr.children;
console.log(children);
$(children[1]).text(form.name.value);
$(children[2]).text(form.comment.value);
})
}
// 수정취소 -> 등록버튼
function editCancel() {
var form = document.getElementById("form_comment");
form.reset();
var html = "<button type='button' onclick='writeComment();'>등록</button>";
document.getElementById("button-group").innerHTML = html;
}
// 삭제
function deleteComment( id ) {
axios({
method: 'delete',
url: 'http://localhost:8080/visitor/delete',
data: { id: id }
})
.then((response) => { return response.data; })
.then((data) => {
var tr = document.getElementById("tr_" + id);
$(tr).remove();
// $("#tr_" + id).remove();
});
}
</script>
사용자 이름과 방명록을 작성하고 등록버튼을 눌렀을 때
table의 수정 버튼을 클릭했을 때
정보를 수정하고 수정 버튼을 눌렀을 때
정보 수정하지 않고 수정취소 버튼을 눌렀을 때
table의 삭제 버튼을 눌렀을 때