안녕하세요.
이번에는 Node.js의 MVC 패턴에 대해서 알아보겠습니다.
1. MVC
- Model View Controller
- 소프트웨어 설계와 관련된 디자인 패턴
- MVC 이용 웹 프레임워크
- php
- Django
- Express
- Angular 등
장점
- 패턴들을 구분해 개발한다.
- 유지보수가 용이하다.
- 유연성이 높다.
- 확장성이 높다.
- 협업에 용이하다.
단점
- 완벽한 의존성 분리가 어렵다.
- 설계 단계가 복잡하다.
- 설계 시간이 오래 걸린다.
- 클래스가 많아진다.
2. 처음 폴더 및 파일 세팅
환경 : Node.js + MySQL
node6 말고 다른 이름으로 사용하셔도 됩니다.
Model
- 데이터를 처리하는 부분
Views
- UI 관련된 것을 처리하는 부분 (사용자에게 보이는 부분)
Controller
- Views와 Model을 연결해 주는 부분
3. npm 설치
npm init —yes
npm install express
npm install ejs
npm install -g nodemon
최상단 index.js
const express = require('express');
const app = express();
const port = 8080;
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: true}));
app.use(express.json());
app.use('/static', express.static(__dirname+'/static'));
// routes 폴더에서 필요한 정보를 가져온다.
// 뒤에 파일 이름을 작성하지 않으면 기본 값으로 index.js 파일을 가져온다.
// const router = require('./routes/index');
const router = require('./routes');
// localhost:8080
// localhost:8080/url주소
// 2개와 같이 입력이 되면 router로 이동을 시키겠다 라는 의미
// 미들웨어 등록을 통해 접속을 하게 되면 무조건 router를 들르게 된다.
app.use('/', router);
// 주소가 아니라 *로 입력이 되어 있는데, 위에서 정의해둔 router가 아닌 다른 주소를
// 입력하였을 경우 error 페이지를 보여주게 설정하는 방법이다.
// ('*')은 무조건 마지막에 작성을 해줘야 다른 주소들이 정상적으로 작동한다.
app.get('*', (req, res) => {
// res.render('error');
res.send('주소가 존재하지 않습니다. 다시 한 번 확인해주세요.');
});
app.listen(port, () => {
console.log('server open : ', port);
});
[참고] 현 상태에서는 코드를 실행해도 Error 메시지가 출력된다.
- 분리된 코드들이 전부 작성이 안되어 있기 때문에 아직 Error 메시지만 출력된다.
- routes 폴더의 index.js와 controller 폴더의 Cmain.js 까지 작성하면 코드가 실행 가능하다.
Router 코드 설명
- 주소가 아니라 *로 입력이 되어 있는데, 위에서 정의해 둔 router가 아닌 다른 주소를 입력하였을 경우 error 페이지를 보여주게 설정하는 방법이다.
const router = require('./routes');
app.use('/', router);
// 주소가 아니라 *로 입력이 되어 있는데, 위에서 정의해둔 router가 아닌 다른 주소를
// 입력하였을 경우 error 페이지를 보여주게 설정하는 방법이다.
app.get('*', (req, res) => {
res.render('error');
});
따로 지정을 안 한 주소를 입력하면 ‘Cannot Get /url’ 페이지가 나오는데 웹에 대하여 전혀 모르는 사람이 당황할 수 있기 때문에 사용한다.
여기서는 error.ejs 파일이 없어서 출력이 안되었다.
app.get(’*’) 코드의 res.render 대신에 res.send 코드로 아래와 같이 수정하고 실행한다.
app.get('*', (req, res) => {
// res.render('error');
res.send('주소가 존재하지 않습니다. 다시 한 번 확인해주세요.');
});
코드를 실행하고 없는 주소를 입력하면 다음과 같이 출력되는 것을 볼 수 있다.
routes 폴더
- 경로를 controller와 연결 지어 설정할 수 있다.
routes/index.js
const express = require('express');
const controller = require('../controller/Cmain');
const router = express.Router();
// app.get 처럼 사용하지만 뒤에 함수는 요청에 따라서 달라야 하기 때문에 다르게 작성되는 것임
router.get('/', controller.main);
// 예시2개
// controller/Cmain.js 에서 설정을 해줬기 때문에 사용이 가능하다.
router.get('/test', controller.test);
router.post('/postForm', controller.post);
// 다른 파일에서 require() 함수를 이용해서
// 파일을 사용하고 싶은 경우에는 해당 파일에서는 module.exports 를 사용해줘야 한다.
module.exports = router;
model 폴더
- DB에서 나오는 결과 값
exports.hello = function() {
return 'helloTest';
// 임시 값
}
// Arrow 방식으로 작성해도 상관 없음
// exports.hello = () => {
// return 'hello';
// }
// 나중에 DB에서 값을 가져올 때 이렇게 배열로 가져올 수 있다는 예시
exports.test = function() {
return [
{ id: 'a', name: '새싹'},
{ id: 'b', name: '용산'}
];
}
controller 폴더
- 경로와 연결될 함수 내용을 정의한다.
- 경로와 연결되는 함수이기에 request 객체와 response 객체를 사용할 수 있다.
controller/Cmain.js
const Test = require('../model/Test');
// model 폴더 사용을 위해 require 함수 사용
exports.main = (req, res) => {
// res.send('hello');
// send 말고 render 이용해서 views 폴더의 index.ejs 파일도 호출 가능
// res.render('index');
let hi = Test.hello();
// hi 변수에 hello 함수의 return 값 helloTest 문자가 저장됨
res.send(hi);
}
exports.test = (req, res) => {
res.send('test');
}
exports.post = (req, res) => {
res.send('post');
}
views 폴더
- 실제 html 및 사용자에게 보이는 코드, 파일
views/index.ejs
<!DOCTYPE html>
<html lang="en, ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MVC</title>
</head>
<body>
안녕하세요 index.ejs 파일입니다.
</body>
</html>
실습코드
해당 실습 코드는 조회 + 등록만 가능합니다
수정, 삭제는 구현되어 있지 않습니다.
1. MVC 구조 만들어보기
DB - visitor 테이블 만들기
DB - visitor 테이블 데이터 넣기
Node.js - MySQL 연결
npm install mysql
package.json 파일에서 설치되었는지 확인
/model/Visitor.js
const mysql = require('mysql');
const cnn = mysql.createConnection({
host: 'localhost',
user: 'test',
password: 'qwer1234',
database: 'test'
});
// cb는 controller의 get_visitor 함수의 매개변수에 해당한다.
exports.get_visitor = (cb) => {
let sql = 'select * from visitor';
cnn.query(sql, (err, rows) => {
if(err) throw err;
console.log('visitors : ', rows);
cb(rows);
});
}
*굳이 콜백 함수로 넘기는 이유
- query문 마지막에 return rows; 이용해서 값을 넘긴 후에 controller에서 렌더링을 해주면 되지 않냐라는 의문이 생길 수 있지만
- query문은 다른 명령문에 비해서 시간이 오래 걸리고 비동기식으로 인해서 원하는 값이 제대로 출력이 되지 않는 경우가 발생할 수 있습니다.
- 그래서 query 함수에다가 콜백 함수를 이용하여 비동기식을 사용해도 값이 정상적으로 출력되기 위하여 사용합니다.
#root 계정 사용 불가
- mysql 특성으로 인한 root 계정 사용 불가로 새로운 계정 생성 필요
- 새로운 계정 생성 후 권한 설정 필요
/contorller/Cvisitor.js
const Visitor = require('../model/Visitor');
exports.visitor = (req, res) => {
Visitor.get_visitor(function(result) {
console.log(result);
res.render('visitor', { data: result});
});
}
최상단 index.js
const express = require('express');
const app = express();
const port = 8080;
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: true}));
app.use(express.json());
app.use('/static', express.static(__dirname+'/static'));
const router = require('./routes');
app.use('/visitor', router);
app.get('*', (req, res) => {
res.send('주소를 확인해 주세요.');
});
app.listen(port, () => {
console.log('server open : ', port);
});
/routes/index.js
const express = require('express');
const controller = require('../controller/Cvisitor');
const router = express.Router();
router.get('/', controller.visitor);
module.exports = router;
/views/visitor.ejs
<!DOCTYPE html>
<html lang="en, ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MVC 구조 만들기 - 2</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
<style>
table, th, td {
border: 1px solid black;
}
</style>
<script>
function register() {
const form = document.getElementById('form_register');
let data = {
name: form.name.value,
comment: form.comment.value
};
axios({
method: 'post',
url: '/visitor/register',
data: data
})
.then((response) => {
// $('table').append(`
$('#mainTable').append(`
<tr>
<td>${response.data}</td>
<td>${data.name}</td>
<td>${data.comment}</td>
<td><button type="button">수정</button></td>
<td><button type="button">삭제</button></td>
</tr>
`)
// JavaScript 버전
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${response.data}</td>
<td>${data.name}</td>
<td>${data.comment}</td>
<td><button type="button">수정</button></td>
<td><button type="button">삭제</button></td>
`;
document.getElementById('testTable').append(tr);
});
}
</script>
</head>
<body>
<div>
<form id="form_register">
<fieldset>
<legend>방명록 등록</legend>
이름 : <input type="text" placeholder="사용자 이름" name="name"><br>
방명록 : <input type="text" placeholder="방명록" name="comment"><br>
<div class="register-btn">
<button type="button" onclick="register();">등록</button>
</div>
</fieldset>
</form>
</div>
<div>
<table id='mainTable' cellspacing="0" cellpadding="10" style="margin-top: 30px;">
<tr>
<th>ID</th>
<th>작성자</th>
<th>방명록</th>
<th>수정</th>
<th>삭제</th>
</tr>
<% for(let i = 0; i < data.length; i++) { %>
<tr>
<td><%= data[i].id %></td>
<td><%= data[i].name %></td>
<td><%= data[i].comment %></td>
<td><button type="button">수정</button></td>
<td><button type="button">삭제</button></td>
</tr>
<% } %>
</table>
<div>
<table id='testTable'>
</table>
</div>
</div>
</body>
</html>
실행 결과 - html
실행 결과 - 콘솔 창
등록 기능 추가 (기존 파일 수정)
/model/Visitor.js
const mysql = require('mysql');
const cnn = mysql.createConnection({
host: 'localhost',
user: 'test',
password: 'qwer1234',
database: 'test'
});
// cb는 controller의 get_visitor 함수의 매개변수에 해당한다.
exports.get_visitor = (cb) => {
let sql = 'select * from visitor';
cnn.query(sql, (err, rows) => {
if(err) throw err;
console.log('visitors : ', rows);
cb(rows);
});
}
exports.register_visitor = (info, cb) => {
let sql = `insert into visitor (name, comment) values ('${info.name}', '${info.comment}')`;
cnn.query(sql, (err, result) => {
if (err) throw err;
console.log('insert result: ', result);
console.log('insert result: ', result.insertId);
cb(result.insertId);
});
}
/contorller/Cvisitor.js
const Visitor = require('../model/Visitor');
exports.visitor = (req, res) => {
Visitor.get_visitor(function(result) {
console.log(result);
res.render('visitor', { data: result});
});
}
exports.register = (req, res) => {
Visitor.register_visitor(req.body, function(id) {
console.log(id);
res.send(String(id));
});
}
/routes.index.js
const express = require('express');
const controller = require('../controller/Cvisitor');
const router = express.Router();
router.get('/', controller.visitor);
router.post('/register', controller.register);
module.exports = router;
이상으로 Node.js에서 MVC 패턴 사용해 보기였습니다.
감사합니다.
'Node' 카테고리의 다른 글
[Node.js] 쿠키(Cookie)에 대하여 알아보기 (0) | 2023.03.13 |
---|---|
[Node.js] MySQL Sequelize 사용해보기 (0) | 2023.03.12 |
[Node.js] Node.js + MySQL 연동하기 (0) | 2023.03.10 |
[Node.js] 파일 업로드(multer) 사용해보기 (0) | 2023.03.06 |
[Node.js] 미들웨어 사용해보기 (0) | 2023.03.05 |